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 }