github.com/wfusion/gofusion@v1.1.14/common/utils/enum.go (about) 1 package utils 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/pkg/errors" 9 "golang.org/x/text/cases" 10 "golang.org/x/text/language" 11 ) 12 13 type Enumerable[T comparable, TS ~[]T] interface { 14 Enum(s string) TS 15 String(k any) string 16 IsValid(t T) bool 17 } 18 19 type enumStringOption struct { 20 ignoreCaseSensitivity bool 21 } 22 23 type enumStringOptFn func(*enumStringOption) 24 25 func IgnoreEnumStringCase() enumStringOptFn { 26 return func(e *enumStringOption) { 27 e.ignoreCaseSensitivity = true 28 } 29 } 30 31 func NewEnumString[T comparable, TS ~[]T](mapping map[T]string, opts ...OptionExtender) Enumerable[T, TS] { 32 opt := ApplyOptions[enumStringOption](opts...) 33 if len(mapping) == 0 { 34 panic(errors.New("enum mapping is empty")) 35 } 36 return (&enumString[T, TS]{ 37 mapping: mapping, 38 ignoreCase: opt.ignoreCaseSensitivity, 39 }).init() 40 } 41 42 type enumString[T comparable, TS ~[]T] struct { 43 ignoreCase bool 44 elemType reflect.Type 45 elemSliceType reflect.Type 46 prefix string 47 mapping map[T]string 48 reversedMapping map[string]TS 49 } 50 51 func (e *enumString[T, TS]) Enum(s string) TS { 52 s = e.caseSensitivityConv(s) 53 if v, ok := e.reversedMapping[s]; ok { 54 return SliceConvert(v, e.elemSliceType).(TS) 55 } 56 return nil 57 } 58 59 func (e *enumString[T, TS]) String(k any) string { 60 if reflect.TypeOf(k).ConvertibleTo(e.elemType) { 61 k = reflect.ValueOf(k).Convert(e.elemType).Interface().(T) 62 } 63 if t, ok := k.(T); !ok { 64 return fmt.Sprintf("%s(%v)", e.prefix, k) 65 } else { 66 if v, ok := e.mapping[t]; ok { 67 return v 68 } 69 // avoid stack overflow for Stringer implement 70 sortable := ComparableToSortable(t) 71 if sortable != nil { 72 return fmt.Sprintf("%s(%+v)", e.prefix, sortable) 73 } else { 74 return fmt.Sprintf("%s(N/A)", e.prefix) 75 } 76 } 77 } 78 79 func (e *enumString[T, TS]) IsValid(t T) bool { 80 _, ok := e.mapping[t] 81 return ok 82 } 83 84 func (e *enumString[T, TS]) init() Enumerable[T, TS] { 85 // get key 86 var key any 87 for k := range e.mapping { 88 key = k 89 break 90 } 91 e.elemType = reflect.TypeOf(key) 92 e.elemSliceType = reflect.SliceOf(e.elemType) 93 94 // get prefix name 95 e.prefix = cases.Title(language.English, cases.NoLower).String(e.elemType.Name()) 96 97 // get reversed mapping 98 e.reversedMapping = make(map[string]TS, len(e.mapping)) 99 for k, v := range e.mapping { 100 v = e.caseSensitivityConv(v) 101 e.reversedMapping[v] = append(e.reversedMapping[v], k) 102 } 103 104 return e 105 } 106 107 func (e *enumString[T, TS]) caseSensitivityConv(s string) string { 108 if e.ignoreCase { 109 return strings.ToLower(s) 110 } 111 return s 112 }