mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
c15763c556
Former-commit-id: 0b3fb2841d5032ff47bdca42a6f4ccfeb789ce3c
126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
package di
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
type (
|
|
targetFuncInput struct {
|
|
Object *BindObject
|
|
InputIndex int
|
|
}
|
|
|
|
FuncInjector struct {
|
|
// the original function, is being used
|
|
// only the .Call, which is referring to the same function, always.
|
|
fn reflect.Value
|
|
|
|
inputs []*targetFuncInput
|
|
// Length is the number of the valid, final binded input arguments.
|
|
Length int
|
|
// Valid is True when `Length` is > 0, it's statically set-ed for
|
|
// performance reasons.
|
|
Valid bool
|
|
|
|
trace string // for debug info.
|
|
}
|
|
)
|
|
|
|
func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *FuncInjector {
|
|
typ := IndirectType(fn.Type())
|
|
s := &FuncInjector{
|
|
fn: fn,
|
|
}
|
|
|
|
if !IsFunc(typ) {
|
|
return s
|
|
}
|
|
|
|
n := typ.NumIn()
|
|
|
|
// function input can have many values of the same types,
|
|
// so keep track of them in order to not set a func input to a next bind value,
|
|
// i.e (string, string) with two different binder funcs because of the different param's name.
|
|
consumedValues := make(map[int]bool, n)
|
|
|
|
for i := 0; i < n; i++ {
|
|
inTyp := typ.In(i)
|
|
|
|
if hijack != nil {
|
|
if b, ok := hijack(inTyp); ok && b != nil {
|
|
s.inputs = append(s.inputs, &targetFuncInput{
|
|
InputIndex: i,
|
|
Object: b,
|
|
})
|
|
continue
|
|
}
|
|
}
|
|
|
|
for valIdx, val := range values {
|
|
if _, shouldSkip := consumedValues[valIdx]; shouldSkip {
|
|
continue
|
|
}
|
|
inTyp := typ.In(i)
|
|
|
|
// the binded values to the func's inputs.
|
|
b, err := MakeBindObject(val, goodFunc)
|
|
|
|
if err != nil {
|
|
return s // if error stop here.
|
|
}
|
|
|
|
if b.IsAssignable(inTyp) {
|
|
// println(inTyp.String() + " is assignable to " + val.Type().String())
|
|
// fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
|
|
// i, b.Type.String(), val.String(), val.Pointer())
|
|
s.inputs = append(s.inputs, &targetFuncInput{
|
|
InputIndex: i,
|
|
Object: &b,
|
|
})
|
|
|
|
consumedValues[valIdx] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
s.Length = len(s.inputs)
|
|
s.Valid = s.Length > 0
|
|
|
|
for i, in := range s.inputs {
|
|
bindmethodTyp := bindTypeString(in.Object.BindType)
|
|
typIn := typ.In(in.InputIndex)
|
|
// remember: on methods that are part of a struct (i.e controller)
|
|
// the input index = 1 is the begggining instead of the 0,
|
|
// because the 0 is the controller receiver pointer of the method.
|
|
s.trace += fmt.Sprintf("[%d] %s binding: '%s' for input position: %d and type: '%s'\n", i+1, bindmethodTyp, in.Object.Type.String(), in.InputIndex, typIn.String())
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *FuncInjector) String() string {
|
|
return s.trace
|
|
}
|
|
|
|
func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
|
|
args := *in
|
|
for _, input := range s.inputs {
|
|
input.Object.Assign(ctx, func(v reflect.Value) {
|
|
// fmt.Printf("assign input index: %d for value: %v\n",
|
|
// input.InputIndex, v.String())
|
|
args[input.InputIndex] = v
|
|
})
|
|
|
|
}
|
|
|
|
*in = args
|
|
}
|
|
|
|
func (s *FuncInjector) Call(ctx ...reflect.Value) []reflect.Value {
|
|
in := make([]reflect.Value, s.Length, s.Length)
|
|
s.Inject(&in, ctx...)
|
|
return s.fn.Call(in)
|
|
}
|