2017-11-23 11:43:29 +01:00
|
|
|
package mvc2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
|
|
|
// InputBinder is the result of `MakeBinder`.
|
|
|
|
// It contains the binder wrapped information, like the
|
|
|
|
// type that is responsible to bind
|
|
|
|
// and a function which will accept a context and returns a value of something.
|
|
|
|
type InputBinder struct {
|
|
|
|
BindType reflect.Type
|
2017-11-24 11:32:35 +01:00
|
|
|
// ctx is slice because all binder functions called by
|
|
|
|
// their `.Call` method which accepts a slice of reflect.Value,
|
|
|
|
// so on the handler maker we will allocate a slice of a single ctx once
|
|
|
|
// and used to all binders.
|
2017-11-23 21:36:47 +01:00
|
|
|
BindFunc func(ctx []reflect.Value) reflect.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
// getBindersForInput returns a map of the responsible binders for the "expected" types,
|
|
|
|
// which are the expected input parameters' types,
|
|
|
|
// based on the available "binders" collection.
|
|
|
|
//
|
|
|
|
// It returns a map which its key is the index of the "expected" which
|
|
|
|
// a valid binder for that in's type found,
|
|
|
|
// the value is the pointer of the responsible `InputBinder`.
|
|
|
|
//
|
|
|
|
// Check of "a nothing responsible for those expected types"
|
|
|
|
// should be done using the `len(m) == 0`.
|
|
|
|
func getBindersForInput(binders []*InputBinder, expected ...reflect.Type) map[int]*InputBinder {
|
|
|
|
var m map[int]*InputBinder
|
|
|
|
|
|
|
|
for idx, in := range expected {
|
2017-11-24 11:32:35 +01:00
|
|
|
if idx == 0 && isContext(in) {
|
|
|
|
// if the first is context then set it directly here.
|
|
|
|
m = make(map[int]*InputBinder)
|
|
|
|
m[0] = &InputBinder{
|
|
|
|
BindType: contextTyp,
|
|
|
|
BindFunc: func(ctxValues []reflect.Value) reflect.Value {
|
|
|
|
return ctxValues[0]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2017-11-23 21:36:47 +01:00
|
|
|
for _, b := range binders {
|
|
|
|
// if same type or the result of binder implements the expected in's type.
|
|
|
|
if equalTypes(b.BindType, in) {
|
|
|
|
if m == nil {
|
|
|
|
m = make(map[int]*InputBinder)
|
|
|
|
}
|
|
|
|
// fmt.Printf("set index: %d to type: %s where input type is: %s\n", idx, b.BindType.String(), in.String())
|
|
|
|
m[idx] = b
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
2017-11-23 11:43:29 +01:00
|
|
|
}
|
|
|
|
|
2017-11-23 21:36:47 +01:00
|
|
|
// MustMakeFuncInputBinder calls the `MakeFuncInputBinder` and returns its first result, see its docs.
|
2017-11-23 11:43:29 +01:00
|
|
|
// It panics on error.
|
2017-11-23 21:36:47 +01:00
|
|
|
func MustMakeFuncInputBinder(binder interface{}) *InputBinder {
|
|
|
|
b, err := MakeFuncInputBinder(binder)
|
2017-11-23 11:43:29 +01:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2017-11-23 21:36:47 +01:00
|
|
|
// MakeFuncInputBinder takes a binder function or a struct which contains a "Bind"
|
2017-11-23 11:43:29 +01:00
|
|
|
// function and returns an `InputBinder`, which Iris uses to
|
|
|
|
// resolve and set the input parameters when a handler is executed.
|
|
|
|
//
|
|
|
|
// The "binder" can have the following form:
|
|
|
|
// `func(iris.Context) UserViewModel`
|
|
|
|
// and a struct which contains a "Bind" method
|
|
|
|
// of the same binder form that was described above.
|
|
|
|
//
|
|
|
|
// The return type of the "binder" 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`).
|
2017-11-23 21:36:47 +01:00
|
|
|
func MakeFuncInputBinder(binder interface{}) (*InputBinder, error) {
|
2017-11-23 11:43:29 +01:00
|
|
|
v := reflect.ValueOf(binder)
|
|
|
|
|
|
|
|
// check if it's a struct or a pointer to a struct
|
|
|
|
// and contains a "Bind" method, if yes use that as the binder func.
|
|
|
|
if indirectTyp(v.Type()).Kind() == reflect.Struct {
|
|
|
|
if m := v.MethodByName("Bind"); m.IsValid() && m.CanInterface() {
|
|
|
|
v = m
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-23 21:36:47 +01:00
|
|
|
return makeFuncInputBinder(v)
|
2017-11-23 11:43:29 +01:00
|
|
|
}
|
|
|
|
|
2017-11-23 21:36:47 +01:00
|
|
|
func makeFuncInputBinder(fn reflect.Value) (*InputBinder, error) {
|
2017-11-23 11:43:29 +01:00
|
|
|
typ := indirectTyp(fn.Type())
|
|
|
|
|
|
|
|
// invalid if not a func.
|
|
|
|
if typ.Kind() != reflect.Func {
|
|
|
|
return nil, errBad
|
|
|
|
}
|
|
|
|
|
|
|
|
// invalid if not returns one single value.
|
|
|
|
if typ.NumOut() != 1 {
|
|
|
|
return nil, errBad
|
|
|
|
}
|
|
|
|
|
|
|
|
// invalid if input args length is not one.
|
|
|
|
if typ.NumIn() != 1 {
|
|
|
|
return nil, errBad
|
|
|
|
}
|
|
|
|
|
|
|
|
// invalid if that single input arg is not a typeof context.Context.
|
|
|
|
if !isContext(typ.In(0)) {
|
|
|
|
return nil, errBad
|
|
|
|
}
|
|
|
|
|
|
|
|
outTyp := typ.Out(0)
|
|
|
|
zeroOutVal := reflect.New(outTyp).Elem()
|
|
|
|
|
2017-11-23 21:36:47 +01:00
|
|
|
bf := func(ctxValue []reflect.Value) reflect.Value {
|
|
|
|
// []reflect.Value{reflect.ValueOf(ctx)}
|
|
|
|
results := fn.Call(ctxValue)
|
2017-11-23 11:43:29 +01:00
|
|
|
if len(results) == 0 {
|
|
|
|
return zeroOutVal
|
|
|
|
}
|
|
|
|
|
|
|
|
v := results[0]
|
|
|
|
if !v.IsValid() {
|
|
|
|
return zeroOutVal
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
return &InputBinder{
|
|
|
|
BindType: outTyp,
|
|
|
|
BindFunc: bf,
|
|
|
|
}, nil
|
|
|
|
}
|