Skip to content

Commit

Permalink
add WrapUintEnum and WrapFloatEnum (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
huykingsofm committed Dec 17, 2024
1 parent bbe7666 commit ec74283
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 44 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ func init() {

## ⭐ WrapEnum

`WrapEnum` offers a set of built-in methods to simplify working with enums.
`WrapEnum` offers a set of built-in methods to simplify working with `int64` enums.

> [!TIP]
> For other numeric types, use `WrapUintEnum` for `uint64` and `WrapFloatEnum` for `float64`.
**Pros 💪**
- Supports constant values ([#][5]).
Expand Down
22 changes: 11 additions & 11 deletions enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import (
// innerEnumable is an internal interface used for handling centralized
// initialization via New function.
type innerEnumable interface {
// newEnum creates an enum value of the current type and map it into the
// enum system.
// newEnum creates a dynamic enum value of the current type and map it into
// the enum system.
newEnum(id int64, s string) any
}

Expand Down Expand Up @@ -158,13 +158,13 @@ func Finalize[Enum any]() bool {
//
// DEPRECATED: Use FromNumber instead.
func FromInt[Enum any](i int) (Enum, bool) {
return mtmap.Get(mtkey.Number2Enum[int, Enum](i))
return mtmap.Get2(mtkey.Number2Enum[int, Enum](i))
}

// FromNumber returns the corresponding enum for a given number representation,
// and whether it is valid.
func FromNumber[Enum any, N xreflect.Number](n N) (Enum, bool) {
return mtmap.Get(mtkey.Number2Enum[N, Enum](n))
return mtmap.Get2(mtkey.Number2Enum[N, Enum](n))
}

// MustFromInt returns the corresponding enum for a given int representation.
Expand Down Expand Up @@ -197,7 +197,7 @@ func MustFromNumber[Enum any, N xreflect.Number](n N) Enum {
// FromString returns the corresponding enum for a given string representation,
// and whether it is valid.
func FromString[Enum any](s string) (Enum, bool) {
return mtmap.Get(mtkey.String2Enum[Enum](s))
return mtmap.Get2(mtkey.String2Enum[Enum](s))
}

// MustFromString returns the corresponding enum for a given string
Expand All @@ -216,7 +216,7 @@ func MustFromString[Enum any](s string) Enum {
// ToString returns the string representation of the given enum value. It
// returns <nil> for invalid enums.
func ToString[Enum any](value Enum) string {
str, ok := mtmap.Get(mtkey.Enum2String(value))
str, ok := mtmap.Get2(mtkey.Enum2String(value))
if !ok {
return "<nil>"
}
Expand All @@ -229,7 +229,7 @@ func ToString[Enum any](value Enum) string {
//
// DEPRECATED: It is only valid if the enum is not a floating-point number.
func ToInt[Enum any](enum Enum) int {
value, ok := mtmap.Get(mtkey.Enum2Number[Enum, int](enum))
value, ok := mtmap.Get2(mtkey.Enum2Number[Enum, int](enum))
if !ok {
return math.MinInt32
}
Expand All @@ -240,7 +240,7 @@ func ToInt[Enum any](enum Enum) int {
// IsValid checks if an enum value is valid.
// It returns true if the enum value is valid, and false otherwise.
func IsValid[Enum any](value Enum) bool {
_, ok := mtmap.Get(mtkey.Enum2String(value))
_, ok := mtmap.Get2(mtkey.Enum2String(value))
return ok
}

Expand Down Expand Up @@ -303,7 +303,7 @@ func ScanSQL[Enum any](a any, value *Enum) error {

// All returns a slice containing all enum values of a specific type.
func All[Enum any]() []Enum {
return mtmap.MustGet(mtkey.AllEnums[Enum]())
return mtmap.Get(mtkey.AllEnums[Enum]())
}

var advancedEnumNames = []string{"WrapEnum", "SafeEnum"}
Expand All @@ -317,7 +317,7 @@ var advancedEnumNames = []string{"WrapEnum", "SafeEnum"}
// NameOf[Role]() = "Role"
// NameOf[WrapEnum[role]]() = "Role"
func NameOf[T any]() string {
if name, ok := mtmap.Get(mtkey.NameOf[T]()); ok {
if name, ok := mtmap.Get2(mtkey.NameOf[T]()); ok {
return name
}

Expand All @@ -342,7 +342,7 @@ func NameOf[T any]() string {
// TrueNameOf[Role]() = "Role"
// TrueNameOf[WrapEnum[role]]() = "WrapEnum[role]"
func TrueNameOf[T any]() string {
if name, ok := mtmap.Get(mtkey.TrueNameOf[T]()); ok {
if name, ok := mtmap.Get2(mtkey.TrueNameOf[T]()); ok {
return name
}

Expand Down
30 changes: 13 additions & 17 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
func GetAvailableEnumValue[T any]() int64 {
id := int64(0)
for {
if _, ok := mtmap.Get(mtkey.Number2Enum[int64, T](id)); !ok {
if _, ok := mtmap.Get2(mtkey.Number2Enum[int64, T](id)); !ok {
break
}
id++
Expand All @@ -23,45 +23,41 @@ func GetAvailableEnumValue[T any]() int64 {

// MapAny map the enum value to the enum system.
func MapAny[N xreflect.Number, Enum any](id N, enum Enum, s string) Enum {
if s == "" {
panic("not allow empty string representation in enum definition")
}

if s == "<nil>" {
panic("not allow \"<nil>\" string representation in enum definition")
}

if ok := mtmap.MustGet(mtkey.IsFinalized[Enum]()); ok {
if ok := mtmap.Get(mtkey.IsFinalized[Enum]()); ok {
panic("enum is finalized")
}

if _, ok := mtmap.Get(mtkey.Number2Enum[N, Enum](id)); ok {
if _, ok := mtmap.Get2(mtkey.Number2Enum[N, Enum](id)); ok {
panic("duplicate enum number is not allowed")
}

if _, ok := mtmap.Get(mtkey.String2Enum[Enum](s)); ok {
if _, ok := mtmap.Get2(mtkey.String2Enum[Enum](s)); ok {
panic("duplicate enum string is not allowed")
}

if _, ok := mtmap.Get(mtkey.Enum2Number[Enum, N](enum)); ok {
if _, ok := mtmap.Get2(mtkey.Enum2Number[Enum, N](enum)); ok {
panic("duplicate enum is not allowed")
}

mtmap.Set(mtkey.Enum2String(enum), s)
mtmap.Set(mtkey.String2Enum[Enum](s), enum)
mapnumber(enum, id)
mapEnumNumber(enum, id)

allVals := mtmap.MustGet(mtkey.AllEnums[Enum]())
allVals := mtmap.Get(mtkey.AllEnums[Enum]())
allVals = append(allVals, enum)
mtmap.Set(mtkey.AllEnums[Enum](), allVals)

return enum
}

func mapnumber[Enum any, N xreflect.Number](enum Enum, n N) {
// Only map the enum to integer if the enum is represented by integer
// mapEnumNumber maps the enum to all its number representations (including
// signed and unsigned integers, floating-point numbers) and vice versa.
func mapEnumNumber[Enum any, N xreflect.Number](enum Enum, n N) {
// Only map the enum to integers if the enum is represented by integer
// values, where the integer corresponds to the actual numeric value,
// regardless of the underlying type.
//
// For example: float32(3) is also an integer, whereas float32(0.7) is not.
mapInteger := true
if xreflect.IsFloat32[N]() {
mapInteger = xreflect.Convert[float32](n) == xmath.Trunc32(xreflect.Convert[float32](n))
Expand Down
8 changes: 4 additions & 4 deletions internal/mtmap/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package mtmap

var globalmap = &MTMap{}

func Get[V any](key mtKeyer[V]) (V, bool) {
return GetM(globalmap, key)
func Get2[V any](key mtKeyer[V]) (V, bool) {
return Get2M(globalmap, key)
}

func MustGet[V any](key mtKeyer[V]) V {
return MustGetM(globalmap, key)
func Get[V any](key mtKeyer[V]) V {
return GetM(globalmap, key)
}

func Set[V any](key mtKeyer[V], v V) {
Expand Down
6 changes: 3 additions & 3 deletions internal/mtmap/mtmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type mtKeyer[V any] interface {
InferValue() V
}

func GetM[V any](m *MTMap, key mtKeyer[V]) (V, bool) {
func Get2M[V any](m *MTMap, key mtKeyer[V]) (V, bool) {
var zero V
if m.data == nil {
return zero, false
Expand Down Expand Up @@ -36,8 +36,8 @@ func GetM[V any](m *MTMap, key mtKeyer[V]) (V, bool) {
}
}

func MustGetM[V any](m *MTMap, key mtKeyer[V]) V {
v, _ := GetM(m, key)
func GetM[V any](m *MTMap, key mtKeyer[V]) V {
v, _ := Get2M(m, key)
return v
}

Expand Down
9 changes: 6 additions & 3 deletions internal/xmath/math32.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const (
bias32 = 127
)

// Trunc32 is similar to math.Trunc, but for float32 instead.
//
// The implementation is copied and modified from math.Trunc.
func Trunc32(f float32) float32 {
if f == 0 || IsNaN32(f) || IsInf32(f, 0) {
return f
Expand Down Expand Up @@ -43,9 +46,9 @@ func modf32(f float32) (i float32, frac float32) {
x := math.Float32bits(f)
e := uint(x>>shift32)&mask32 - bias32

// Keep the top 12+e bits, the integer part; clear the rest.
if e < 64-12 {
x &^= 1<<(64-12-e) - 1
// Keep the top 9+e bits, the integer part; clear the rest.
if e < 32-9 {
x &^= 1<<(32-9-e) - 1
}
i = math.Float32frombits(x)
frac = f - i
Expand Down
2 changes: 1 addition & 1 deletion safe_enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (e *SafeEnum[underlyingEnum]) Scan(a any) error {
}

func (e SafeEnum[underlyingEnum]) Int() int {
return mtmap.MustGet(mtkey.Enum2Number[SafeEnum[underlyingEnum], int](e))
return mtmap.Get(mtkey.Enum2Number[SafeEnum[underlyingEnum], int](e))
}

func (e SafeEnum[underlyingEnum]) String() string {
Expand Down
50 changes: 50 additions & 0 deletions wrap_float_enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package enum

import (
"database/sql/driver"
"fmt"

"github.com/xybor-x/enum/internal/core"
)

// WrapFloatEnum provides a set of built-in methods to simplify working with
// float64 enums.
type WrapFloatEnum[underlyingEnum any] float64

func (e WrapFloatEnum[underlyingEnum]) IsValid() bool {
return IsValid(e)
}

func (e WrapFloatEnum[underlyingEnum]) MarshalJSON() ([]byte, error) {
return MarshalJSON(e)
}

func (e *WrapFloatEnum[underlyingEnum]) UnmarshalJSON(data []byte) error {
return UnmarshalJSON(data, e)
}

func (e WrapFloatEnum[underlyingEnum]) Value() (driver.Value, error) {
return ValueSQL(e)
}

func (e *WrapFloatEnum[underlyingEnum]) Scan(a any) error {
return ScanSQL(a, e)
}

func (e WrapFloatEnum[underlyingEnum]) String() string {
return ToString(e)
}

func (e WrapFloatEnum[underlyingEnum]) GoString() string {
if !e.IsValid() {
return fmt.Sprintf("%f", e)
}

return fmt.Sprintf("%f (%s)", e, e)
}

// WARNING: Only use this function if you fully understand its behavior.
// It might cause unexpected results if used improperly.
func (e WrapFloatEnum[underlyingEnum]) newEnum(id int64, s string) any {
return core.MapAny(id, WrapFloatEnum[underlyingEnum](id), s)
}
11 changes: 7 additions & 4 deletions wrap_enum.go → wrap_int_enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package enum
import (
"database/sql/driver"
"fmt"
"strconv"

"github.com/xybor-x/enum/internal/core"
"github.com/xybor-x/enum/internal/mtkey"
"github.com/xybor-x/enum/internal/mtmap"
)

// WrapEnum provides a set of built-in methods to simplify working with enums.
// WrapEnum provides a set of built-in methods to simplify working with int64
// enums.
type WrapEnum[underlyingEnum any] int64

func (e WrapEnum[underlyingEnum]) IsValid() bool {
Expand All @@ -33,8 +33,11 @@ func (e *WrapEnum[underlyingEnum]) Scan(a any) error {
return ScanSQL(a, e)
}

// Int returns the int representation of the enum.
//
// DEPRECATED: directly cast the enum to int instead.
func (e WrapEnum[underlyingEnum]) Int() int {
return mtmap.MustGet(mtkey.Enum2Number[WrapEnum[underlyingEnum], int](e))
return mtmap.Get(mtkey.Enum2Number[WrapEnum[underlyingEnum], int](e))
}

func (e WrapEnum[underlyingEnum]) String() string {
Expand All @@ -43,7 +46,7 @@ func (e WrapEnum[underlyingEnum]) String() string {

func (e WrapEnum[underlyingEnum]) GoString() string {
if !e.IsValid() {
return strconv.Itoa(int(e))
return fmt.Sprintf("%d", e)
}

return fmt.Sprintf("%d (%s)", e, e)
Expand Down
50 changes: 50 additions & 0 deletions wrap_uint_enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package enum

import (
"database/sql/driver"
"fmt"

"github.com/xybor-x/enum/internal/core"
)

// WrapUintEnum provides a set of built-in methods to simplify working with
// uint64 enums.
type WrapUintEnum[underlyingEnum any] uint64

func (e WrapUintEnum[underlyingEnum]) IsValid() bool {
return IsValid(e)
}

func (e WrapUintEnum[underlyingEnum]) MarshalJSON() ([]byte, error) {
return MarshalJSON(e)
}

func (e *WrapUintEnum[underlyingEnum]) UnmarshalJSON(data []byte) error {
return UnmarshalJSON(data, e)
}

func (e WrapUintEnum[underlyingEnum]) Value() (driver.Value, error) {
return ValueSQL(e)
}

func (e *WrapUintEnum[underlyingEnum]) Scan(a any) error {
return ScanSQL(a, e)
}

func (e WrapUintEnum[underlyingEnum]) String() string {
return ToString(e)
}

func (e WrapUintEnum[underlyingEnum]) GoString() string {
if !e.IsValid() {
return fmt.Sprintf("%d", e)
}

return fmt.Sprintf("%d (%s)", e, e)
}

// WARNING: Only use this function if you fully understand its behavior.
// It might cause unexpected results if used improperly.
func (e WrapUintEnum[underlyingEnum]) newEnum(id int64, s string) any {
return core.MapAny(id, WrapUintEnum[underlyingEnum](id), s)
}

0 comments on commit ec74283

Please sign in to comment.