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  }