struct.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package hero
  2. import (
  3. "fmt"
  4. "reflect"
  5. "github.com/kataras/iris/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. func makeStruct(structPtr interface{}, c *Container, partyParamsCount int) *Struct {
  38. v := valueOf(structPtr)
  39. typ := v.Type()
  40. if typ.Kind() != reflect.Ptr || indirectType(typ).Kind() != reflect.Struct {
  41. panic("binder: struct: should be a pointer to a struct value")
  42. }
  43. // get struct's fields bindings.
  44. bindings := getBindingsForStruct(v, c.Dependencies, partyParamsCount, c.Sorter)
  45. // length bindings of 0, means that it has no fields or all mapped deps are static.
  46. // If static then Struct.Acquire will return the same "value" instance, otherwise it will create a new one.
  47. singleton := true
  48. elem := v.Elem()
  49. for _, b := range bindings {
  50. if b.Dependency.Static {
  51. // Fill now.
  52. input, err := b.Dependency.Handle(nil, b.Input)
  53. if err != nil {
  54. if err == ErrSeeOther {
  55. continue
  56. }
  57. panic(err)
  58. }
  59. elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
  60. } else if !b.Dependency.Static {
  61. singleton = false
  62. }
  63. }
  64. s := &Struct{
  65. ptrValue: v,
  66. ptrType: typ,
  67. elementType: elem.Type(),
  68. bindings: bindings,
  69. Singleton: singleton,
  70. }
  71. isErrHandler := isErrorHandler(typ)
  72. newContainer := c.Clone()
  73. // Add the controller dependency itself as func dependency but with a known type which should be explicit binding
  74. // in order to keep its maximum priority.
  75. newContainer.Register(s.Acquire).
  76. Explicitly().
  77. DestType = typ
  78. newContainer.GetErrorHandler = func(ctx *context.Context) ErrorHandler {
  79. if isErrHandler {
  80. return ctx.Controller().Interface().(ErrorHandler)
  81. }
  82. return c.GetErrorHandler(ctx)
  83. }
  84. s.Container = newContainer
  85. return s
  86. }
  87. // Acquire returns a struct value based on the request.
  88. // If the dependencies are all static then these are already set-ed at the initialization of this Struct
  89. // and the same struct value instance will be returned, ignoring the Context. Otherwise
  90. // a new struct value with filled fields by its pre-calculated bindings will be returned instead.
  91. func (s *Struct) Acquire(ctx *context.Context) (reflect.Value, error) {
  92. if s.Singleton {
  93. ctx.Values().Set(context.ControllerContextKey, s.ptrValue)
  94. return s.ptrValue, nil
  95. }
  96. ctrl := ctx.Controller()
  97. if ctrl.Kind() == reflect.Invalid ||
  98. ctrl.Type() != s.ptrType /* in case of changing controller in the same request (see RouteOverlap feature) */ {
  99. ctrl = reflect.New(s.elementType)
  100. ctx.Values().Set(context.ControllerContextKey, ctrl)
  101. elem := ctrl.Elem()
  102. for _, b := range s.bindings {
  103. input, err := b.Dependency.Handle(ctx, b.Input)
  104. if err != nil {
  105. if err == ErrSeeOther {
  106. continue
  107. }
  108. // return emptyValue, err
  109. return ctrl, err
  110. }
  111. elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
  112. }
  113. }
  114. return ctrl, nil
  115. }
  116. // MethodHandler accepts a "methodName" that should be a valid an exported
  117. // method of the struct and returns its converted Handler.
  118. //
  119. // Second input is optional,
  120. // even zero is a valid value and can resolve path parameters correctly if from root party.
  121. func (s *Struct) MethodHandler(methodName string, paramsCount int) context.Handler {
  122. m, ok := s.ptrValue.Type().MethodByName(methodName)
  123. if !ok {
  124. panic(fmt.Sprintf("struct: method: %s does not exist", methodName))
  125. }
  126. return makeHandler(m.Func, s.Container, paramsCount)
  127. }