123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- package hero
- import (
- "fmt"
- "reflect"
- "github.com/kataras/iris/v12/context"
- )
- // Sorter is the type for sort customization of a struct's fields
- // and its available bindable values.
- //
- // Sorting applies only when a field can accept more than one registered value.
- type Sorter func(t1 reflect.Type, t2 reflect.Type) bool
- // sortByNumMethods is a builtin sorter to sort fields and values
- // based on their type and its number of methods, highest number of methods goes first.
- //
- // It is the default sorter on struct injector of `hero.Struct` method.
- var sortByNumMethods Sorter = func(t1 reflect.Type, t2 reflect.Type) bool {
- if t1.Kind() != t2.Kind() {
- return true
- }
- if k := t1.Kind(); k == reflect.Interface || k == reflect.Struct {
- return t1.NumMethod() > t2.NumMethod()
- } else if k != reflect.Struct {
- return false // non-structs goes last.
- }
- return true
- }
- // Struct keeps a record of a particular struct value injection.
- // See `Container.Struct` and `mvc#Application.Handle` methods.
- type Struct struct {
- ptrType reflect.Type
- ptrValue reflect.Value // the original ptr struct value.
- elementType reflect.Type // the original struct type.
- bindings []*binding // struct field bindings.
- Container *Container
- Singleton bool
- }
- type singletonStruct interface {
- Singleton() bool
- }
- func isMarkedAsSingleton(structPtr any) bool {
- if sing, ok := structPtr.(singletonStruct); ok && sing.Singleton() {
- return true
- }
- return false
- }
- func makeStruct(structPtr interface{}, c *Container, partyParamsCount int) *Struct {
- v := valueOf(structPtr)
- typ := v.Type()
- if typ.Kind() != reflect.Ptr || indirectType(typ).Kind() != reflect.Struct {
- panic("binder: struct: should be a pointer to a struct value")
- }
- isSingleton := isMarkedAsSingleton(structPtr)
- disablePayloadAutoBinding := c.DisablePayloadAutoBinding
- enableStructDependents := c.EnableStructDependents
- disableStructDynamicBindings := c.DisableStructDynamicBindings
- if isSingleton {
- disablePayloadAutoBinding = true
- enableStructDependents = false
- disableStructDynamicBindings = true
- }
- // get struct's fields bindings.
- bindings := getBindingsForStruct(v, c.Dependencies, c.MarkExportedFieldsAsRequired, disablePayloadAutoBinding, enableStructDependents, c.DependencyMatcher, partyParamsCount, c.Sorter)
- // length bindings of 0, means that it has no fields or all mapped deps are static.
- // If static then Struct.Acquire will return the same "value" instance, otherwise it will create a new one.
- singleton := true
- elem := v.Elem()
- // fmt.Printf("Service: %s, Bindings(%d):\n", typ, len(bindings))
- for _, b := range bindings {
- // fmt.Printf("* " + b.String() + "\n")
- if b.Dependency.Static {
- // Fill now.
- input, err := b.Dependency.Handle(nil, b.Input)
- if err != nil {
- if err == ErrSeeOther {
- continue
- }
- panic(err)
- }
- elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
- } else if !b.Dependency.Static {
- if disableStructDynamicBindings {
- panic(fmt.Sprintf("binder: DisableStructDynamicBindings setting is set to true: dynamic binding found: %s", b.String()))
- }
- singleton = false
- }
- }
- if isSingleton && !singleton {
- panic(fmt.Sprintf("binder: Singleton setting is set to true but struct has dynamic bindings: %s", typ))
- }
- s := &Struct{
- ptrValue: v,
- ptrType: typ,
- elementType: elem.Type(),
- bindings: bindings,
- Singleton: singleton,
- }
- isErrHandler := isErrorHandler(typ)
- newContainer := c.Clone()
- newContainer.fillReport(typ.String(), bindings)
- // Add the controller dependency itself as func dependency but with a known type which should be explicit binding
- // in order to keep its maximum priority.
- newContainer.Register(s.Acquire).Explicitly().DestType = typ
- newContainer.GetErrorHandler = func(ctx *context.Context) ErrorHandler {
- if isErrHandler {
- return ctx.Controller().Interface().(ErrorHandler)
- }
- return c.GetErrorHandler(ctx)
- }
- s.Container = newContainer
- return s
- }
- // Acquire returns a struct value based on the request.
- // If the dependencies are all static then these are already set-ed at the initialization of this Struct
- // and the same struct value instance will be returned, ignoring the Context. Otherwise
- // a new struct value with filled fields by its pre-calculated bindings will be returned instead.
- func (s *Struct) Acquire(ctx *context.Context) (reflect.Value, error) {
- if s.Singleton {
- ctx.Values().Set(context.ControllerContextKey, s.ptrValue)
- return s.ptrValue, nil
- }
- ctrl := ctx.Controller()
- if ctrl.Kind() == reflect.Invalid ||
- ctrl.Type() != s.ptrType /* in case of changing controller in the same request (see RouteOverlap feature) */ {
- ctrl = reflect.New(s.elementType)
- ctx.Values().Set(context.ControllerContextKey, ctrl)
- elem := ctrl.Elem()
- for _, b := range s.bindings {
- input, err := b.Dependency.Handle(ctx, b.Input)
- if err != nil {
- if err == ErrSeeOther {
- continue
- }
- s.Container.GetErrorHandler(ctx).HandleError(ctx, err)
- if ctx.IsStopped() {
- // return emptyValue, err
- return ctrl, err
- } // #1629
- }
- elem.FieldByIndex(b.Input.StructFieldIndex).Set(input)
- }
- }
- return ctrl, nil
- }
- // MethodHandler accepts a "methodName" that should be a valid an exported
- // method of the struct and returns its converted Handler.
- //
- // Second input is optional,
- // even zero is a valid value and can resolve path parameters correctly if from root party.
- func (s *Struct) MethodHandler(methodName string, paramsCount int) context.Handler {
- m, ok := s.ptrValue.Type().MethodByName(methodName)
- if !ok {
- panic(fmt.Sprintf("struct: method: %s does not exist", methodName))
- }
- return makeHandler(m.Func, s.Container, paramsCount)
- }
|