Categorygithub.com/xybor-x/enum
repositorypackage
1.4.0
Repository: https://github.com/xybor-x/enum.git
Documentation: pkg.go.dev

# README

Go Reference GitHub top language GitHub go.mod Go version GitHub release (release name instead of tag name) GitHub Repo stars

Golang

⚙️ Go Enum

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.

🔧 Installation

go get -u github.com/xybor-x/enum

⚡ Quick start

Define enums

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).

Usage

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"}
}

Nullable fields

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}
}

Integrate with protobuf

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
}

📈 Performance

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 generationxybor-x/enum
ToString6 ns17 ns
FromString15 ns22 ns
json.Marshal113 ns148 ns
json.Unmarshal147 ns144 ns
SQL Value29 ns38 ns
SQL Scan (bytes)29 ns41 ns
SQL Scan (string)15 ns22 ns