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) }