mirror of
https://github.com/kataras/iris.git
synced 2025-12-20 03:17:04 +00:00
create one generic package for dependency injection which can be used outside of Iris too - worked but unfished
Former-commit-id: a9d600321c07d7c9f39105416f14ae91528a16a3
This commit is contained in:
254
mvc2/bind.go
254
mvc2/bind.go
@@ -1,258 +1,34 @@
|
||||
package mvc2
|
||||
|
||||
import "reflect"
|
||||
|
||||
type bindType uint32
|
||||
|
||||
const (
|
||||
objectType bindType = iota // simple assignable value.
|
||||
functionResultType // dynamic value, depends on the context.
|
||||
import (
|
||||
"github.com/kataras/di"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type bindObject struct {
|
||||
Type reflect.Type // the Type of 'Value' or the type of the returned 'ReturnValue' .
|
||||
Value reflect.Value
|
||||
|
||||
BindType bindType
|
||||
ReturnValue func(ctx []reflect.Value) reflect.Value
|
||||
}
|
||||
|
||||
// makeReturnValue takes any function
|
||||
// that accept a context and returns something
|
||||
// and returns a binder function, which accepts the context as slice of reflect.Value
|
||||
// and returns a reflect.Value for that.
|
||||
// Iris uses to
|
||||
// resolve and set the input parameters when a handler is executed.
|
||||
//
|
||||
// The "fn" can have the following form:
|
||||
// `func(iris.Context) UserViewModel`.
|
||||
//
|
||||
// The return type of the "fn" should be a value instance, not a pointer, for your own protection.
|
||||
// The binder function should return only one value and
|
||||
// it can accept only one input argument,
|
||||
// the Iris' Context (`context.Context` or `iris.Context`).
|
||||
func makeReturnValue(fn reflect.Value) (func([]reflect.Value) reflect.Value, reflect.Type, error) {
|
||||
typ := indirectTyp(fn.Type())
|
||||
|
||||
// invalid if not a func.
|
||||
if typ.Kind() != reflect.Func {
|
||||
return nil, typ, errBad
|
||||
var (
|
||||
typeChecker = func(fn reflect.Type) bool {
|
||||
// invalid if that single input arg is not a typeof context.Context.
|
||||
return isContext(fn.In(0))
|
||||
}
|
||||
|
||||
// invalid if not returns one single value.
|
||||
if typ.NumOut() != 1 {
|
||||
return nil, typ, errBad
|
||||
}
|
||||
|
||||
// invalid if input args length is not one.
|
||||
if typ.NumIn() != 1 {
|
||||
return nil, typ, errBad
|
||||
}
|
||||
|
||||
// invalid if that single input arg is not a typeof context.Context.
|
||||
if !isContext(typ.In(0)) {
|
||||
return nil, typ, errBad
|
||||
}
|
||||
|
||||
outTyp := typ.Out(0)
|
||||
zeroOutVal := reflect.New(outTyp).Elem()
|
||||
|
||||
bf := func(ctxValue []reflect.Value) reflect.Value {
|
||||
results := fn.Call(ctxValue) // ctxValue is like that because of; read makeHandler.
|
||||
if len(results) == 0 {
|
||||
return zeroOutVal
|
||||
hijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
|
||||
if isContext(fieldOrFuncInput) {
|
||||
return newContextBindObject(), true
|
||||
}
|
||||
|
||||
v := results[0]
|
||||
if !v.IsValid() {
|
||||
return zeroOutVal
|
||||
}
|
||||
return v
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return bf, outTyp, nil
|
||||
}
|
||||
|
||||
func makeBindObject(v reflect.Value) (b bindObject, err error) {
|
||||
if isFunc(v) {
|
||||
b.BindType = functionResultType
|
||||
b.ReturnValue, b.Type, err = makeReturnValue(v)
|
||||
} else {
|
||||
b.BindType = objectType
|
||||
b.Type = v.Type()
|
||||
b.Value = v
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
// newContextBindObject is being used on both targetFunc and targetStruct.
|
||||
// if the func's input argument or the struct's field is a type of Context
|
||||
// then we can do a fast binding using the ctxValue
|
||||
// which is used as slice of reflect.Value, because of the final method's `Call`.
|
||||
func newContextBindObject() *bindObject {
|
||||
return &bindObject{
|
||||
func newContextBindObject() *di.BindObject {
|
||||
return &di.BindObject{
|
||||
Type: contextTyp,
|
||||
BindType: functionResultType,
|
||||
BindType: di.Dynamic,
|
||||
ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
||||
return ctxValue[0]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bindObject) IsAssignable(to reflect.Type) bool {
|
||||
return equalTypes(b.Type, to)
|
||||
}
|
||||
|
||||
func (b *bindObject) Assign(ctx []reflect.Value, toSetter func(reflect.Value)) {
|
||||
if b.BindType == functionResultType {
|
||||
toSetter(b.ReturnValue(ctx))
|
||||
return
|
||||
}
|
||||
toSetter(b.Value)
|
||||
}
|
||||
|
||||
type (
|
||||
targetField struct {
|
||||
Object *bindObject
|
||||
FieldIndex []int
|
||||
}
|
||||
targetFuncInput struct {
|
||||
Object *bindObject
|
||||
InputIndex int
|
||||
}
|
||||
)
|
||||
|
||||
type targetStruct struct {
|
||||
Fields []*targetField
|
||||
Valid bool // is True when contains fields and it's a valid target struct.
|
||||
}
|
||||
|
||||
func newTargetStruct(v reflect.Value, bindValues ...reflect.Value) *targetStruct {
|
||||
typ := indirectTyp(v.Type())
|
||||
s := &targetStruct{}
|
||||
|
||||
fields := lookupFields(typ, nil)
|
||||
for _, f := range fields {
|
||||
// if it's context then bind it directly here and continue to the next field.
|
||||
if isContext(f.Type) {
|
||||
s.Fields = append(s.Fields, &targetField{
|
||||
FieldIndex: f.Index,
|
||||
Object: newContextBindObject(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
for _, val := range bindValues {
|
||||
// the binded values to the struct's fields.
|
||||
b, err := makeBindObject(val)
|
||||
|
||||
if err != nil {
|
||||
return s // if error stop here.
|
||||
}
|
||||
|
||||
if b.IsAssignable(f.Type) {
|
||||
// fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String())
|
||||
s.Fields = append(s.Fields, &targetField{
|
||||
FieldIndex: f.Index,
|
||||
Object: &b,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
s.Valid = len(s.Fields) > 0
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *targetStruct) Fill(destElem reflect.Value, ctx ...reflect.Value) {
|
||||
for _, f := range s.Fields {
|
||||
f.Object.Assign(ctx, func(v reflect.Value) {
|
||||
// if isContext(v.Type()) {
|
||||
// println("WTF BIND CONTEXT TYPE WHEN BASE CONTROLLER?")
|
||||
// }
|
||||
destElem.FieldByIndex(f.FieldIndex).Set(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type targetFunc struct {
|
||||
Inputs []*targetFuncInput
|
||||
Valid bool // is True when contains func inputs and it's a valid target func.
|
||||
}
|
||||
|
||||
func newTargetFunc(fn reflect.Value, bindValues ...reflect.Value) *targetFunc {
|
||||
typ := indirectTyp(fn.Type())
|
||||
s := &targetFunc{
|
||||
Valid: false,
|
||||
}
|
||||
|
||||
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 it's context then bind it directly here and continue to the next func's input arg.
|
||||
if isContext(inTyp) {
|
||||
s.Inputs = append(s.Inputs, &targetFuncInput{
|
||||
InputIndex: i,
|
||||
Object: newContextBindObject(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
for valIdx, val := range bindValues {
|
||||
if _, shouldSkip := consumedValues[valIdx]; shouldSkip {
|
||||
continue
|
||||
}
|
||||
inTyp := typ.In(i)
|
||||
|
||||
// the binded values to the func's inputs.
|
||||
b, err := makeBindObject(val)
|
||||
|
||||
if err != nil {
|
||||
return s // if error stop here.
|
||||
}
|
||||
|
||||
if b.IsAssignable(inTyp) {
|
||||
// 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.Valid = len(s.Inputs) > 0
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *targetFunc) Fill(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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user