github.com/goki/ki@v1.1.11/kit/embeds.go (about)

     1  // Copyright (c) 2018, The GoKi Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package kit
     6  
     7  import (
     8  	"log"
     9  	"reflect"
    10  	"strings"
    11  )
    12  
    13  // This file contains helpful functions for dealing with embedded structs, in
    14  // the reflect system
    15  
    16  // FlatFieldsTypeFunc calls a function on all the primary fields of a given
    17  // struct type, including those on anonymous embedded structs that this struct
    18  // has, passing the current (embedded) type and StructField -- effectively
    19  // flattens the reflect field list -- if fun returns false then iteration
    20  // stops -- overall rval is false if iteration was stopped or there was an
    21  // error (logged), true otherwise
    22  func FlatFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool {
    23  	typ = NonPtrType(typ)
    24  	if typ.Kind() != reflect.Struct {
    25  		log.Printf("kit.FlatFieldsTypeFunc: Must call on a struct type, not: %v\n", typ)
    26  		return false
    27  	}
    28  	rval := true
    29  	for i := 0; i < typ.NumField(); i++ {
    30  		f := typ.Field(i)
    31  		if f.Type.Kind() == reflect.Struct && f.Anonymous {
    32  			rval = FlatFieldsTypeFunc(f.Type, fun) // no err here
    33  			if !rval {
    34  				break
    35  			}
    36  		} else {
    37  			rval = fun(typ, f)
    38  			if !rval {
    39  				break
    40  			}
    41  		}
    42  	}
    43  	return rval
    44  }
    45  
    46  // AllFieldsTypeFunc calls a function on all the fields of a given struct type,
    47  // including those on *any* embedded structs that this struct has -- if fun
    48  // returns false then iteration stops -- overall rval is false if iteration
    49  // was stopped or there was an error (logged), true otherwise.
    50  func AllFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool {
    51  	typ = NonPtrType(typ)
    52  	if typ.Kind() != reflect.Struct {
    53  		log.Printf("kit.AllFieldsTypeFunc: Must call on a struct type, not: %v\n", typ)
    54  		return false
    55  	}
    56  	rval := true
    57  	for i := 0; i < typ.NumField(); i++ {
    58  		f := typ.Field(i)
    59  		if f.Type.Kind() == reflect.Struct {
    60  			rval = AllFieldsTypeFunc(f.Type, fun) // no err here
    61  			if !rval {
    62  				break
    63  			}
    64  		} else {
    65  			rval = fun(typ, f)
    66  			if !rval {
    67  				break
    68  			}
    69  		}
    70  	}
    71  	return rval
    72  }
    73  
    74  // FlatFieldsValueFunc calls a function on all the primary fields of a
    75  // given struct value (must pass a pointer to the struct) including those on
    76  // anonymous embedded structs that this struct has, passing the current
    77  // (embedded) type and StructField -- effectively flattens the reflect field
    78  // list
    79  func FlatFieldsValueFunc(stru interface{}, fun func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool) bool {
    80  	vv := reflect.ValueOf(stru)
    81  	if stru == nil || vv.Kind() != reflect.Ptr {
    82  		log.Printf("kit.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru)
    83  		return false
    84  	}
    85  	v := NonPtrValue(vv)
    86  	if !v.IsValid() {
    87  		return true
    88  	}
    89  	typ := v.Type()
    90  	if typ.Kind() != reflect.Struct {
    91  		// log.Printf("kit.FlatFieldsValueFunc: non-pointer type is not a struct: %v\n", typ.String())
    92  		return false
    93  	}
    94  	rval := true
    95  	for i := 0; i < typ.NumField(); i++ {
    96  		f := typ.Field(i)
    97  		vf := v.Field(i)
    98  		if !vf.CanInterface() {
    99  			continue
   100  		}
   101  		vfi := vf.Interface()
   102  		if vfi == stru {
   103  			continue
   104  		}
   105  		if f.Type.Kind() == reflect.Struct && f.Anonymous {
   106  			// key to take addr here so next level is addressable
   107  			rval = FlatFieldsValueFunc(PtrValue(vf).Interface(), fun)
   108  			if !rval {
   109  				break
   110  			}
   111  		} else {
   112  			rval = fun(vfi, typ, f, vf)
   113  			if !rval {
   114  				break
   115  			}
   116  		}
   117  	}
   118  	return rval
   119  }
   120  
   121  // FlatFields returns a slice list of all the StructField type information for
   122  // fields of given type and any embedded types -- returns nil on error
   123  // (logged)
   124  func FlatFields(typ reflect.Type) []reflect.StructField {
   125  	ff := make([]reflect.StructField, 0)
   126  	falseErr := FlatFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
   127  		ff = append(ff, field)
   128  		return true
   129  	})
   130  	if falseErr == false {
   131  		return nil
   132  	}
   133  	return ff
   134  }
   135  
   136  // AllFields returns a slice list of all the StructField type information for
   137  // all elemental fields of given type and all embedded types -- returns nil on
   138  // error (logged)
   139  func AllFields(typ reflect.Type) []reflect.StructField {
   140  	ff := make([]reflect.StructField, 0)
   141  	falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
   142  		ff = append(ff, field)
   143  		return true
   144  	})
   145  	if falseErr == false {
   146  		return nil
   147  	}
   148  	return ff
   149  }
   150  
   151  // AllFieldsN returns number of elemental fields in given type
   152  func AllFieldsN(typ reflect.Type) int {
   153  	n := 0
   154  	falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
   155  		n++
   156  		return true
   157  	})
   158  	if falseErr == false {
   159  		return 0
   160  	}
   161  	return n
   162  }
   163  
   164  // FlatFieldsVals returns a slice list of all the field reflect.Value's for
   165  // fields of given struct (must pass a pointer to the struct) and any of its
   166  // embedded structs -- returns nil on error (logged)
   167  func FlatFieldVals(stru interface{}) []reflect.Value {
   168  	ff := make([]reflect.Value, 0)
   169  	falseErr := FlatFieldsValueFunc(stru, func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool {
   170  		ff = append(ff, fieldVal)
   171  		return true
   172  	})
   173  	if falseErr == false {
   174  		return nil
   175  	}
   176  	return ff
   177  }
   178  
   179  // FlatFieldInterfaces returns a slice list of all the field interface{}
   180  // values *as pointers to the field value* (i.e., calling Addr() on the Field
   181  // Value) for fields of given struct (must pass a pointer to the struct) and
   182  // any of its embedded structs -- returns nil on error (logged)
   183  func FlatFieldInterfaces(stru interface{}) []interface{} {
   184  	ff := make([]interface{}, 0)
   185  	falseErr := FlatFieldsValueFunc(stru, func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool {
   186  		ff = append(ff, PtrValue(fieldVal).Interface())
   187  		return true
   188  	})
   189  	if falseErr == false {
   190  		return nil
   191  	}
   192  	return ff
   193  }
   194  
   195  // FlatFieldByName returns field in type or embedded structs within type, by
   196  // name -- native function already does flat version, so this is just for
   197  // reference and consistency
   198  func FlatFieldByName(typ reflect.Type, nm string) (reflect.StructField, bool) {
   199  	return typ.FieldByName(nm)
   200  }
   201  
   202  // FieldByPath returns field in type or embedded structs within type, by a
   203  // dot-separated path -- finds field by name for each level of the path, and
   204  // recurses.
   205  func FieldByPath(typ reflect.Type, path string) (reflect.StructField, bool) {
   206  	pels := strings.Split(path, ".")
   207  	ctyp := typ
   208  	plen := len(pels)
   209  	for i, pe := range pels {
   210  		fld, ok := ctyp.FieldByName(pe)
   211  		if !ok {
   212  			log.Printf("kit.FieldByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, ctyp.String(), path, typ.String())
   213  			return fld, false
   214  		}
   215  		if i == plen-1 {
   216  			return fld, true
   217  		}
   218  		ctyp = fld.Type
   219  	}
   220  	return reflect.StructField{}, false
   221  }
   222  
   223  // FieldValueByPath returns field interface in type or embedded structs within
   224  // type, by a dot-separated path -- finds field by name for each level of the
   225  // path, and recurses.
   226  func FieldValueByPath(stru interface{}, path string) (reflect.Value, bool) {
   227  	pels := strings.Split(path, ".")
   228  	sval := reflect.ValueOf(stru)
   229  	cval := sval
   230  	typ := sval.Type()
   231  	ctyp := typ
   232  	plen := len(pels)
   233  	for i, pe := range pels {
   234  		_, ok := ctyp.FieldByName(pe)
   235  		if !ok {
   236  			log.Printf("kit.FieldValueByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, cval.Type().String(), path, typ.String())
   237  			return cval, false
   238  		}
   239  		fval := cval.FieldByName(pe)
   240  		if i == plen-1 {
   241  			return fval, true
   242  		}
   243  		cval = fval
   244  		ctyp = fval.Type()
   245  	}
   246  	return reflect.Value{}, false
   247  }
   248  
   249  // FlatFieldTag returns given tag value in field in type or embedded structs
   250  // within type, by name -- empty string if not set or field not found
   251  func FlatFieldTag(typ reflect.Type, nm, tag string) string {
   252  	fld, ok := typ.FieldByName(nm)
   253  	if !ok {
   254  		return ""
   255  	}
   256  	return fld.Tag.Get(tag)
   257  }
   258  
   259  // FlatFieldValueByName finds field in object and embedded objects, by name,
   260  // returning reflect.Value of field -- native version of Value function
   261  // already does flat find, so this just provides a convenient wrapper
   262  func FlatFieldValueByName(stru interface{}, nm string) reflect.Value {
   263  	vv := reflect.ValueOf(stru)
   264  	if stru == nil || vv.Kind() != reflect.Ptr {
   265  		log.Printf("kit.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru)
   266  		return reflect.Value{}
   267  	}
   268  	v := NonPtrValue(vv)
   269  	return v.FieldByName(nm)
   270  }
   271  
   272  // FlatFieldInterfaceByName finds field in object and embedded objects, by
   273  // name, returning interface{} to pointer of field, or nil if not found
   274  func FlatFieldInterfaceByName(stru interface{}, nm string) interface{} {
   275  	ff := FlatFieldValueByName(stru, nm)
   276  	if !ff.IsValid() {
   277  		return nil
   278  	}
   279  	return PtrValue(ff).Interface()
   280  }
   281  
   282  // TypeEmbeds checks if given type embeds another type, at any level of
   283  // recursive embedding (including being the type itself)
   284  func TypeEmbeds(typ, embed reflect.Type) bool {
   285  	typ = NonPtrType(typ)
   286  	embed = NonPtrType(embed)
   287  	if typ == embed {
   288  		return true
   289  	}
   290  	for i := 0; i < typ.NumField(); i++ {
   291  		f := typ.Field(i)
   292  		if f.Type.Kind() == reflect.Struct && f.Anonymous {
   293  			// fmt.Printf("typ %v anon struct %v\n", typ.Name(), f.Name)
   294  			if f.Type == embed {
   295  				return true
   296  			}
   297  			return TypeEmbeds(f.Type, embed)
   298  		}
   299  	}
   300  	return false
   301  }
   302  
   303  // Embed returns the embedded struct of given type within given struct
   304  func Embed(stru interface{}, embed reflect.Type) interface{} {
   305  	if IfaceIsNil(stru) {
   306  		return nil
   307  	}
   308  	v := NonPtrValue(reflect.ValueOf(stru))
   309  	typ := v.Type()
   310  	if typ == embed {
   311  		return PtrValue(v).Interface()
   312  	}
   313  	for i := 0; i < typ.NumField(); i++ {
   314  		f := typ.Field(i)
   315  		if f.Type.Kind() == reflect.Struct && f.Anonymous { // anon only avail on StructField fm typ
   316  			vf := v.Field(i)
   317  			vfpi := PtrValue(vf).Interface()
   318  			if f.Type == embed {
   319  				return vfpi
   320  			}
   321  			rv := Embed(vfpi, embed)
   322  			if rv != nil {
   323  				return rv
   324  			}
   325  		}
   326  	}
   327  	return nil
   328  }
   329  
   330  // EmbedImplements checks if given type implements given interface, or
   331  // it embeds a type that does so -- must pass a type constructed like this:
   332  // reflect.TypeOf((*gi.Node2D)(nil)).Elem() or just reflect.TypeOf(ki.BaseIface())
   333  func EmbedImplements(typ, iface reflect.Type) bool {
   334  	if iface.Kind() != reflect.Interface {
   335  		log.Printf("kit.TypeRegistry EmbedImplements -- type is not an interface: %v\n", iface)
   336  		return false
   337  	}
   338  	if typ.Implements(iface) {
   339  		return true
   340  	}
   341  	if reflect.PtrTo(typ).Implements(iface) { // typically need the pointer type to impl
   342  		return true
   343  	}
   344  	typ = NonPtrType(typ)
   345  	if typ.Implements(iface) { // try it all possible ways..
   346  		return true
   347  	}
   348  	if typ.Kind() != reflect.Struct {
   349  		return false
   350  	}
   351  	for i := 0; i < typ.NumField(); i++ {
   352  		f := typ.Field(i)
   353  		if f.Type.Kind() == reflect.Struct && f.Anonymous {
   354  			rv := EmbedImplements(f.Type, iface)
   355  			if rv {
   356  				return true
   357  			}
   358  		}
   359  	}
   360  	return false
   361  }