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  }