123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- 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()
- }
- // IndirectValue returns the reflect.Value that "v" points to.
- // If "v" is a nil pointer, Indirect returns a zero Value.
- // If "v" is not a pointer, Indirect returns v.
- func IndirectValue(v reflect.Value) reflect.Value {
- return reflect.Indirect(v)
- }
- // ValueOf returns the reflect.Value of "o".
- // If "o" is already a reflect.Value returns "o".
- func ValueOf(o interface{}) reflect.Value {
- if v, ok := o.(reflect.Value); ok {
- return v
- }
- return reflect.ValueOf(o)
- }
- // ValuesOf same as `ValueOf` but accepts a slice of
- // somethings and returns a slice of reflect.Value.
- func ValuesOf(valuesAsInterface []interface{}) (values []reflect.Value) {
- for _, v := range valuesAsInterface {
- values = append(values, ValueOf(v))
- }
- return
- }
- // 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()
- }
- // 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
- }
|