github.com/goki/ki@v1.1.11/kit/enums.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  	"bytes"
     9  	"fmt"
    10  	"log"
    11  	"reflect"
    12  	"strings"
    13  
    14  	"github.com/goki/ki/bitflag"
    15  )
    16  
    17  // design notes: for methods that return string, not passing error b/c you can
    18  // easily check for null string, and registering errors in log for setter
    19  // methods, returning error and also logging so it is safe to ignore err if
    20  // you don't care
    21  
    22  // Bit flags are setup just using the ordinal count iota, and the only diff is
    23  // the methods which do 1 << flag when operating on them
    24  // see bitflag package
    25  
    26  // EnumRegistry is a map from an enum-style const int type name to a
    27  // corresponding reflect.Type and conversion methods generated by (modified)
    28  // stringer that convert to / from strings.
    29  //
    30  // Each such type must be explicitly registered by calling AddEnum
    31  // in an expression that also initializes a new global variable
    32  // that is then useful whenever you need to specify that type:
    33  //
    34  //     var KiT_MyEnum = kit.Enums.AddEnum(MyEnumN, bitFlag true/false,
    35  //        TypeNameProps (or nil))
    36  //
    37  // where MyEnum is the name of the type, MyEnumN is the enum value
    38  // representing the number of defined enums (always good practice to define
    39  // this value, for ease of extension by others), and TypeNameProps is nil or a
    40  // map[string]interface{} of properties, OR:
    41  //
    42  //     var KiT_MyEnum = kit.Enums.AddEnumAltLower(MyEnumN, bitFlag true/false,
    43  //        TypeNameProps, "Prefix")
    44  //
    45  // which automatically registers alternative names as lower-case versions of
    46  // const names with given prefix removed -- often what is used in e.g., json
    47  // or xml kinds of formats.
    48  //
    49  // The resulting type name is registered using a *short* package-qualified
    50  // version of the type name, with just the last directory name . Type.
    51  // This is the usual name used in programming Go.  All properties are
    52  // registered using this same short name.
    53  //
    54  // special properties:
    55  //
    56  // * "N": max value of enum defined -- number of enum entries (assuming
    57  // ordinal, which is all that is currently supported here)
    58  //
    59  // * "BitFlag": true -- each value represents a bit in a set of bit flags, so
    60  // the string rep of a value contains an or-list of names for each bit set,
    61  // separated by "|".  Use the bitflag package to set and clear bits while
    62  // keeping the definition of the flags as a standard ordinal integer value --
    63  // much more flexible than pre-compiling the bitmasks.  Usually should be an
    64  // int64 type.
    65  //
    66  // * "AltStrings": map[int64]string -- provides an alternative string mapping for
    67  // the enum values
    68  //
    69  // * "ParType": reflect.Type -- parent type that this extends.
    70  //
    71  // Also recommend defining JSON I/O functions for each registered enum -- much
    72  // safer to save enums as strings than using their raw numerical values, which
    73  // can change over time:
    74  //
    75  //     func (ev TestFlags) MarshalJSON() ([]byte, error) { return kit.EnumMarshalJSON(ev) }
    76  //     func (ev *TestFlags) UnmarshalJSON() ([]byte, error) { return kit.EnumUnmarshalJSON(ev) }
    77  //
    78  // And any value that will be used as a key in a map must define Text versions (which don't use quotes)
    79  //
    80  //     func (ev TestFlags) MarshalText() ([]byte, error) { return kit.EnumMarshalText(ev) }
    81  //     func (ev *TestFlags) UnmarshalText() ([]byte, error) { return kit.EnumUnmarshalText(ev) }
    82  //
    83  type EnumRegistry struct {
    84  	// Enums is a map from the *short* package-qualified name to reflect.Type
    85  	Enums map[string]reflect.Type
    86  
    87  	// Props contains properties that can be associated with each enum type
    88  	// e.g., "BitFlag": true, "AltStrings" : map[int64]string, or other custom settings.
    89  	// The key here is the short package-qualified name
    90  	Props map[string]map[string]interface{}
    91  
    92  	// Vals contains cached EnumValue representations of the enum values.
    93  	// Used by Values method.
    94  	Vals map[string][]EnumValue
    95  }
    96  
    97  // Enums is master registry of enum types -- can also create your own package-specific ones
    98  var Enums EnumRegistry
    99  
   100  const (
   101  	// BitFlag is used for AddEnum to indicate that this is a bit flag enum
   102  	BitFlag = true
   103  
   104  	// NotBitFlag is used for AddEnum to indicate that this is NOT a bit flag enum
   105  	NotBitFlag = false
   106  )
   107  
   108  // AddEnum adds a given type to the registry -- requires the N value to set N
   109  // from and grab type info from -- if bitFlag then sets BitFlag property, and
   110  // each value represents a bit in a set of bit flags, so the string rep of a
   111  // value contains an or-list of names for each bit set, separated by | -- can
   112  // also add additional properties -- they are copied so can be re-used across enums
   113  func (tr *EnumRegistry) AddEnum(en interface{}, bitFlag bool, props map[string]interface{}) reflect.Type {
   114  	if tr.Enums == nil {
   115  		tr.Enums = make(map[string]reflect.Type)
   116  		tr.Props = make(map[string]map[string]interface{})
   117  		tr.Vals = make(map[string][]EnumValue)
   118  	}
   119  
   120  	// get the pointer-to version and elem so it is a settable type!
   121  	typ := PtrType(reflect.TypeOf(en)).Elem()
   122  	n := EnumIfaceToInt64(en)
   123  	snm := ShortTypeName(typ)
   124  	tr.Enums[snm] = typ
   125  	if props != nil {
   126  		// make a copy of props for enums -- often shared
   127  		nwprops := make(map[string]interface{}, len(props))
   128  		for key, val := range props {
   129  			nwprops[key] = val
   130  		}
   131  		tr.Props[snm] = nwprops
   132  	}
   133  	tp := tr.Properties(snm)
   134  	tp["N"] = n
   135  	if bitFlag {
   136  		tp := tr.Properties(snm)
   137  		tp["BitFlag"] = true
   138  		EnumBitDepthCheck(typ, n)
   139  	}
   140  	// fmt.Printf("added enum: %v with n: %v\n", tn, n)
   141  	return typ
   142  }
   143  
   144  // EnumBitDepthCheck checks if given type can handle given number of bit flags
   145  func EnumBitDepthCheck(typ reflect.Type, n int64) error {
   146  	cp := typ.Size() * 8
   147  	if n > int64(cp) {
   148  		err := fmt.Errorf("kit.EnumBitDepthCheck ERROR: enum: %v is a bitflag of kind: %v with capacity of: %d bits, but more flags were defined: %d", ShortTypeName(typ), typ.Kind(), cp, n)
   149  		log.Println(err)
   150  		return err
   151  	}
   152  	return nil
   153  }
   154  
   155  // AddEnumAltLower adds a given type to the registry -- requires the N value
   156  // to set N from and grab type info from -- automatically initializes
   157  // AltStrings alternative string map based on the name with given prefix
   158  // removed (e.g., a type name-based prefix) and lower-cased -- also requires
   159  // the number of enums -- assumes starts at 0
   160  func (tr *EnumRegistry) AddEnumAltLower(en interface{}, bitFlag bool, props map[string]interface{}, prefix string) reflect.Type {
   161  	typ := tr.AddEnum(en, bitFlag, props)
   162  	n := EnumIfaceToInt64(en)
   163  	snm := ShortTypeName(typ)
   164  	alts := make(map[int64]string)
   165  	tp := tr.Properties(snm)
   166  	for i := int64(0); i < n; i++ {
   167  		str := EnumInt64ToString(i, typ)
   168  		str = strings.ToLower(strings.TrimPrefix(str, prefix))
   169  		alts[i] = str
   170  	}
   171  	tp["AltStrings"] = alts
   172  	return typ
   173  }
   174  
   175  // AddEnumExt adds a given type to the registry that extends an existing parTyp enum.
   176  // Requires the N value to set N from and grab type info from.
   177  // if bitFlag then sets BitFlag property, and each value represents a bit in a set of bit
   178  // flags, so the string rep of a value contains an or-list of names for each bit set,
   179  // separated by | -- can also add additional properties -- they are copied so
   180  // can be re-used across enums
   181  func (tr *EnumRegistry) AddEnumExt(parTyp reflect.Type, en interface{}, bitFlag bool, props map[string]interface{}) reflect.Type {
   182  	typ := tr.AddEnum(en, bitFlag, props)
   183  	snm := ShortTypeName(typ)
   184  	if parTyp == typ {
   185  		log.Printf("kit.Enums.AddEnumExt: parent type: %v is same as type being defined -- must be different!\n", parTyp.String())
   186  	} else {
   187  		tr.SetProp(snm, "ParType", parTyp)
   188  	}
   189  	return typ
   190  }
   191  
   192  // AddEnumExtAltLower adds a given type to the registry that extends an existing parTyp enum.
   193  // Requires the N value to set N from and grab type info from.
   194  // Automatically initializes AltStrings alternative string map based on the name with
   195  // given prefix removed (e.g., a type name-based prefix) and lower-cased.
   196  // Also requires the number of enums -- assumes starts at end of parent.
   197  func (tr *EnumRegistry) AddEnumExtAltLower(parTyp reflect.Type, en interface{}, bitFlag bool, props map[string]interface{}, prefix string) reflect.Type {
   198  	typ := tr.AddEnumExt(parTyp, en, bitFlag, props)
   199  	n := EnumIfaceToInt64(en)
   200  	snm := ShortTypeName(typ)
   201  	alts := make(map[int64]string)
   202  	tp := tr.Properties(snm)
   203  	pnm := ShortTypeName(parTyp)
   204  	pp := tr.Properties(pnm)
   205  	pn, _ := ToInt(pp["N"])
   206  	if palti, ok := pp["AltStrings"]; ok {
   207  		palt := palti.(map[int64]string)
   208  		for k, v := range palt {
   209  			alts[k] = v
   210  		}
   211  	}
   212  	for i := int64(pn); i < n; i++ {
   213  		str := EnumInt64ToString(i, typ)
   214  		str = strings.ToLower(strings.TrimPrefix(str, prefix))
   215  		alts[i] = str
   216  	}
   217  	tp["AltStrings"] = alts
   218  	return typ
   219  }
   220  
   221  // Enum finds an enum type based on its *short* package-qualified type name
   222  // returns nil if not found.
   223  func (tr *EnumRegistry) Enum(name string) reflect.Type {
   224  	return tr.Enums[name]
   225  }
   226  
   227  // TypeRegistered returns true if the given type is registered as an enum type.
   228  func (tr *EnumRegistry) TypeRegistered(typ reflect.Type) bool {
   229  	enumName := ShortTypeName(typ)
   230  	_, ok := tr.Enums[enumName]
   231  	return ok
   232  }
   233  
   234  // Props returns properties for this type based on short package-qualified name.
   235  // Makes props map if not already made.
   236  func (tr *EnumRegistry) Properties(enumName string) map[string]interface{} {
   237  	tp, ok := tr.Props[enumName]
   238  	if !ok {
   239  		tp = make(map[string]interface{})
   240  		tr.Props[enumName] = tp
   241  	}
   242  	return tp
   243  }
   244  
   245  // Prop safely finds an enum type property from short package-qualified name
   246  // and property key.  Returns nil if not found.
   247  func (tr *EnumRegistry) Prop(enumName, propKey string) interface{} {
   248  	tp, ok := tr.Props[enumName]
   249  	if !ok {
   250  		// fmt.Printf("no props for enum type: %v\n", enumName)
   251  		return nil
   252  	}
   253  	p, ok := tp[propKey]
   254  	if !ok {
   255  		// fmt.Printf("no props for key: %v\n", propKey)
   256  		return nil
   257  	}
   258  	return p
   259  }
   260  
   261  // SetProp safely sets given property for given enum name
   262  func (tr *EnumRegistry) SetProp(enumName, propKey string, val interface{}) {
   263  	tp := tr.Properties(enumName)
   264  	tp[propKey] = val
   265  }
   266  
   267  // AltStrings returns optional alternative string map for enums -- e.g.,
   268  // lower-case, without prefixes etc -- can put multiple such alt strings in
   269  // the one string with your own separator, in a predefined order, if
   270  // necessary, and just call strings.Split on those and get the one you want.
   271  // Uses short package-qualified name. Returns nil if not set.
   272  func (tr *EnumRegistry) AltStrings(enumName string) map[int64]string {
   273  	ps := tr.Prop(enumName, "AltStrings")
   274  	if ps == nil {
   275  		return nil
   276  	}
   277  	m, ok := ps.(map[int64]string)
   278  	if !ok {
   279  		log.Printf("kit.EnumRegistry AltStrings error: AltStrings property must be a map[int64]string type, is not -- is instead: %T\n", m)
   280  		return nil
   281  	}
   282  	return m
   283  }
   284  
   285  // ParType returns optional parent type that given type extends -- nil if not set.
   286  func (tr *EnumRegistry) ParType(enumName string) reflect.Type {
   287  	pti := tr.Prop(enumName, "ParType")
   288  	if pti == nil {
   289  		return nil
   290  	}
   291  	pt, ok := pti.(reflect.Type)
   292  	if !ok {
   293  		log.Printf("kit.EnumRegistry ParType error: ParType property must be a reflect.Type, is not -- is instead: %T\n", pt)
   294  		return nil
   295  	}
   296  	return pt
   297  }
   298  
   299  // NVals returns the number of defined enum values for given enum interface
   300  func (tr *EnumRegistry) NVals(eval interface{}) int64 {
   301  	typ := reflect.TypeOf(eval)
   302  	nm := ShortTypeName(typ)
   303  	n := tr.Prop(nm, "N")
   304  	if n != nil {
   305  		return n.(int64)
   306  	}
   307  	log.Printf("kit.EnumRegistry: no N registered for type: %v\n", nm)
   308  	return 0
   309  }
   310  
   311  // IsBitFlag checks if this enum is for bit flags instead of mutually-exclusive int
   312  // values -- checks BitFlag property -- if true string rep of a value contains
   313  // an or-list of names for each bit set, separated by |
   314  func (tr *EnumRegistry) IsBitFlag(typ reflect.Type) bool {
   315  	b, _ := ToBool(tr.Prop(ShortTypeName(typ), "BitFlag"))
   316  	return b
   317  }
   318  
   319  ////////////////////////////////////////////////////////////////////////////////////////
   320  //   To / From Int64 for generic interface{} and reflect.Value
   321  
   322  // EnumIfaceToInt64 converts an enum interface{} into an int64 using reflect
   323  // -- just use int64(eval) when you have the enum value in hand -- this is
   324  // when you just have a generic interface{}
   325  func EnumIfaceToInt64(eval interface{}) int64 {
   326  	ev := NonPtrValue(reflect.ValueOf(eval))
   327  	var ival int64
   328  	reflect.ValueOf(&ival).Elem().Set(ev.Convert(reflect.TypeOf(ival)))
   329  	return ival
   330  }
   331  
   332  // SetEnumIfaceFromInt64 sets enum interface{} value from int64 value -- must
   333  // pass a pointer to the enum and also needs raw type of the enum as well --
   334  // can't get it from the interface{} reliably
   335  func SetEnumIfaceFromInt64(eval interface{}, ival int64, typ reflect.Type) error {
   336  	if reflect.TypeOf(eval).Kind() != reflect.Ptr {
   337  		err := fmt.Errorf("kit.SetEnumFromInt64: must pass a pointer to the enum: Type: %v, Kind: %v\n", reflect.TypeOf(eval).Name(), reflect.TypeOf(eval).Kind())
   338  		log.Printf("%v", err)
   339  		return err
   340  	}
   341  	reflect.ValueOf(eval).Elem().Set(reflect.ValueOf(ival).Convert(typ))
   342  	return nil
   343  }
   344  
   345  // SetEnumValueFromInt64 sets enum value from int64 value, using a
   346  // reflect.Value representation of the enum -- does more checking and can get
   347  // type from value compared to Iface version
   348  func SetEnumValueFromInt64(eval reflect.Value, ival int64) error {
   349  	if eval.Kind() != reflect.Ptr {
   350  		err := fmt.Errorf("kit.SetEnumValueFromInt64: must pass a pointer value to the enum: Type: %v, Kind: %v\n", eval.Type().String(), eval.Kind())
   351  		log.Printf("%v", err)
   352  		return err
   353  	}
   354  	npt := NonPtrType(eval.Type())
   355  	eval.Elem().Set(reflect.ValueOf(ival).Convert(npt))
   356  	return nil
   357  }
   358  
   359  // EnumIfaceFromInt64 returns an interface{} value which is an enum value of
   360  // given type (not a pointer to it), set to given integer value
   361  func EnumIfaceFromInt64(ival int64, typ reflect.Type) interface{} {
   362  	evn := reflect.New(typ)
   363  	SetEnumValueFromInt64(evn, ival)
   364  	return evn.Elem().Interface()
   365  }
   366  
   367  ////////////////////////////////////////////////////////////////////////////////////////
   368  //   To / From String for generic interface{} and reflect.Value
   369  
   370  // EnumIfaceToString converts an enum interface{} value to its corresponding
   371  // string value, using fmt.Stringer interface directly -- same effect as
   372  // calling fmt.Sprintf("%v") but this is slightly faster
   373  func EnumIfaceToString(eval interface{}) string {
   374  	strer, ok := eval.(fmt.Stringer) // will fail if not impl
   375  	if !ok {
   376  		log.Printf("kit.EnumIfaceToString: fmt.Stringer interface not supported by type %v\n", reflect.TypeOf(eval).Name())
   377  		return ""
   378  	}
   379  	return strer.String()
   380  }
   381  
   382  // EnumInt64ToString first converts an int64 to enum of given type, and then
   383  // converts that to a string value
   384  func EnumInt64ToString(ival int64, typ reflect.Type) string {
   385  	ev := EnumIfaceFromInt64(ival, typ)
   386  	return EnumIfaceToString(ev)
   387  }
   388  
   389  // EnumIfaceFromString returns an interface{} value which is an enum value of
   390  // given type (not a pointer to it), set to given string value -- requires
   391  // reflect type of enum
   392  func EnumIfaceFromString(str string, typ reflect.Type) interface{} {
   393  	evn := reflect.New(typ)
   394  	SetEnumValueFromString(evn, str)
   395  	return evn.Elem().Interface()
   396  }
   397  
   398  // EnumIfaceToAltString converts an enum interface{} value to its
   399  // corresponding alternative string value from the enum registry
   400  func (tr *EnumRegistry) EnumIfaceToAltString(eval interface{}) string {
   401  	if reflect.TypeOf(eval).Kind() == reflect.Ptr {
   402  		eval = reflect.ValueOf(eval).Elem() // deref the pointer
   403  	}
   404  	et := reflect.TypeOf(eval)
   405  	tn := ShortTypeName(et)
   406  	alts := tr.AltStrings(tn)
   407  	if alts == nil {
   408  		log.Printf("kit.EnumToAltString: no alternative string map for type %v\n", tn)
   409  		return ""
   410  	}
   411  	// convert to int64 for lookup
   412  	ival := EnumIfaceToInt64(eval)
   413  	return alts[ival]
   414  }
   415  
   416  // EnumInt64ToAltString converts an int64 value to the enum of given type, and
   417  // then into corresponding alternative string value
   418  func (tr *EnumRegistry) EnumInt64ToAltString(ival int64, typnm string) string {
   419  	alts := tr.AltStrings(typnm)
   420  	if alts == nil {
   421  		log.Printf("kit.EnumInt64ToAltString: no alternative string map for type %v\n", typnm)
   422  		return ""
   423  	}
   424  	return alts[ival]
   425  }
   426  
   427  // SetEnumValueFromString sets enum value from string using reflect.Value
   428  // IMPORTANT: requires the modified stringer go generate utility
   429  // that generates a StringToTypeName method
   430  func SetEnumValueFromString(eval reflect.Value, str string) error {
   431  	etp := eval.Type()
   432  	if etp.Kind() != reflect.Ptr {
   433  		err := fmt.Errorf("kit.SetEnumValueFromString -- you must pass a pointer enum, not type: %v kind %v\n", etp, etp.Kind())
   434  		// log.Printf("%v", err)
   435  		return err
   436  	}
   437  	et := etp.Elem()
   438  	methnm := "FromString"
   439  	meth := eval.MethodByName(methnm)
   440  	if ValueIsZero(meth) || meth.IsNil() {
   441  		err := fmt.Errorf("kit.SetEnumValueFromString: stringer-generated FromString() method not found: %v for type: %v %T\n", methnm, et.Name(), eval.Interface())
   442  		log.Printf("%v", err)
   443  		return err
   444  	}
   445  	sv := reflect.ValueOf(str)
   446  	args := make([]reflect.Value, 1)
   447  	args[0] = sv
   448  	meth.Call(args)
   449  	// fmt.Printf("return from FromString method: %v\n", rv[0].Interface())
   450  	return nil
   451  }
   452  
   453  // SetEnumIfaceFromString sets enum value from string -- must pass a *pointer*
   454  // to the enum item. IMPORTANT: requires the modified stringer go generate
   455  // utility that generates a StringToTypeName method
   456  func SetEnumIfaceFromString(eptr interface{}, str string) error {
   457  	return SetEnumValueFromString(reflect.ValueOf(eptr), str)
   458  }
   459  
   460  // SetEnumValueFromAltString sets value from alternative string using a
   461  // reflect.Value -- must pass a *pointer* value to the enum item.
   462  func (tr *EnumRegistry) SetEnumValueFromAltString(eval reflect.Value, str string) error {
   463  	etp := eval.Type()
   464  	if etp.Kind() != reflect.Ptr {
   465  		err := fmt.Errorf("kit.SetEnumValueFromString -- you must pass a pointer enum, not type: %v kind %v\n", etp, etp.Kind())
   466  		log.Printf("%v", err)
   467  		return err
   468  	}
   469  	et := etp.Elem()
   470  	tn := ShortTypeName(et)
   471  	alts := tr.AltStrings(tn)
   472  	if alts == nil {
   473  		err := fmt.Errorf("kit.SetEnumValueFromAltString: no alternative string map for type %v\n", tn)
   474  		// log.Printf("%v", err)
   475  		return err
   476  	}
   477  	for i, v := range alts {
   478  		if v == str {
   479  			return SetEnumValueFromInt64(eval, int64(i))
   480  		}
   481  	}
   482  	err := fmt.Errorf("kit.SetEnumValueFromAltString: string: %v not found in alt list of strings for type%v\n", str, tn)
   483  	// log.Printf("%v", err)
   484  	return err
   485  }
   486  
   487  // SetEnumIfaceFromAltString sets from alternative string list using an interface{}
   488  // to the enum -- must pass a *pointer* to the enum item.
   489  func (tr *EnumRegistry) SetEnumIfaceFromAltString(eptr interface{}, str string) error {
   490  	return tr.SetEnumValueFromAltString(reflect.ValueOf(eptr), str)
   491  }
   492  
   493  // SetEnumValueFromStringAltFirst first attempts to set an enum from an
   494  // alternative string, and if that fails, then it tries to set from the
   495  // regular string representation func (tr *EnumRegistry)
   496  func (tr *EnumRegistry) SetEnumValueFromStringAltFirst(eval reflect.Value, str string) error {
   497  	err := tr.SetEnumValueFromAltString(eval, str)
   498  	if err != nil {
   499  		return SetEnumValueFromString(eval, str)
   500  	}
   501  	return err
   502  }
   503  
   504  // SetEnumIfaceFromStringAltFirst first attempts to set an enum from an
   505  // alternative string, and if that fails, then it tries to set from the
   506  // regular string representation func (tr *EnumRegistry)
   507  func (tr *EnumRegistry) SetEnumIfaceFromStringAltFirst(eptr interface{}, str string) error {
   508  	err := tr.SetEnumIfaceFromAltString(eptr, str)
   509  	if err != nil {
   510  		return SetEnumIfaceFromString(eptr, str)
   511  	}
   512  	return err
   513  }
   514  
   515  ///////////////////////////////////////////////////////////////////////////////
   516  //  BitFlags
   517  
   518  // BitFlagsToString converts an int64 of bit flags into a string
   519  // representation of the bits that are set -- en is the number of defined
   520  // bits, and also provides the type name for looking up strings
   521  func BitFlagsToString(bflg int64, en interface{}) string {
   522  	et := PtrType(reflect.TypeOf(en)).Elem()
   523  	n := int(EnumIfaceToInt64(en))
   524  	str := ""
   525  	for i := 0; i < n; i++ {
   526  		if bitflag.Has(bflg, i) {
   527  			evs := EnumInt64ToString(int64(i), et)
   528  			if str == "" {
   529  				str = evs
   530  			} else {
   531  				str += "|" + evs
   532  			}
   533  		}
   534  	}
   535  	return str
   536  }
   537  
   538  // BitFlagsFromString sets an int64 of bit flags from a string representation
   539  // of the bits that are set -- en is the number of defined bits, and also
   540  // provides the type name for looking up strings
   541  func BitFlagsFromString(bflg *int64, str string, en interface{}) error {
   542  	et := PtrType(reflect.TypeOf(en)).Elem()
   543  	n := int(EnumIfaceToInt64(en))
   544  	return BitFlagsTypeFromString(bflg, str, et, n)
   545  }
   546  
   547  // BitFlagsTypeFromString sets an int64 of bit flags from a string representation
   548  // of the bits that are set -- gets enum type and n of defined elements directly
   549  func BitFlagsTypeFromString(bflg *int64, str string, et reflect.Type, n int) error {
   550  	flgs := strings.Split(str, "|")
   551  	evv := reflect.New(et)
   552  	var err error
   553  	for _, flg := range flgs {
   554  		err = SetEnumValueFromString(evv, flg)
   555  		if err == nil {
   556  			evi := EnumIfaceToInt64(evv.Interface())
   557  			bitflag.Set(bflg, int(evi))
   558  		}
   559  	}
   560  	return err
   561  }
   562  
   563  // BitFlagsFromStringAltFirst sets an int64 of bit flags from a string
   564  // representation of the bits that are set, using alt-strings first -- gets
   565  // enum type and n of defined elements directly
   566  func (tr *EnumRegistry) BitFlagsFromStringAltFirst(bflg *int64, str string, et reflect.Type, n int) error {
   567  	flgs := strings.Split(str, "|")
   568  	evv := reflect.New(et)
   569  	var err error
   570  	for _, flg := range flgs {
   571  		err = tr.SetEnumValueFromStringAltFirst(evv, flg)
   572  		if err == nil {
   573  			evi := EnumIfaceToInt64(evv.Interface())
   574  			bitflag.Set(bflg, int(evi))
   575  		}
   576  	}
   577  	return err
   578  }
   579  
   580  // SetAnyEnumValueFromString looks up enum type on registry, and if it is
   581  // registered as a bitflag, sets bits from string, otherwise tries to set from
   582  // alt strings if those exist, and finally tries direct set from string --
   583  // must pass a *pointer* value to the enum item.
   584  func (tr *EnumRegistry) SetAnyEnumValueFromString(eval reflect.Value, str string) error {
   585  	etp := eval.Type()
   586  	if etp.Kind() != reflect.Ptr {
   587  		err := fmt.Errorf("kit.SetAnyEnumValueFromString -- you must pass a pointer enum, not type: %v kind %v\n", etp, etp.Kind())
   588  		log.Printf("%v", err)
   589  		return err
   590  	}
   591  	et := etp.Elem()
   592  	if tr.IsBitFlag(et) {
   593  		var bf int64
   594  		err := tr.BitFlagsFromStringAltFirst(&bf, str, et, int(tr.NVals(eval.Elem().Interface())))
   595  		if err != nil {
   596  			return err
   597  		}
   598  		return SetEnumValueFromInt64(eval, bf)
   599  	}
   600  	return tr.SetEnumValueFromStringAltFirst(eval, str)
   601  }
   602  
   603  // SetAnyEnumIfaceFromString looks up enum type on registry, and if it is
   604  // registered as a bitflag, sets bits from string, otherwise tries to set from
   605  // alt strings if those exist, and finally tries direct set from string --
   606  // must pass a *pointer* value to the enum item.
   607  func (tr *EnumRegistry) SetAnyEnumIfaceFromString(eptr interface{}, str string) error {
   608  	return tr.SetAnyEnumValueFromString(reflect.ValueOf(eptr), str)
   609  }
   610  
   611  ///////////////////////////////////////////////////////////////////////////////
   612  //  EnumValue
   613  
   614  // EnumValue represents enum values, in common int64 terms, e.g., for GUI
   615  type EnumValue struct {
   616  	Name  string       `desc:"name for this value"`
   617  	Value int64        `desc:"integer value"`
   618  	Type  reflect.Type `desc:"the enum type that this value belongs to"`
   619  }
   620  
   621  // Set sets the values of the EnumValue struct
   622  func (ev *EnumValue) Set(name string, val int64, typ reflect.Type) {
   623  	ev.Name = name
   624  	ev.Value = val
   625  	ev.Type = typ
   626  }
   627  
   628  // String satisfies fmt.Stringer and provides a string representation of enum: just the name
   629  func (ev EnumValue) String() string {
   630  	return ev.Name
   631  }
   632  
   633  // Values returns an EnumValue slice for all the values of an enum type -- if
   634  // alt is true and alt names exist, then those are used
   635  func (tr *EnumRegistry) Values(enumName string, alt bool) []EnumValue {
   636  	et, ok := tr.Enums[enumName]
   637  	if !ok {
   638  		return nil
   639  	}
   640  	vals, ok := tr.Vals[enumName]
   641  	if ok {
   642  		return vals
   643  	}
   644  	alts := tr.AltStrings(enumName)
   645  	pt := tr.ParType(enumName)
   646  	n := tr.Prop(enumName, "N").(int64)
   647  	vals = make([]EnumValue, n)
   648  	st := 0
   649  	if pt != nil {
   650  		ptv := tr.TypeValues(pt, alt)
   651  		copy(vals, ptv)
   652  		st = len(ptv)
   653  	}
   654  	for i := int64(st); i < n; i++ {
   655  		str := EnumInt64ToString(i, et) // todo: what happens when no string for given values?
   656  		if alt && alts != nil {
   657  			str = alts[i]
   658  		}
   659  		vals[i].Set(str, i, et)
   660  	}
   661  	tr.Vals[enumName] = vals
   662  	return vals
   663  }
   664  
   665  // TypeValues returns an EnumValue slice for all the values of an enum type --
   666  // if alt is true and alt names exist, then those are used
   667  func (tr *EnumRegistry) TypeValues(et reflect.Type, alt bool) []EnumValue {
   668  	return tr.Values(ShortTypeName(et), alt)
   669  }
   670  
   671  // AllTagged returns a list of all registered enum types that include a given
   672  // property key value -- does not check for the value of that value -- just
   673  // its existence
   674  func (tr *EnumRegistry) AllTagged(key string) []reflect.Type {
   675  	tl := make([]reflect.Type, 0)
   676  	for _, typ := range tr.Enums {
   677  		tp := tr.Prop(ShortTypeName(typ), key)
   678  		if tp == nil {
   679  			continue
   680  		}
   681  		tl = append(tl, typ)
   682  	}
   683  	return tl
   684  }
   685  
   686  ///////////////////////////////////////////////////////////////////////////////
   687  //  JSON, Text Marshal
   688  
   689  func EnumMarshalJSON(eval interface{}) ([]byte, error) {
   690  	et := reflect.TypeOf(eval)
   691  	b := make([]byte, 0, 50)
   692  	b = append(b, []byte("\"")...)
   693  	if Enums.IsBitFlag(et) {
   694  		b = append(b, []byte(BitFlagsToString(EnumIfaceToInt64(eval), eval))...)
   695  	} else {
   696  		b = append(b, []byte(EnumIfaceToString(eval))...)
   697  	}
   698  	b = append(b, []byte("\"")...)
   699  	return b, nil
   700  }
   701  
   702  func EnumUnmarshalJSON(eval interface{}, b []byte) error {
   703  	et := reflect.TypeOf(eval)
   704  	noq := string(bytes.Trim(b, "\""))
   705  	if Enums.IsBitFlag(et) {
   706  		bf := int64(0)
   707  		err := BitFlagsTypeFromString(&bf, noq, et, int(Enums.NVals(eval)))
   708  		if err == nil {
   709  			return SetEnumIfaceFromInt64(eval, bf, et)
   710  		}
   711  		return err
   712  	}
   713  	return SetEnumIfaceFromString(eval, noq)
   714  }
   715  
   716  func EnumMarshalText(eval interface{}) ([]byte, error) {
   717  	et := reflect.TypeOf(eval)
   718  	b := make([]byte, 0, 50)
   719  	if Enums.IsBitFlag(et) {
   720  		b = append(b, []byte(BitFlagsToString(EnumIfaceToInt64(eval), eval))...)
   721  	} else {
   722  		b = append(b, []byte(EnumIfaceToString(eval))...)
   723  	}
   724  	return b, nil
   725  }
   726  
   727  func EnumUnmarshalText(eval interface{}, b []byte) error {
   728  	et := reflect.TypeOf(eval)
   729  	noq := string(b)
   730  	if Enums.IsBitFlag(et) {
   731  		bf := int64(0)
   732  		err := BitFlagsTypeFromString(&bf, noq, et, int(Enums.NVals(eval)))
   733  		if err == nil {
   734  			return SetEnumIfaceFromInt64(eval, bf, et)
   735  		}
   736  		return err
   737  	}
   738  	return SetEnumIfaceFromString(eval, noq)
   739  }
   740  
   741  /////////////////////////////////////////////////////////////
   742  // Following is for testing..
   743  
   744  // TestFlags are for testing -- need the generated string code, so putting in here
   745  type TestFlags int32
   746  
   747  const (
   748  	TestFlagsNil TestFlags = iota
   749  	TestFlag1
   750  	TestFlag2
   751  	TestFlagsN
   752  )
   753  
   754  //go:generate stringer -type=TestFlags
   755  
   756  var KiT_TestFlags = Enums.AddEnumAltLower(TestFlagsN, NotBitFlag, nil, "Test")
   757  
   758  func (ev TestFlags) MarshalJSON() ([]byte, error)  { return EnumMarshalJSON(ev) }
   759  func (ev *TestFlags) UnmarshalJSON(b []byte) error { return EnumUnmarshalJSON(ev, b) }