Giter Site home page Giter Site logo

goenum's Introduction

Go enumeration template implementation

License Go Report Card codecov

How to define and use enumerations?

Simply embed goenum.Enum into a struct to define an enumeration type and automatically obtain a set of instance methods and utility functions.

go get github.com/lvyahui8/goenum
import "github.com/lvyahui8/goenum"

// Declaring an enumeration type
type State struct {
    goenum.Enum
}

// Defines a set of enumeration instances
var (
    Created = goenum.NewEnum[State]("Created")
    Running = goenum.NewEnum[State]("Running")
    Success = goenum.NewEnum[State]("Success")
)

// Usage
Created.Name() // string "Created"
Created.Ordinal() // int 0
Created.Compare(Running) //  < 0 : Created < Running
Created.String() // Created
json.Marshal(Created) // \"Created\"
IsValidEnum[State]("Created") // true
s,valid := goenum.ValueOf[State]("Created") // s: Created(struct instance) ,valid = true
Created.Equals(s) // true
s,valid := goenum.ValueOf[State]("cReaTed") //  s: Created(struct instance) ,valid = true
Created.Equals(s) // true
goenum.Values[State]() // equals []State{Created,Running,Success}
Size[State]() // = 3

More Examples examples

Instance Methods

type EnumDefinition interface {
    fmt.Stringer
    json.Marshaler
    encoding.TextMarshaler
    // Name The name of the enumeration. The enumeration names of the same Type must be unique.
    Name() string
    // Equals Compare with another enumeration. Only return true if both Type and Name are the same
    Equals(other EnumDefinition) bool
    // Type String representation of enumeration type
    Type() string
    // Ordinal Get the ordinal of the enumeration, starting from zero and increasing in declared order.
    Ordinal() int
    // Compare -Compare with the ordinal value of another enumeration
    Compare(other EnumDefinition) int
}

Utility functions

  • ValueOf: Find an enumeration instance based on the string, and return a zero value if not found
  • ValueOfIgnoreCase: Ignoring case to obtain enumeration instances.
  • Values: Return all enumeration instances. The returned slice are sorted by ordinal
  • Size: Number of instances of specified enumeration type
  • GetEnumMap: Get all enumeration instances of the specified type.
  • EnumNames: Get the names of a batch of enumerations.
  • GetEnums: Obtain a batch of enumeration instances based on the enumeration name list
  • IsValidEnum: Determine if the incoming string is a valid enumeration
func TestHelpers(t *testing.T) {
	t.Run("NewEnum", func(t *testing.T) {
		defer func() {
			err := recover()
			require.NotNil(t, err)
			require.Equal(t, "Enum must be unique", err)
		}()
		_ = goenum.NewEnum[Role]("Owner")
	})
	t.Run("ValueOf", func(t *testing.T) {
		r, valid := goenum.ValueOf[Role]("Owner")
		require.True(t, valid)
		require.True(t, Owner.Equals(r))
		r, valid = goenum.ValueOf[Role]("Owner")
		require.True(t, valid)
		require.False(t, Developer.Equals(r))
	})
	t.Run("ValueOfIgnoreCase", func(t *testing.T) {
		r, valid := goenum.ValueOfIgnoreCase[Role]("oWnEr")
		require.True(t, valid)
		require.True(t, Owner.Equals(r))
		r, valid = goenum.ValueOfIgnoreCase[Role]("oWnEr")
		require.True(t, valid)
		require.False(t, Reporter.Equals(r))
	})
	t.Run("Values", func(t *testing.T) {
		require.True(t, reflect.DeepEqual([]Role{Reporter, Developer, Owner}, goenum.Values[Role]()))
	})
	t.Run("GetEnumMap", func(t *testing.T) {
		enumMap := goenum.GetEnumMap[Role]()
		require.True(t, len(enumMap) == 3)
		role, exist := enumMap["Owner"]
		require.True(t, exist)
		require.True(t, role.Equals(Owner))
	})
	t.Run("EnumNames", func(t *testing.T) {
		require.True(t, reflect.DeepEqual([]string{"Owner", "Developer"}, goenum.EnumNames(Owner, Developer)))
	})
	t.Run("GetEnums", func(t *testing.T) {
		res, valid := goenum.GetEnums[Role]("Owner", "Developer")
		require.True(t, valid)
		require.True(t, reflect.DeepEqual([]Role{Owner, Developer}, res))
		_, valid = goenum.GetEnums[Role]("a", "b")
		require.False(t, valid)
	})
	t.Run("IsValidEnum", func(t *testing.T) {
		require.True(t, goenum.IsValidEnum[Role]("Owner"))
		require.False(t, goenum.IsValidEnum[Role]("Test"))
	})
}

More features

Complex enumeration initialization

The second parameter in the NewEnum function, if a Src is passed in, NewEnum will use the passed object to construct an enumeration instance.

example code gitlab_role_perms

type Module struct {
	goenum.Enum
	perms    []Permission
	basePath string
}

func (m Module) GetPerms() []Permission {
	return m.perms
}

func (m Module) BasePath() string {
	return m.basePath
}

// 定义模块
var (
	Issues        = goenum.NewEnum[Module]("Issues", Module{perms: []Permission{AddLabels, AddTopic}, basePath: "/issues/"})
	MergeRequests = goenum.NewEnum[Module]("MergeRequests", Module{perms: []Permission{ViewMergeRequest, ApproveMergeRequest, DeleteMergeRequest}, basePath: "/merge/"})
)

JSON serialization and deserialization support for enumerating instances

JSON serialization is already supported by default. Restricted by the implementation of the go JSON library, enumeration classes are required to implement JSON Unmarshaler interface to realize deserialization. Call Unmarshal tool function in the interface to obtain enumeration instances.

type Member struct {
	Roles []Role
}

type Role struct {
	goenum.Enum
	perms []Permission
}

func (r *Role) UnmarshalJSON(data []byte) (err error) {
	role, err := goenum.Unmarshal[Role](data)
	if err == nil {
		*r = role
	}
	return
}

t.Run("jsonMarshal", func(t *testing.T) {
    bytes, err := json.Marshal(Developer)
    require.Nil(t, err)
    require.Equal(t, "\"Developer\"", string(bytes))
    member := Member{Roles: []Role{Reporter, Owner}}
    bytes, err = json.Marshal(member)
    require.Nil(t, err)
    require.Equal(t, "{\"Roles\":[\"Reporter\",\"Owner\"]}", string(bytes))
})
t.Run("jsonUnmarshal", func(t *testing.T) {
    newMember := Member{}
    err := json.Unmarshal([]byte("{\"Roles\":[\"Reporter\",\"Owner\"]}"), &newMember)
    require.Nil(t, err)
    require.True(t, reflect.DeepEqual([]Role{Reporter, Owner}, newMember.Roles))
})

EnumSet

api声明

// EnumSet Enumerate sets. Usually used for high-performance lookup when
// there are many instances of a certain type of enumeration.
type EnumSet[E EnumDefinition] interface {
    fmt.Stringer
    json.Marshaler
    // Add -Add an element to Set. If successful, return true. If it already exists, return false
    Add(e E) bool
    // AddRange According to the ordinal of the enumeration,
    // add a continuous section of the enumeration and
    // return the actual number added (excluding those that already exist)
    AddRange(begin, end E) int
    // Remove Delete element. If the deletion is successful, return true.
    // If the element does not exist, return false
    Remove(e E) bool
    // RemoveRange  According to the ordinal of the enumeration,
    // continuously delete a segment of the enumeration and
    // return the actual number of deletions  (excluding those that non-exist)
    RemoveRange(begin, end E) int
    // IsEmpty Is Set empty
    IsEmpty() bool
    // Clear -Clear set
    Clear()
    // Len The current number of enumeration instances within the Set
    Len() int
    // Contains Does it contain the specified enumeration?
    // Returns false if there is only one that does not exist in the Set
    Contains(enums ...E) bool
    // ContainsAll  Determine if it contains another enumSet (subset relationship)
    ContainsAll(set EnumSet[E]) bool
    // Equals Determine if two EnumSets are the same
    Equals(set EnumSet[E]) bool
    // Each Set iteration method, if f method returns false, abort iteration
    Each(f func(e E) bool)
    // Names Returns the Name representation of an existing enumeration instance in the set
    Names() []string
    // Clone Deep copy to obtain a new set
    Clone() EnumSet[E]
}

EnumSet usage

stmtSet := NewUnsafeEnumSet[Statement]()

stmtSet.Add(Decl)
stmtSet.IsEmpty()
stmtSet.Contains(Decl)
stmtSet.Len() == 1
stmtSet.AddRange(Comm, Range)
stmtSet.Remove(Decl)

Example code enum_set_test

ValueOf Performance

Don't worry about any performance issues, reflection calls are mostly only used in NewEnum methods, and other methods will try to avoid reflection calls as much as possible.

goos: linux
goarch: arm64
pkg: github.com/lvyahui8/goenum/internal
BenchmarkValueOf/ValueOf-4             1000000000             0.0002492 ns/op           0 B/op           0 allocs/op
BenchmarkValueOf/ValueOf-4             1000000000             0.0002966 ns/op           0 B/op           0 allocs/op
BenchmarkValueOf/ValueOf-4             1000000000             0.0002713 ns/op           0 B/op           0 allocs/op
BenchmarkValueOf/ValueOfIgnoreCase-4             1000000000             0.002228 ns/op           0 B/op           0 allocs/op
BenchmarkValueOf/ValueOfIgnoreCase-4             1000000000             0.002611 ns/op           0 B/op           0 allocs/op
BenchmarkValueOf/ValueOfIgnoreCase-4             1000000000             0.002606 ns/op           0 B/op           0 allocs/op
PASS
ok      github.com/lvyahui8/goenum/internal    0.097s

goenum's People

Contributors

lvyahui8 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

goenum's Issues

1、是否可以模拟支持 tagged union type?2、API 命名不合理

如下代码:

enum Option<T> {
    Some(T) | None
}

如果用这个库,目前似乎是写不出来的。

import "github.com/lvyahui8/goenum"
type Option struct {
	goenum.Enum
}
var (
	Some = goenum.NewEnum[Option]("Some", 0) // 能否让这里的 0 作为可以携带的任意(any)值?
	None = goenum.NewEnum[Option]("None")
)
func() {
    some := *goenum.Value[Option]("Some")
    fmt.Println(some) // output: 0
    goenum.Set[Option]("Some", "a")
    foo := *goenum.Value[Option]("Some")
    fmt.Println(foo)  // output: a
}

另外目前的 ValueOfValueOfIgnoreCaseValues返回的是枚举变体enum variant所以名称应更改为VariantOfVariantOfIgnoreCaseVariants
Value  tagged union type 指的是 Variant 所携带的值

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.