mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
246 lines
6.2 KiB
Go
246 lines
6.2 KiB
Go
package hero
|
|
|
|
import (
|
|
"net"
|
|
"reflect"
|
|
|
|
"github.com/kataras/iris/v12/context"
|
|
)
|
|
|
|
func valueOf(v interface{}) reflect.Value {
|
|
if val, ok := v.(reflect.Value); ok {
|
|
// check if it's already a reflect.Value.
|
|
return val
|
|
}
|
|
|
|
return reflect.ValueOf(v)
|
|
}
|
|
|
|
// indirectType returns the value of a pointer-type "typ".
|
|
// If "typ" is a pointer, array, chan, map or slice it returns its Elem,
|
|
// otherwise returns the typ as it's.
|
|
func indirectType(typ reflect.Type) reflect.Type {
|
|
switch typ.Kind() {
|
|
case reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
|
return typ.Elem()
|
|
}
|
|
return typ
|
|
}
|
|
|
|
func goodVal(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
|
|
if v.IsNil() {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return v.IsValid()
|
|
}
|
|
|
|
func isFunc(kindable interface{ Kind() reflect.Kind }) bool {
|
|
return kindable.Kind() == reflect.Func
|
|
}
|
|
|
|
var (
|
|
inputTyp = reflect.TypeOf((*Input)(nil))
|
|
errTyp = reflect.TypeOf((*error)(nil)).Elem()
|
|
|
|
ipTyp = reflect.TypeOf(net.IP{})
|
|
)
|
|
|
|
// isError returns true if "typ" is type of `error`.
|
|
func isError(typ reflect.Type) bool {
|
|
return typ.Implements(errTyp)
|
|
}
|
|
|
|
func toError(v reflect.Value) error {
|
|
if v.IsNil() {
|
|
return nil
|
|
}
|
|
|
|
return v.Interface().(error)
|
|
}
|
|
|
|
var contextType = reflect.TypeOf((*context.Context)(nil))
|
|
|
|
// isContext returns true if the "typ" is a type of Context.
|
|
func isContext(typ reflect.Type) bool {
|
|
return typ == contextType
|
|
}
|
|
|
|
var errorHandlerTyp = reflect.TypeOf((*ErrorHandler)(nil)).Elem()
|
|
|
|
func isErrorHandler(typ reflect.Type) bool {
|
|
return typ.Implements(errorHandlerTyp)
|
|
}
|
|
|
|
var emptyValue reflect.Value
|
|
|
|
func equalTypes(binding reflect.Type, input reflect.Type) bool {
|
|
if binding == input {
|
|
return true
|
|
}
|
|
|
|
// fmt.Printf("got: %s expected: %s\n", got.String(), expected.String())
|
|
// if accepts an interface, check if the given "got" type does
|
|
// implement this "expected" user handler's input argument.
|
|
if input.Kind() == reflect.Interface {
|
|
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", binding.String(), input.String())
|
|
// return input.Implements(binding)
|
|
return binding.AssignableTo(input)
|
|
}
|
|
|
|
// dependency: func(...) interface{} { return "string" }
|
|
// expected input: string.
|
|
if binding.Kind() == reflect.Interface {
|
|
return input.AssignableTo(binding)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func structFieldIgnored(f reflect.StructField) bool {
|
|
if !f.Anonymous {
|
|
return true // if not anonymous(embedded), ignore it.
|
|
}
|
|
|
|
if s := f.Tag.Get("ignore"); s == "true" {
|
|
return true
|
|
}
|
|
|
|
if s := f.Tag.Get("stateless"); s == "true" {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// all except non-zero.
|
|
func lookupFields(elem reflect.Value, skipUnexported bool, onlyZeros bool, parentIndex []int) (fields []reflect.StructField, stateless int) {
|
|
elemTyp := elem.Type()
|
|
for i, n := 0, elem.NumField(); i < n; i++ {
|
|
field := elemTyp.Field(i)
|
|
fieldValue := elem.Field(i)
|
|
|
|
// embed any fields from other structs.
|
|
if indirectType(field.Type).Kind() == reflect.Struct {
|
|
if structFieldIgnored(field) {
|
|
stateless++ // don't skip the loop yet, e.g. iris.Context.
|
|
} else {
|
|
structFields, statelessN := lookupFields(fieldValue, skipUnexported, onlyZeros, append(parentIndex, i))
|
|
stateless += statelessN
|
|
fields = append(fields, structFields...)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if onlyZeros && !isZero(fieldValue) {
|
|
continue
|
|
}
|
|
|
|
// skip unexported fields here.
|
|
if isExported := field.PkgPath == ""; skipUnexported && !isExported {
|
|
continue
|
|
}
|
|
|
|
index := []int{i}
|
|
if len(parentIndex) > 0 {
|
|
index = append(parentIndex, i)
|
|
}
|
|
|
|
tmp := make([]int, len(index))
|
|
copy(tmp, index)
|
|
field.Index = tmp
|
|
|
|
fields = append(fields, field)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func lookupNonZeroFieldValues(elem reflect.Value) (nonZeroFields []reflect.StructField) {
|
|
fields, _ := lookupFields(elem, true, false, nil)
|
|
for _, f := range fields {
|
|
if fieldVal := elem.FieldByIndex(f.Index); goodVal(fieldVal) && !isZero(fieldVal) {
|
|
/* && f.Type.Kind() == reflect.Ptr &&*/
|
|
nonZeroFields = append(nonZeroFields, f)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// isZero returns true if a value is nil.
|
|
// Remember; fields to be checked should be exported otherwise it returns false.
|
|
// Notes for users:
|
|
// Boolean's zero value is false, even if not set-ed.
|
|
// UintXX are not zero on 0 because they are pointers to.
|
|
func isZero(v reflect.Value) bool {
|
|
// switch v.Kind() {
|
|
// case reflect.Struct:
|
|
// zero := true
|
|
// for i := 0; i < v.NumField(); i++ {
|
|
// f := v.Field(i)
|
|
// if f.Type().PkgPath() != "" {
|
|
// continue // unexported.
|
|
// }
|
|
// zero = zero && isZero(f)
|
|
// }
|
|
|
|
// if typ := v.Type(); typ != nil && v.IsValid() {
|
|
// f, ok := typ.MethodByName("IsZero")
|
|
// // if not found
|
|
// // if has input arguments (1 is for the value receiver, so > 1 for the actual input args)
|
|
// // if output argument is not boolean
|
|
// // then skip this IsZero user-defined function.
|
|
// if !ok || f.Type.NumIn() > 1 || f.Type.NumOut() != 1 && f.Type.Out(0).Kind() != reflect.Bool {
|
|
// return zero
|
|
// }
|
|
|
|
// method := v.Method(f.Index)
|
|
// // no needed check but:
|
|
// if method.IsValid() && !method.IsNil() {
|
|
// // it shouldn't panic here.
|
|
// zero = method.Call([]reflect.Value{})[0].Interface().(bool)
|
|
// }
|
|
// }
|
|
|
|
// return zero
|
|
// case reflect.Func, reflect.Map, reflect.Slice:
|
|
// return v.IsNil()
|
|
// case reflect.Array:
|
|
// zero := true
|
|
// for i := 0; i < v.Len(); i++ {
|
|
// zero = zero && isZero(v.Index(i))
|
|
// }
|
|
// return zero
|
|
// }
|
|
// if not any special type then use the reflect's .Zero
|
|
// usually for fields, but remember if it's boolean and it's false
|
|
// then it's zero, even if set-ed.
|
|
|
|
if !v.CanInterface() {
|
|
// if can't interface, i.e return value from unexported field or method then return false
|
|
return false
|
|
}
|
|
|
|
if v.Type() == ipTyp {
|
|
return len(v.Interface().(net.IP)) == 0
|
|
}
|
|
|
|
zero := reflect.Zero(v.Type())
|
|
return v.Interface() == zero.Interface()
|
|
}
|
|
|
|
// IsNil same as `reflect.IsNil` but a bit safer to use, returns false if not a correct type.
|
|
func isNil(v reflect.Value) bool {
|
|
k := v.Kind()
|
|
switch k {
|
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
|
return v.IsNil()
|
|
default:
|
|
return false
|
|
}
|
|
}
|