mirror of
https://github.com/kataras/iris.git
synced 2025-01-24 19:21:03 +01:00
591806795e
Former-commit-id: b72a23ba063be60a9750c8b1b0df024b0c8ed549
221 lines
5.4 KiB
Go
221 lines
5.4 KiB
Go
package field
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
// Field is a controller's field
|
|
// contains all the necessary, internal, information
|
|
// to work with.
|
|
type Field struct {
|
|
Name string // the field's original name
|
|
// but if a tag with `name: "other"`
|
|
// exist then this fill is filled, otherwise it's the same as the Name.
|
|
TagName string
|
|
Index int
|
|
Type reflect.Type
|
|
Value reflect.Value
|
|
|
|
embedded *Field
|
|
}
|
|
|
|
// GetIndex returns all the "dimensions"
|
|
// of the controller struct field's position that this field is referring to,
|
|
// recursively.
|
|
// Usage: elem.FieldByIndex(field.getIndex())
|
|
// for example the {0,1} means that the field is on the second field of the first's
|
|
// field of this struct.
|
|
func (ff Field) GetIndex() []int {
|
|
deepIndex := []int{ff.Index}
|
|
|
|
if emb := ff.embedded; emb != nil {
|
|
deepIndex = append(deepIndex, emb.GetIndex()...)
|
|
}
|
|
|
|
return deepIndex
|
|
}
|
|
|
|
// GetType returns the type of the referring field, recursively.
|
|
func (ff Field) GetType() reflect.Type {
|
|
typ := ff.Type
|
|
if emb := ff.embedded; emb != nil {
|
|
return emb.GetType()
|
|
}
|
|
|
|
return typ
|
|
}
|
|
|
|
// GetFullName returns the full name of that field
|
|
// i.e: UserController.SessionController.Manager,
|
|
// it's useful for debugging only.
|
|
func (ff Field) GetFullName() string {
|
|
name := ff.Name
|
|
|
|
if emb := ff.embedded; emb != nil {
|
|
return name + "." + emb.GetFullName()
|
|
}
|
|
|
|
return name
|
|
}
|
|
|
|
// GetTagName returns the tag name of the referring field
|
|
// recursively.
|
|
func (ff Field) GetTagName() string {
|
|
name := ff.TagName
|
|
|
|
if emb := ff.embedded; emb != nil {
|
|
return emb.GetTagName()
|
|
}
|
|
|
|
return name
|
|
}
|
|
|
|
// checkVal checks if that value
|
|
// is valid to be set-ed to the new controller's instance.
|
|
// Used on binder.
|
|
func checkVal(val reflect.Value) bool {
|
|
return val.IsValid() && (val.Kind() == reflect.Ptr && !val.IsNil()) && val.CanInterface()
|
|
}
|
|
|
|
// GetValue returns a valid value of the referring field, recursively.
|
|
func (ff Field) GetValue() interface{} {
|
|
if ff.embedded != nil {
|
|
return ff.embedded.GetValue()
|
|
}
|
|
|
|
if checkVal(ff.Value) {
|
|
return ff.Value.Interface()
|
|
}
|
|
|
|
return "undefinied value"
|
|
}
|
|
|
|
// SendTo should be used when this field or its embedded
|
|
// has a Value on it.
|
|
// It sets the field's value to the "elem" instance, it's the new controller.
|
|
func (ff Field) SendTo(elem reflect.Value) {
|
|
// note:
|
|
// we don't use the getters here
|
|
// because we do recursively search by our own here
|
|
// to be easier to debug if ever needed.
|
|
if embedded := ff.embedded; embedded != nil {
|
|
if ff.Index >= 0 {
|
|
embedded.SendTo(elem.Field(ff.Index))
|
|
}
|
|
return
|
|
}
|
|
elemField := elem.Field(ff.Index)
|
|
if elemField.Kind() == reflect.Ptr && !elemField.IsNil() {
|
|
return
|
|
}
|
|
|
|
elemField.Set(ff.Value)
|
|
}
|
|
|
|
// lookupTagName checks if the "elemField" struct's field
|
|
// contains a tag `name`, if it contains then it returns its value
|
|
// otherwise returns the field's original Name.
|
|
func lookupTagName(elemField reflect.StructField) string {
|
|
vname := elemField.Name
|
|
|
|
if taggedName, ok := elemField.Tag.Lookup("name"); ok {
|
|
vname = taggedName
|
|
}
|
|
return vname
|
|
}
|
|
|
|
// LookupFields iterates all "elem"'s fields and its fields
|
|
// if structs, recursively.
|
|
// Compares them to the "matcher", if they passed
|
|
// then it executes the "handler" if any,
|
|
// the handler can change the field as it wants to.
|
|
//
|
|
// It finally returns that collection of the valid fields, can be empty.
|
|
func LookupFields(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*Field)) (fields []Field) {
|
|
for i, n := 0, elem.NumField(); i < n; i++ {
|
|
elemField := elem.Field(i)
|
|
if matcher(elemField) {
|
|
field := Field{
|
|
Index: i,
|
|
Name: elemField.Name,
|
|
TagName: lookupTagName(elemField),
|
|
Type: elemField.Type,
|
|
}
|
|
|
|
if handler != nil {
|
|
handler(&field)
|
|
}
|
|
|
|
// we area inside the correct type
|
|
fields = append(fields, field)
|
|
continue
|
|
}
|
|
|
|
f := lookupStructField(elemField.Type, matcher, handler)
|
|
if f != nil {
|
|
fields = append(fields, Field{
|
|
Index: i,
|
|
Name: elemField.Name,
|
|
Type: elemField.Type,
|
|
embedded: f,
|
|
})
|
|
}
|
|
|
|
}
|
|
return
|
|
}
|
|
|
|
// lookupStructField is here to search for embedded field only,
|
|
// is working with the "lookupFields".
|
|
// We could just one one function
|
|
// for both structured (embedded) fields and normal fields
|
|
// but we keep that as it's, a new function like this
|
|
// is easier for debugging, if ever needed.
|
|
func lookupStructField(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*Field)) *Field {
|
|
// fmt.Printf("lookup struct for elem: %s\n", elem.Name())
|
|
|
|
// ignore if that field is not a struct
|
|
if elem.Kind() != reflect.Struct {
|
|
return nil
|
|
}
|
|
|
|
// search by fields.
|
|
for i, n := 0, elem.NumField(); i < n; i++ {
|
|
elemField := elem.Field(i)
|
|
if matcher(elemField) {
|
|
// we area inside the correct type.
|
|
f := &Field{
|
|
Index: i,
|
|
Name: elemField.Name,
|
|
TagName: lookupTagName(elemField),
|
|
Type: elemField.Type,
|
|
}
|
|
|
|
if handler != nil {
|
|
handler(f)
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
// if field is struct and the value is struct
|
|
// then try inside its fields for a compatible
|
|
// field type.
|
|
if elemField.Type.Kind() == reflect.Struct { // 3-level
|
|
elemFieldEmb := elem.Field(i)
|
|
f := lookupStructField(elemFieldEmb.Type, matcher, handler)
|
|
if f != nil {
|
|
fp := &Field{
|
|
Index: i,
|
|
Name: elemFieldEmb.Name,
|
|
TagName: lookupTagName(elemFieldEmb),
|
|
Type: elemFieldEmb.Type,
|
|
embedded: f,
|
|
}
|
|
return fp
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|