func.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package di
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type (
  7. targetFuncInput struct {
  8. Object *BindObject
  9. InputIndex int
  10. }
  11. // FuncInjector keeps the data that are needed in order to do the binding injection
  12. // as fast as possible and with the best possible and safest way.
  13. FuncInjector struct {
  14. // the original function, is being used
  15. // only the .Call, which is referring to the same function, always.
  16. fn reflect.Value
  17. typ reflect.Type
  18. goodFunc TypeChecker
  19. inputs []*targetFuncInput
  20. // Length is the number of the valid, final binded input arguments.
  21. Length int
  22. // Valid is True when `Length` is > 0, it's statically set-ed for
  23. // performance reasons.
  24. Has bool
  25. trace string // for debug info.
  26. lost []*missingInput // Author's note: don't change this to a map.
  27. }
  28. )
  29. type missingInput struct {
  30. index int // the function's input argument's index.
  31. found bool
  32. }
  33. func (s *FuncInjector) miss(index int) {
  34. s.lost = append(s.lost, &missingInput{
  35. index: index,
  36. })
  37. }
  38. // MakeFuncInjector returns a new func injector, which will be the object
  39. // that the caller should use to bind input arguments of the "fn" function.
  40. //
  41. // The hijack and the goodFunc are optional, the "values" is the dependencies collection.
  42. func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *FuncInjector {
  43. typ := IndirectType(fn.Type())
  44. s := &FuncInjector{
  45. fn: fn,
  46. typ: typ,
  47. goodFunc: goodFunc,
  48. }
  49. if !IsFunc(typ) {
  50. return s
  51. }
  52. defer s.refresh()
  53. n := typ.NumIn()
  54. for i := 0; i < n; i++ {
  55. inTyp := typ.In(i)
  56. if hijack != nil {
  57. b, ok := hijack(inTyp)
  58. if ok && b != nil {
  59. s.inputs = append(s.inputs, &targetFuncInput{
  60. InputIndex: i,
  61. Object: b,
  62. })
  63. continue
  64. }
  65. }
  66. matched := false
  67. for j, v := range values {
  68. if s.addValue(i, v) {
  69. matched = true
  70. // remove this value, so it will not try to get binded
  71. // again, a next value even with the same type is able to be
  72. // used to other input arg. One value per input argument, order
  73. // matters if same type of course.
  74. //if len(values) > j+1 {
  75. values = append(values[:j], values[j+1:]...)
  76. //}
  77. break
  78. }
  79. }
  80. if !matched {
  81. // if no binding for this input argument,
  82. // this will make the func injector invalid state,
  83. // but before this let's make a list of failed
  84. // inputs, so they can be used for a re-try
  85. // with different set of binding "values".
  86. s.miss(i)
  87. }
  88. }
  89. return s
  90. }
  91. func (s *FuncInjector) refresh() {
  92. s.Length = len(s.inputs)
  93. s.Has = s.Length > 0
  94. }
  95. func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
  96. defer s.refresh()
  97. if s.typ.NumIn() < inputIndex {
  98. return false
  99. }
  100. inTyp := s.typ.In(inputIndex)
  101. // the binded values to the func's inputs.
  102. b, err := MakeBindObject(value, s.goodFunc)
  103. if err != nil {
  104. return false
  105. }
  106. if b.IsAssignable(inTyp) {
  107. // println(inTyp.String() + " is assignable to " + val.Type().String())
  108. // fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
  109. // i, b.Type.String(), value.String(), val.Pointer())
  110. s.inputs = append(s.inputs, &targetFuncInput{
  111. InputIndex: inputIndex,
  112. Object: &b,
  113. })
  114. return true
  115. }
  116. return false
  117. }
  118. // Retry used to add missing dependencies, i.e path parameter built'n bindings if not already exists
  119. // in the `hero.Handler`, once, only for that func injector.
  120. func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type) (reflect.Value, bool)) bool {
  121. for _, missing := range s.lost {
  122. if missing.found {
  123. continue
  124. }
  125. invalidIndex := missing.index
  126. inTyp := s.typ.In(invalidIndex)
  127. v, ok := retryFn(invalidIndex, inTyp)
  128. if !ok {
  129. continue
  130. }
  131. if !s.addValue(invalidIndex, v) {
  132. continue
  133. }
  134. // if this value completes an invalid index
  135. // then remove this from the invalid input indexes.
  136. missing.found = true
  137. }
  138. return s.Length == s.typ.NumIn()
  139. }
  140. // String returns a debug trace text.
  141. func (s *FuncInjector) String() (trace string) {
  142. for i, in := range s.inputs {
  143. bindmethodTyp := bindTypeString(in.Object.BindType)
  144. typIn := s.typ.In(in.InputIndex)
  145. // remember: on methods that are part of a struct (i.e controller)
  146. // the input index = 1 is the begggining instead of the 0,
  147. // because the 0 is the controller receiver pointer of the method.
  148. trace += fmt.Sprintf("[%d] %s binding: '%s' for input position: %d and type: '%s'\n",
  149. i+1, bindmethodTyp, in.Object.Type.String(), in.InputIndex, typIn.String())
  150. }
  151. return
  152. }
  153. // Inject accepts an already created slice of input arguments
  154. // and fills them, the "ctx" is optional and it's used
  155. // on the dependencies that depends on one or more input arguments, these are the "ctx".
  156. func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
  157. args := *in
  158. for _, input := range s.inputs {
  159. input.Object.Assign(ctx, func(v reflect.Value) {
  160. // fmt.Printf("assign input index: %d for value: %v\n",
  161. // input.InputIndex, v.String())
  162. args[input.InputIndex] = v
  163. })
  164. }
  165. *in = args
  166. }
  167. // Call calls the "Inject" with a new slice of input arguments
  168. // that are computed by the length of the input argument from the MakeFuncInjector's "fn" function.
  169. //
  170. // If the function needs a receiver, so
  171. // the caller should be able to in[0] = receiver before injection,
  172. // then the `Inject` method should be used instead.
  173. func (s *FuncInjector) Call(ctx ...reflect.Value) []reflect.Value {
  174. in := make([]reflect.Value, s.Length, s.Length)
  175. s.Inject(&in, ctx...)
  176. return s.fn.Call(in)
  177. }