Elegant, powerful, and dependency-free enums for Go with zero code generation!
Tip
This is just a ⚡ quick tutorial for general use cases. See more advanced features at the documentation.
go get -u github.com/xybor-x/enum
package main
import "github.com/xybor-x/enum"
type role any
type Role = enum.WrapEnum[role]
const (
RoleUser Role = iota
RoleAdmin
)
func init() {
enum.Map(RoleUser, "user")
enum.Map(RoleAdmin, "admin")
enum.Finalize[Role]()
}
Caution
Enum definitions are not thread-safe. Therefore, they should be finalized during initialization (at the global scope).
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Role Role `json:"role"`
}
func main() {
// Print out the string representation of enum.
fmt.Println(RoleAdmin) // Output: admin
// Serialize a user to json.
user := User{Role: RoleUser}
data, _ := json.Marshal(user)
fmt.Println(string(data)) // Output: {"role": "user"}
}
package main
import (
"encoding/json"
"fmt"
)
// NullRole is similar to sql.NullXXX, designed to handle nullable SQL and JSON fields.
type NullRole = enum.Nullable[Role]
type User struct {
Role NullRole `json:"role"`
}
func main() {
// Serialize a nullable role with a non-null value.
user := User{Role: NullRole{Enum: RoleUser, Valid: true}}
data, _ := json.Marshal(user)
fmt.Println(string(data)) // Output: {"role": "user"}
// Serialize a nullable role with a null value.
data, _ = json.Marshal(User{})
fmt.Println(string(data)) // Output: {"role": null}
}
Refer to the Integration Guide for details.
Suppose we have a protobuf enum defined as follows:
// Code generated by protoc-gen-go.
package proto
type Role int32
const (
Role_User Role = 0
Role_Admin Role = 1
)
...
We can integrate them into xybor-x/enum
. Here's an example:
package main
import (
"path/to/proto"
"github.com/xybor-x/enum"
)
type Role = enum.WrapEnum[proto.Role]
const (
RoleUser Role = iota
RoleAdmin
)
func init() {
// Map the enum to protobuf enum value.
enum.Map(RoleUser, proto.Role_User)
enum.Map(RoleAdmin, proto.Role_Admin)
enum.Finalize[Role]()
}
func main() {
// Convert from the protobuf enum to the Role enum.
role, ok := enum.From[Role](proto.Role_User)
// ok == true && role == RoleUser
// Convert from the Role enum to the protobuf enum.
role = RoleAdmin.To()
// role == proto.Role_Admin
// The string representation of these enums is inherited from proto.Role.
fmt.Println(RoleUser) // Output: User
}
While it's true that the xybor-x/enum
approach will generally be slower than the code generation approaches, I still want to highlight the difference.
The benchmark results are based on defining an enum with 10 values at bench.
Code generation | xybor-x/enum |
|
---|---|---|
ToString | 6 ns | 17 ns |
FromString | 15 ns | 22 ns |
json.Marshal | 113 ns | 148 ns |
json.Unmarshal | 147 ns | 144 ns |
SQL Value | 29 ns | 38 ns |
SQL Scan (bytes) | 29 ns | 41 ns |
SQL Scan (string) | 15 ns | 22 ns |