goki.dev/laser@v0.1.34/structs.go (about) 1 // Copyright (c) 2023, 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 laser 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "log" 11 "reflect" 12 "strings" 13 ) 14 15 // FlatFieldsTypeFunc calls a function on all the primary fields of a given 16 // struct type, including those on anonymous embedded structs that this struct 17 // has, passing the current (embedded) type and StructField -- effectively 18 // flattens the reflect field list -- if fun returns false then iteration 19 // stops -- overall rval is false if iteration was stopped or there was an 20 // error (logged), true otherwise 21 func FlatFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool { 22 return FlatFieldsTypeFuncIf(typ, nil, fun) 23 } 24 25 // FlatFieldsTypeFunc calls a function on all the primary fields of a given 26 // struct type, including those on anonymous embedded structs that this struct 27 // has, passing the current (embedded) type and StructField -- effectively 28 // flattens the reflect field list -- if fun returns false then iteration 29 // stops -- overall rval is false if iteration was stopped or there was an 30 // error (logged), true otherwise. If the given ifFun is non-nil, it is called 31 // on every embedded struct field to determine whether the fields of that embedded 32 // field should be handled (a return value of true indicates to continue down and 33 // a value of false indicates to not). 34 func FlatFieldsTypeFuncIf(typ reflect.Type, ifFun, fun func(typ reflect.Type, field reflect.StructField) bool) bool { 35 typ = NonPtrType(typ) 36 if typ.Kind() != reflect.Struct { 37 log.Printf("laser.FlatFieldsTypeFunc: Must call on a struct type, not: %v\n", typ) 38 return false 39 } 40 rval := true 41 for i := 0; i < typ.NumField(); i++ { 42 f := typ.Field(i) 43 if f.Type.Kind() == reflect.Struct && f.Anonymous { 44 if ifFun != nil { 45 if !ifFun(typ, f) { 46 continue 47 } 48 } 49 rval = FlatFieldsTypeFunc(f.Type, fun) // no err here 50 if !rval { 51 break 52 } 53 } else { 54 rval = fun(typ, f) 55 if !rval { 56 break 57 } 58 } 59 } 60 return rval 61 } 62 63 // AllFieldsTypeFunc calls a function on all the fields of a given struct type, 64 // including those on *any* fields of struct fields that this struct has -- if fun 65 // returns false then iteration stops -- overall rval is false if iteration 66 // was stopped or there was an error (logged), true otherwise. 67 func AllFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool { 68 typ = NonPtrType(typ) 69 if typ.Kind() != reflect.Struct { 70 log.Printf("laser.AllFieldsTypeFunc: Must call on a struct type, not: %v\n", typ) 71 return false 72 } 73 rval := true 74 for i := 0; i < typ.NumField(); i++ { 75 f := typ.Field(i) 76 if f.Type.Kind() == reflect.Struct { 77 rval = AllFieldsTypeFunc(f.Type, fun) // no err here 78 if !rval { 79 break 80 } 81 } else { 82 rval = fun(typ, f) 83 if !rval { 84 break 85 } 86 } 87 } 88 return rval 89 } 90 91 // FlatFieldsValueFunc calls a function on all the primary fields of a 92 // given struct value (must pass a pointer to the struct) including those on 93 // anonymous embedded structs that this struct has, passing the current 94 // (embedded) type and StructField, which effectively flattens the reflect field list. 95 func FlatFieldsValueFunc(stru any, fun func(stru any, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool) bool { 96 return FlatFieldsValueFuncIf(stru, nil, fun) 97 } 98 99 // FlatFieldsValueFunc calls a function on all the primary fields of a 100 // given struct value (must pass a pointer to the struct) including those on 101 // anonymous embedded structs that this struct has, passing the current 102 // (embedded) type and StructField, which effectively flattens the reflect field 103 // list. If the given ifFun is non-nil, it is called on every embedded struct field to 104 // determine whether the fields of that embedded field should be handled (a return value 105 // of true indicates to continue down and a value of false indicates to not). 106 func FlatFieldsValueFuncIf(stru any, ifFun, fun func(stru any, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool) bool { 107 vv := reflect.ValueOf(stru) 108 if stru == nil || vv.Kind() != reflect.Ptr { 109 log.Printf("laser.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru) 110 return false 111 } 112 v := NonPtrValue(vv) 113 if !v.IsValid() { 114 return true 115 } 116 typ := v.Type() 117 if typ.Kind() != reflect.Struct { 118 // log.Printf("laser.FlatFieldsValueFunc: non-pointer type is not a struct: %v\n", typ.String()) 119 return false 120 } 121 rval := true 122 for i := 0; i < typ.NumField(); i++ { 123 f := typ.Field(i) 124 vf := v.Field(i) 125 if !vf.CanInterface() { 126 continue 127 } 128 vfi := vf.Interface() 129 if vfi == stru { 130 continue 131 } 132 if f.Type.Kind() == reflect.Struct && f.Anonymous { 133 if ifFun != nil { 134 if !ifFun(vfi, typ, f, vf) { 135 continue 136 } 137 } 138 // key to take addr here so next level is addressable 139 rval = FlatFieldsValueFunc(PtrValue(vf).Interface(), fun) 140 if !rval { 141 break 142 } 143 } else { 144 rval = fun(vfi, typ, f, vf) 145 if !rval { 146 break 147 } 148 } 149 } 150 return rval 151 } 152 153 // FlatFields returns a slice list of all the StructField type information for 154 // fields of given type and any embedded types -- returns nil on error 155 // (logged) 156 func FlatFields(typ reflect.Type) []reflect.StructField { 157 ff := make([]reflect.StructField, 0) 158 falseErr := FlatFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool { 159 ff = append(ff, field) 160 return true 161 }) 162 if falseErr == false { 163 return nil 164 } 165 return ff 166 } 167 168 // AllFields returns a slice list of all the StructField type information for 169 // all elemental fields of given type and all embedded types -- returns nil on 170 // error (logged) 171 func AllFields(typ reflect.Type) []reflect.StructField { 172 ff := make([]reflect.StructField, 0) 173 falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool { 174 ff = append(ff, field) 175 return true 176 }) 177 if falseErr == false { 178 return nil 179 } 180 return ff 181 } 182 183 // AllFieldsN returns number of elemental fields in given type 184 func AllFieldsN(typ reflect.Type) int { 185 n := 0 186 falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool { 187 n++ 188 return true 189 }) 190 if falseErr == false { 191 return 0 192 } 193 return n 194 } 195 196 // FlatFieldsVals returns a slice list of all the field reflect.Value's for 197 // fields of given struct (must pass a pointer to the struct) and any of its 198 // embedded structs -- returns nil on error (logged) 199 func FlatFieldVals(stru any) []reflect.Value { 200 ff := make([]reflect.Value, 0) 201 falseErr := FlatFieldsValueFunc(stru, func(stru any, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool { 202 ff = append(ff, fieldVal) 203 return true 204 }) 205 if falseErr == false { 206 return nil 207 } 208 return ff 209 } 210 211 // FlatFieldInterfaces returns a slice list of all the field interface{} 212 // values *as pointers to the field value* (i.e., calling Addr() on the Field 213 // Value) for fields of given struct (must pass a pointer to the struct) and 214 // any of its embedded structs -- returns nil on error (logged) 215 func FlatFieldInterfaces(stru any) []any { 216 ff := make([]any, 0) 217 falseErr := FlatFieldsValueFunc(stru, func(stru any, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool { 218 ff = append(ff, PtrValue(fieldVal).Interface()) 219 return true 220 }) 221 if falseErr == false { 222 return nil 223 } 224 return ff 225 } 226 227 // FlatFieldByName returns field in type or embedded structs within type, by 228 // name -- native function already does flat version, so this is just for 229 // reference and consistency 230 func FlatFieldByName(typ reflect.Type, nm string) (reflect.StructField, bool) { 231 return typ.FieldByName(nm) 232 } 233 234 // FieldByPath returns field in type or embedded structs within type, by a 235 // dot-separated path -- finds field by name for each level of the path, and 236 // recurses. 237 func FieldByPath(typ reflect.Type, path string) (reflect.StructField, bool) { 238 pels := strings.Split(path, ".") 239 ctyp := typ 240 plen := len(pels) 241 for i, pe := range pels { 242 fld, ok := ctyp.FieldByName(pe) 243 if !ok { 244 log.Printf("laser.FieldByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, ctyp.String(), path, typ.String()) 245 return fld, false 246 } 247 if i == plen-1 { 248 return fld, true 249 } 250 ctyp = fld.Type 251 } 252 return reflect.StructField{}, false 253 } 254 255 // FieldValueByPath returns field interface in type or embedded structs within 256 // type, by a dot-separated path -- finds field by name for each level of the 257 // path, and recurses. 258 func FieldValueByPath(stru any, path string) (reflect.Value, bool) { 259 pels := strings.Split(path, ".") 260 sval := reflect.ValueOf(stru) 261 cval := sval 262 typ := sval.Type() 263 ctyp := typ 264 plen := len(pels) 265 for i, pe := range pels { 266 _, ok := ctyp.FieldByName(pe) 267 if !ok { 268 log.Printf("laser.FieldValueByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, cval.Type().String(), path, typ.String()) 269 return cval, false 270 } 271 fval := cval.FieldByName(pe) 272 if i == plen-1 { 273 return fval, true 274 } 275 cval = fval 276 ctyp = fval.Type() 277 } 278 return reflect.Value{}, false 279 } 280 281 // FlatFieldTag returns given tag value in field in type or embedded structs 282 // within type, by name -- empty string if not set or field not found 283 func FlatFieldTag(typ reflect.Type, nm, tag string) string { 284 fld, ok := typ.FieldByName(nm) 285 if !ok { 286 return "" 287 } 288 return fld.Tag.Get(tag) 289 } 290 291 // FlatFieldValueByName finds field in object and embedded objects, by name, 292 // returning reflect.Value of field -- native version of Value function 293 // already does flat find, so this just provides a convenient wrapper 294 func FlatFieldValueByName(stru any, nm string) reflect.Value { 295 vv := reflect.ValueOf(stru) 296 if stru == nil || vv.Kind() != reflect.Ptr { 297 log.Printf("laser.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru) 298 return reflect.Value{} 299 } 300 v := NonPtrValue(vv) 301 return v.FieldByName(nm) 302 } 303 304 // FlatFieldInterfaceByName finds field in object and embedded objects, by 305 // name, returning interface{} to pointer of field, or nil if not found 306 func FlatFieldInterfaceByName(stru any, nm string) any { 307 ff := FlatFieldValueByName(stru, nm) 308 if !ff.IsValid() { 309 return nil 310 } 311 return PtrValue(ff).Interface() 312 } 313 314 // TypeEmbeds checks if given type embeds another type, at any level of 315 // recursive embedding (including being the type itself) 316 func TypeEmbeds(typ, embed reflect.Type) bool { 317 typ = NonPtrType(typ) 318 embed = NonPtrType(embed) 319 if typ == embed { 320 return true 321 } 322 for i := 0; i < typ.NumField(); i++ { 323 f := typ.Field(i) 324 if f.Type.Kind() == reflect.Struct && f.Anonymous { 325 // fmt.Printf("typ %v anon struct %v\n", typ.Name(), f.Name) 326 if f.Type == embed { 327 return true 328 } 329 return TypeEmbeds(f.Type, embed) 330 } 331 } 332 return false 333 } 334 335 // Embed returns the embedded struct of given type within given struct 336 func Embed(stru any, embed reflect.Type) any { 337 if AnyIsNil(stru) { 338 return nil 339 } 340 v := NonPtrValue(reflect.ValueOf(stru)) 341 typ := v.Type() 342 if typ == embed { 343 return PtrValue(v).Interface() 344 } 345 for i := 0; i < typ.NumField(); i++ { 346 f := typ.Field(i) 347 if f.Type.Kind() == reflect.Struct && f.Anonymous { // anon only avail on StructField fm typ 348 vf := v.Field(i) 349 vfpi := PtrValue(vf).Interface() 350 if f.Type == embed { 351 return vfpi 352 } 353 rv := Embed(vfpi, embed) 354 if rv != nil { 355 return rv 356 } 357 } 358 } 359 return nil 360 } 361 362 // EmbedImplements checks if given type implements given interface, or 363 // it embeds a type that does so -- must pass a type constructed like this: 364 // reflect.TypeOf((*gi.Node2D)(nil)).Elem() or just reflect.TypeOf(laser.BaseIface()) 365 func EmbedImplements(typ, iface reflect.Type) bool { 366 if iface.Kind() != reflect.Interface { 367 log.Printf("laser.EmbedImplements -- type is not an interface: %v\n", iface) 368 return false 369 } 370 if typ.Implements(iface) { 371 return true 372 } 373 if reflect.PtrTo(typ).Implements(iface) { // typically need the pointer type to impl 374 return true 375 } 376 typ = NonPtrType(typ) 377 if typ.Implements(iface) { // try it all possible ways.. 378 return true 379 } 380 if typ.Kind() != reflect.Struct { 381 return false 382 } 383 for i := 0; i < typ.NumField(); i++ { 384 f := typ.Field(i) 385 if f.Type.Kind() == reflect.Struct && f.Anonymous { 386 rv := EmbedImplements(f.Type, iface) 387 if rv { 388 return true 389 } 390 } 391 } 392 return false 393 } 394 395 // SetFromDefaultTags sets values of fields in given struct based on 396 // `def:` default value field tags. 397 func SetFromDefaultTags(obj any) error { 398 if AnyIsNil(obj) { 399 return nil 400 } 401 ov := reflect.ValueOf(obj) 402 if ov.Kind() == reflect.Pointer && ov.IsNil() { 403 return nil 404 } 405 val := NonPtrValue(ov) 406 typ := val.Type() 407 for i := 0; i < typ.NumField(); i++ { 408 f := typ.Field(i) 409 fv := val.Field(i) 410 def, ok := f.Tag.Lookup("def") 411 if NonPtrType(f.Type).Kind() == reflect.Struct && (!ok || def == "") { 412 SetFromDefaultTags(PtrValue(fv).Interface()) 413 continue 414 } 415 if !ok || def == "" { 416 continue 417 } 418 if def[0] == '{' || def[0] == '[' { // complex type 419 def = strings.ReplaceAll(def, `'`, `"`) // allow single quote to work as double quote for JSON format 420 } else { 421 def = strings.Split(def, ",")[0] 422 if strings.Contains(def, ":") { // don't do ranges 423 continue 424 } 425 } 426 err := SetRobust(PtrValue(fv).Interface(), def) // overkill but whatever 427 if !ok { 428 return fmt.Errorf("laser.SetFromDefaultTags: error setting field %q in object of type %q from val %q: %w", f.Name, typ.Name(), def, err) 429 } 430 } 431 return nil 432 } 433 434 // StructTags returns a map[string]string of the tag string from a reflect.StructTag value 435 // e.g., from StructField.Tag 436 func StructTags(tags reflect.StructTag) map[string]string { 437 if len(tags) == 0 { 438 return nil 439 } 440 flds := strings.Fields(string(tags)) 441 smap := make(map[string]string, len(flds)) 442 for _, fld := range flds { 443 cli := strings.Index(fld, ":") 444 if cli < 0 || len(fld) < cli+3 { 445 continue 446 } 447 vl := strings.TrimSuffix(fld[cli+2:], `"`) 448 smap[fld[:cli]] = vl 449 } 450 return smap 451 } 452 453 // StringJSON returns a JSON representation of item, as a string 454 // e.g., for printing / debugging etc. 455 func StringJSON(it any) string { 456 b, _ := json.MarshalIndent(it, "", " ") 457 return string(b) 458 } 459 460 // SetField sets given field name on given struct object to given value, 461 // using very robust conversion routines to e.g., convert from strings to numbers, and 462 // vice-versa, automatically. Returns error if not successfully set. 463 // wrapped in UpdateStart / End and sets the ValUpdated flag. 464 func SetField(obj any, field string, val any) error { 465 fv := FlatFieldValueByName(obj, field) 466 if !fv.IsValid() { 467 return fmt.Errorf("laser.SetField: could not find field %q", field) 468 } 469 err := SetRobust(PtrValue(fv).Interface(), val) 470 if err != nil { 471 return fmt.Errorf("laser.SetField: SetRobust failed to set field %q to value: %v: %w", field, val, err) 472 } 473 return nil 474 }