Skip to content

Commit

Permalink
add a new x/reflex sub-package for reflection common functions - may …
Browse files Browse the repository at this point in the history
…be improved in the near future
  • Loading branch information
kataras committed Mar 6, 2022
1 parent fc2f8f4 commit 410e5ea
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 0 deletions.
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ The codebase for Dependency Injection, Internationalization and localization and

## Fixes and Improvements

- Add a new [x/reflex](/x/reflex) sub-package.

- Add `Context.ReadMultipartRelated` as requested at: [issues/#1787](https://github.com/kataras/iris/issues/1787).

- Add `Container.DependencyMatcher` and `Dependency.Match` to implement the feature requested at [issues/#1842](https://github.com/kataras/iris/issues/1842).
Expand Down
8 changes: 8 additions & 0 deletions x/reflex/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package reflex

import "reflect"

// IsError reports whether "typ" is an error type.
func IsError(typ interface{ Implements(reflect.Type) bool }) bool {
return typ.Implements(ErrTyp)
}
44 changes: 44 additions & 0 deletions x/reflex/func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package reflex

import "reflect"

// IsFunc reports whether the "kindable" is a type of function.
func IsFunc(typ interface{ Kind() reflect.Kind }) bool {
return typ.Kind() == reflect.Func
}

// FuncParam holds the properties of function input or output.
type FuncParam struct {
Index int
Type reflect.Type
}

// LookupInputs returns the index and type of each function's input argument.
// Panics if "fn" is not a type of Func.
func LookupInputs(fn reflect.Type) []FuncParam {
n := fn.NumIn()
params := make([]FuncParam, 0, n)
for i := 0; i < n; i++ {
in := fn.In(i)
params = append(params, FuncParam{
Index: i,
Type: in,
})
}
return params
}

// LookupOutputs returns the index and type of each function's output argument.
// Panics if "fn" is not a type of Func.
func LookupOutputs(fn reflect.Type) []FuncParam {
n := fn.NumOut()
params := make([]FuncParam, 0, n)
for i := 0; i < n; i++ {
out := fn.Out(i)
params = append(params, FuncParam{
Index: i,
Type: out,
})
}
return params
}
19 changes: 19 additions & 0 deletions x/reflex/reflectx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package reflex

import "reflect"

// IndirectType returns the value of a pointer-type "typ".
// If "IndirectType" is a pointer, array, chan, map or slice it returns its Elem,
// otherwise returns the "typ" as it is.
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
}

// IndirectValue returns the element type (e.g. if pointer of *User it will return the User type).
func IndirectValue(val reflect.Value) reflect.Value {
return reflect.Indirect(val)
}
56 changes: 56 additions & 0 deletions x/reflex/struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package reflex

import "reflect"

// LookupFields returns a slice of all fields containg a struct field
// of the given "fieldTag" of the "typ" struct. The fields returned
// are flatted and reclusive over fields with value of struct.
// Panics if "typ" is not a type of Struct.
func LookupFields(typ reflect.Type, fieldTag string) []reflect.StructField {
fields := lookupFields(typ, fieldTag, nil)
return fields[0:len(fields):len(fields)]
}

func lookupFields(typ reflect.Type, fieldTag string, parentIndex []int) []reflect.StructField {
n := typ.NumField()
fields := make([]reflect.StructField, 0, n)
checkTag := fieldTag != ""
for i := 0; i < n; i++ {
field := typ.Field(i)
if field.PkgPath != "" { // skip unexported fields.
continue
}

if checkTag {
if v := field.Tag.Get(fieldTag); v == "" || v == "-" {
// Skip fields that don't contain the 'fieldTag' tag or has '-'.
continue
}
}

fieldType := IndirectType(field.Type)

if fieldType.Kind() == reflect.Struct { // It's a struct inside a struct and it's not time, flat it.
if fieldType != TimeType {
structFields := lookupFields(fieldType, fieldTag, append(parentIndex, i))
if nn := len(structFields); nn > 0 {
fields = append(fields, structFields...)
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 fields
}
30 changes: 30 additions & 0 deletions x/reflex/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package reflex

import (
"encoding/json"
"fmt"
"net"
"reflect"
"time"
)

// Common reflect types for go standard data types.
var (
StringType = reflect.TypeOf("")
BytesType = reflect.TypeOf([]byte{})
IntType = reflect.TypeOf(int(0))
Int16Type = reflect.TypeOf(int16(0))
Int32Type = reflect.TypeOf(int32(0))
Int64Type = reflect.TypeOf(int64(0))
Float32Type = reflect.TypeOf(float32(0))
Float64Type = reflect.TypeOf(float64(0))
TimeType = reflect.TypeOf(time.Time{})
IpTyp = reflect.TypeOf(net.IP{})
JSONNumberTyp = reflect.TypeOf(json.Number(""))
StringerTyp = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
ArrayIntegerTyp = reflect.TypeOf([]int{})
ArrayStringTyp = reflect.TypeOf([]string{})
DoubleArrayIntegerTyp = reflect.TypeOf([][]int{})
DoubleArrayStringTyp = reflect.TypeOf([][]string{})
ErrTyp = reflect.TypeOf((*error)(nil)).Elem()
)
65 changes: 65 additions & 0 deletions x/reflex/zero.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package reflex

import (
"encoding/json"
"net"
)

// Zeroer can be implemented by custom types
// to report whether its current value is zero.
// Standard Time also implements that.
type Zeroer interface {
IsZero() bool
}

// IsZero reports whether "v" is zero value or no.
// The given "v" value can complete the Zeroer interface
// which can be used to customize the behavior for each type of "v".
func IsZero(v interface{}) bool {
switch t := v.(type) {
case Zeroer: // completes the time.Time as well.
return t.IsZero()
case string:
return t == ""
case int:
return t == 0
case int8:
return t == 0
case int16:
return t == 0
case int32:
return t == 0
case int64:
return t == 0
case uint:
return t == 0
case uint8:
return t == 0
case uint16:
return t == 0
case uint32:
return t == 0
case uint64:
return t == 0
case float32:
return t == 0
case float64:
return t == 0
case bool:
return !t
case []int:
return len(t) == 0
case []string:
return len(t) == 0
case [][]int:
return len(t) == 0
case [][]string:
return len(t) == 0
case json.Number:
return t.String() == ""
case net.IP:
return len(t) == 0
default:
return false
}
}

0 comments on commit 410e5ea

Please sign in to comment.