mirror of
https://github.com/kataras/iris.git
synced 2026-03-08 01:16:30 +00:00
create a new package, name it as hero, I was thinking super or superb but hero is better name for what it does - the goal is to split the new 'mvc handlers' from the mvc system because they are not the same, users should know that they can use these type of rich binded handlers without controllers as well, like a normal handler and that I implemented here, the old files exist on the mvc package but will be removed at the next commit, I have to decide if we want type aliases for Result or no
Former-commit-id: cb775edc72bedc88aeab4c5a6de6bfc6bd56fae2
This commit is contained in:
202
hero/di/reflect.go
Normal file
202
hero/di/reflect.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package di
|
||||
|
||||
import "reflect"
|
||||
|
||||
// EmptyIn is just an empty slice of reflect.Value.
|
||||
var EmptyIn = []reflect.Value{}
|
||||
|
||||
// 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++ {
|
||||
zero = zero && IsZero(v.Field(i))
|
||||
}
|
||||
|
||||
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(EmptyIn)[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
|
||||
}
|
||||
zero := reflect.Zero(v.Type())
|
||||
return v.Interface() == zero.Interface()
|
||||
}
|
||||
|
||||
func IndirectValue(v reflect.Value) reflect.Value {
|
||||
return reflect.Indirect(v)
|
||||
}
|
||||
|
||||
func ValueOf(o interface{}) reflect.Value {
|
||||
if v, ok := o.(reflect.Value); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return reflect.ValueOf(o)
|
||||
}
|
||||
|
||||
func ValuesOf(valuesAsInterface []interface{}) (values []reflect.Value) {
|
||||
for _, v := range valuesAsInterface {
|
||||
values = append(values, ValueOf(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// IsFunc returns true if the passed type is function.
|
||||
func IsFunc(kindable interface {
|
||||
Kind() reflect.Kind
|
||||
}) bool {
|
||||
return kindable.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func equalTypes(got reflect.Type, expected reflect.Type) bool {
|
||||
if got == expected {
|
||||
return true
|
||||
}
|
||||
// if accepts an interface, check if the given "got" type does
|
||||
// implement this "expected" user handler's input argument.
|
||||
if expected.Kind() == reflect.Interface {
|
||||
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
|
||||
return got.Implements(expected)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// for controller's fields only.
|
||||
func structFieldIgnored(f reflect.StructField) bool {
|
||||
if !f.Anonymous {
|
||||
return true // if not anonymous(embedded), ignore it.
|
||||
}
|
||||
|
||||
s := f.Tag.Get("ignore")
|
||||
return s == "true" // if has an ignore tag then ignore it.
|
||||
}
|
||||
|
||||
type field struct {
|
||||
Type reflect.Type
|
||||
Name string // the actual name.
|
||||
Index []int // the index of the field, slice if it's part of a embedded struct
|
||||
CanSet bool // is true if it's exported.
|
||||
|
||||
// this could be empty, but in our cases it's not,
|
||||
// it's filled with the bind object (as service which means as static value)
|
||||
// and it's filled from the lookupFields' caller.
|
||||
AnyValue reflect.Value
|
||||
}
|
||||
|
||||
// NumFields returns the total number of fields, and the embedded, even if the embedded struct is not exported,
|
||||
// it will check for its exported fields.
|
||||
func NumFields(elemTyp reflect.Type, skipUnexported bool) int {
|
||||
return len(lookupFields(elemTyp, skipUnexported, nil))
|
||||
}
|
||||
|
||||
func lookupFields(elemTyp reflect.Type, skipUnexported bool, parentIndex []int) (fields []field) {
|
||||
if elemTyp.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
||||
f := elemTyp.Field(i)
|
||||
|
||||
if IndirectType(f.Type).Kind() == reflect.Struct &&
|
||||
!structFieldIgnored(f) {
|
||||
fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...)
|
||||
continue
|
||||
}
|
||||
|
||||
// skip unexported fields here,
|
||||
// after the check for embedded structs, these can be binded if their
|
||||
// fields are exported.
|
||||
isExported := f.PkgPath == ""
|
||||
if skipUnexported && !isExported {
|
||||
continue
|
||||
}
|
||||
|
||||
index := []int{i}
|
||||
if len(parentIndex) > 0 {
|
||||
index = append(parentIndex, i)
|
||||
}
|
||||
|
||||
field := field{
|
||||
Type: f.Type,
|
||||
Name: f.Name,
|
||||
Index: index,
|
||||
CanSet: isExported,
|
||||
}
|
||||
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// LookupNonZeroFieldsValues lookup for filled fields based on the "v" struct value instance.
|
||||
// It returns a slice of reflect.Value (same type as `Values`) that can be binded,
|
||||
// like the end-developer's custom values.
|
||||
func LookupNonZeroFieldsValues(v reflect.Value, skipUnexported bool) (bindValues []reflect.Value) {
|
||||
elem := IndirectValue(v)
|
||||
fields := lookupFields(IndirectType(v.Type()), skipUnexported, nil)
|
||||
|
||||
for _, f := range fields {
|
||||
if fieldVal := elem.FieldByIndex(f.Index); /*f.Type.Kind() == reflect.Ptr &&*/
|
||||
!IsZero(fieldVal) {
|
||||
bindValues = append(bindValues, fieldVal)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user