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