object.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package di
  2. import (
  3. "errors"
  4. "reflect"
  5. )
  6. // BindType is the type of a binded object/value, it's being used to
  7. // check if the value is accessible after a function call with a "ctx" when needed ( Dynamic type)
  8. // or it's just a struct value (a service | Static type).
  9. type BindType uint32
  10. const (
  11. // Static is the simple assignable value, a static value.
  12. Static BindType = iota
  13. // Dynamic returns a value but it depends on some input arguments from the caller,
  14. // on serve time.
  15. Dynamic
  16. )
  17. func bindTypeString(typ BindType) string {
  18. switch typ {
  19. case Dynamic:
  20. return "Dynamic"
  21. default:
  22. return "Static"
  23. }
  24. }
  25. // BindObject contains the dependency value's read-only information.
  26. // FuncInjector and StructInjector keeps information about their
  27. // input arguments/or fields, these properties contain a `BindObject` inside them.
  28. type BindObject struct {
  29. Type reflect.Type // the Type of 'Value' or the type of the returned 'ReturnValue' .
  30. Value reflect.Value
  31. BindType BindType
  32. ReturnValue func([]reflect.Value) reflect.Value
  33. }
  34. // MakeBindObject accepts any "v" value, struct, pointer or a function
  35. // and a type checker that is used to check if the fields (if "v.elem()" is struct)
  36. // or the input arguments (if "v.elem()" is func)
  37. // are valid to be included as the final object's dependencies, even if the caller added more
  38. // the "di" is smart enough to select what each "v" needs and what not before serve time.
  39. func MakeBindObject(v reflect.Value, goodFunc TypeChecker) (b BindObject, err error) {
  40. if IsFunc(v) {
  41. b.BindType = Dynamic
  42. b.ReturnValue, b.Type, err = MakeReturnValue(v, goodFunc)
  43. } else {
  44. b.BindType = Static
  45. b.Type = v.Type()
  46. b.Value = v
  47. }
  48. return
  49. }
  50. var errBad = errors.New("bad")
  51. // MakeReturnValue takes any function
  52. // that accept custom values and returns something,
  53. // it returns a binder function, which accepts a slice of reflect.Value
  54. // and returns a single one reflect.Value for that.
  55. // It's being used to resolve the input parameters on a "x" consumer faster.
  56. //
  57. // The "fn" can have the following form:
  58. // `func(myService) MyViewModel`.
  59. //
  60. // The return type of the "fn" should be a value instance, not a pointer, for your own protection.
  61. // The binder function should return only one value.
  62. func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker) (func([]reflect.Value) reflect.Value, reflect.Type, error) {
  63. typ := IndirectType(fn.Type())
  64. // invalid if not a func.
  65. if typ.Kind() != reflect.Func {
  66. return nil, typ, errBad
  67. }
  68. // invalid if not returns one single value.
  69. if typ.NumOut() != 1 {
  70. return nil, typ, errBad
  71. }
  72. if goodFunc != nil {
  73. if !goodFunc(typ) {
  74. return nil, typ, errBad
  75. }
  76. }
  77. outTyp := typ.Out(0)
  78. zeroOutVal := reflect.New(outTyp).Elem()
  79. bf := func(ctxValue []reflect.Value) reflect.Value {
  80. results := fn.Call(ctxValue)
  81. if len(results) == 0 {
  82. return zeroOutVal
  83. }
  84. v := results[0]
  85. if !v.IsValid() {
  86. return zeroOutVal
  87. }
  88. return v
  89. }
  90. return bf, outTyp, nil
  91. }
  92. // IsAssignable checks if "to" type can be used as "b.Value/ReturnValue".
  93. func (b *BindObject) IsAssignable(to reflect.Type) bool {
  94. return equalTypes(b.Type, to)
  95. }
  96. // Assign sets the values to a setter, "toSetter" contains the setter, so the caller
  97. // can use it for multiple and different structs/functions as well.
  98. func (b *BindObject) Assign(ctx []reflect.Value, toSetter func(reflect.Value)) {
  99. if b.BindType == Dynamic {
  100. toSetter(b.ReturnValue(ctx))
  101. return
  102. }
  103. toSetter(b.Value)
  104. }