struct.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package hero
  2. import (
  3. "fmt"
  4. "reflect"
  5. "github.com/kataras/iris/v12/context"
  6. )
  7. // Sorter is the type for sort customization of a struct's fields
  8. // and its available bindable values.
  9. //
  10. // Sorting applies only when a field can accept more than one registered value.
  11. type Sorter func(t1 reflect.Type, t2 reflect.Type) bool
  12. // sortByNumMethods is a builtin sorter to sort fields and values
  13. // based on their type and its number of methods, highest number of methods goes first.
  14. //
  15. // It is the default sorter on struct injector of `hero.Struct` method.
  16. var sortByNumMethods Sorter = func(t1 reflect.Type, t2 reflect.Type) bool {
  17. if t1.Kind() != t2.Kind() {
  18. return true
  19. }
  20. if k := t1.Kind(); k == reflect.Interface || k == reflect.Struct {
  21. return t1.NumMethod() > t2.NumMethod()
  22. } else if k != reflect.Struct {
  23. return false // non-structs goes last.
  24. }
  25. return true
  26. }
  27. // Struct keeps a record of a particular struct value injection.
  28. // See `Container.Struct` and `mvc#Application.Handle` methods.
  29. type Struct struct {
  30. ptrType reflect.Type
  31. ptrValue reflect.Value // the original ptr struct value.
  32. elementType reflect.Type // the original struct type.
  33. bindings []*binding // struct field bindings.
  34. Container *Container
  35. Singleton bool
  36. }
  37. type singletonStruct interface {
  38. Singleton() bool
  39. }
  40. func isMarkedAsSingleton(structPtr any) bool {
  41. if sing, ok := structPtr.(singletonStruct); ok && sing.Singleton() {
  42. return true
  43. }
  44. return false
  45. }
  46. func makeStruct(structPtr interface{}, c *Container, partyParamsCount int) *Struct {
  47. v := valueOf(structPtr)
  48. typ := v.Type()
  49. if typ.Kind() != reflect.Ptr || indirectType(typ).Kind() != reflect.Struct {
  50. panic("binder: struct: should be a pointer to a struct value")
  51. }
  52. isSingleton := isMarkedAsSingleton(structPtr)
  53. disablePayloadAutoBinding := c.DisablePayloadAutoBinding
  54. enableStructDependents := c.EnableStructDependents
  55. disableStructDynamicBindings := c.DisableStructDynamicBindings
  56. if isSingleton {
  57. disablePayloadAutoBinding = true
  58. enableStructDependents = false
  59. disableStructDynamicBindings = true
  60. }
  61. // get struct's fields bindings.
  62. bindings := getBindingsForStruct(v, c.Dependencies, c.MarkExportedFieldsAsRequired, disablePayloadAutoBinding, enableStructDependents, c.DependencyMatcher, partyParamsCount, c.Sorter)
  63. // length bindings of 0, means that it has no fields or all mapped deps are static.
  64. // If static then Struct.Acquire will return the same "value" instance, otherwise it will create a new one.
  65. singleton := true
  66. elem := v.Elem()
  67. // fmt.Printf("Service: %s, Bindings(%d):\n", typ, len(bindings))
  68. for _, b := range bindings {
  69. // fmt.Printf("* " + b.String() + "\n")
  70. if b.Dependency.Static {
  71. // Fill now.
  72. input, err := b.Dependency.Handle(nil, b.Input)
  73. if err != nil {
  74. if err == ErrSeeOther {
  75. continue
  76. }
  77. panic(err)
  78. }
  79. elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
  80. } else if !b.Dependency.Static {
  81. if disableStructDynamicBindings {
  82. panic(fmt.Sprintf("binder: DisableStructDynamicBindings setting is set to true: dynamic binding found: %s", b.String()))
  83. }
  84. singleton = false
  85. }
  86. }
  87. if isSingleton && !singleton {
  88. panic(fmt.Sprintf("binder: Singleton setting is set to true but struct has dynamic bindings: %s", typ))
  89. }
  90. s := &Struct{
  91. ptrValue: v,
  92. ptrType: typ,
  93. elementType: elem.Type(),
  94. bindings: bindings,
  95. Singleton: singleton,
  96. }
  97. isErrHandler := isErrorHandler(typ)
  98. newContainer := c.Clone()
  99. newContainer.fillReport(typ.String(), bindings)
  100. // Add the controller dependency itself as func dependency but with a known type which should be explicit binding
  101. // in order to keep its maximum priority.
  102. newContainer.Register(s.Acquire).Explicitly().DestType = typ
  103. newContainer.GetErrorHandler = func(ctx *context.Context) ErrorHandler {
  104. if isErrHandler {
  105. return ctx.Controller().Interface().(ErrorHandler)
  106. }
  107. return c.GetErrorHandler(ctx)
  108. }
  109. s.Container = newContainer
  110. return s
  111. }
  112. // Acquire returns a struct value based on the request.
  113. // If the dependencies are all static then these are already set-ed at the initialization of this Struct
  114. // and the same struct value instance will be returned, ignoring the Context. Otherwise
  115. // a new struct value with filled fields by its pre-calculated bindings will be returned instead.
  116. func (s *Struct) Acquire(ctx *context.Context) (reflect.Value, error) {
  117. if s.Singleton {
  118. ctx.Values().Set(context.ControllerContextKey, s.ptrValue)
  119. return s.ptrValue, nil
  120. }
  121. ctrl := ctx.Controller()
  122. if ctrl.Kind() == reflect.Invalid ||
  123. ctrl.Type() != s.ptrType /* in case of changing controller in the same request (see RouteOverlap feature) */ {
  124. ctrl = reflect.New(s.elementType)
  125. ctx.Values().Set(context.ControllerContextKey, ctrl)
  126. elem := ctrl.Elem()
  127. for _, b := range s.bindings {
  128. input, err := b.Dependency.Handle(ctx, b.Input)
  129. if err != nil {
  130. if err == ErrSeeOther {
  131. continue
  132. }
  133. s.Container.GetErrorHandler(ctx).HandleError(ctx, err)
  134. if ctx.IsStopped() {
  135. // return emptyValue, err
  136. return ctrl, err
  137. } // #1629
  138. }
  139. elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
  140. }
  141. }
  142. return ctrl, nil
  143. }
  144. // MethodHandler accepts a "methodName" that should be a valid an exported
  145. // method of the struct and returns its converted Handler.
  146. //
  147. // Second input is optional,
  148. // even zero is a valid value and can resolve path parameters correctly if from root party.
  149. func (s *Struct) MethodHandler(methodName string, paramsCount int) context.Handler {
  150. m, ok := s.ptrValue.Type().MethodByName(methodName)
  151. if !ok {
  152. panic(fmt.Sprintf("struct: method: %s does not exist", methodName))
  153. }
  154. return makeHandler(m.Func, s.Container, paramsCount)
  155. }