Categorygithub.com/xybor-x/enum
modulepackage
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

# Functions

All returns a slice containing all enum values of a specific type.
Finalize prevents the creation of any new enum values for the current type.
From returns the corresponding enum for a given representation, and whether it is valid.
FromInt returns the corresponding enum for a given int representation, and whether it is valid.
FromNumber returns the corresponding enum for a given number representation, and whether it is valid.
FromString returns the corresponding enum for a given string representation, and whether it is valid.
IsValid checks if an enum value is valid.
Map associates an enum with its representations under strict rules: - String enums map to themselves as the string representation; Stringer is also treated as a string representation if no string repr is found.
MarshalJSON serializes an enum value into its string representation.
MarshalXML converts enum to its string representation.
MarshalYAML serializes an enum value into its string representation.
MustFrom returns the corresponding enum for a given representation.
MustFromInt returns the corresponding enum for a given int representation.
MustFromNumber returns the corresponding enum for a given number representation.
MustFromString returns the corresponding enum for a given string representation.
MustTo returns the representation (the type is relied on P type parameter) for the given enum value.
NameOf returns the name of the enum type.
New creates a dynamic enum value then mapped to its representations.
NewExtended initializes an extended enum then mapped to its representations.
ScanSQL deserializes a database value into an enum type.
To returns the representation (the type is relied on P type parameter) for the given enum value.
ToInt returns the int representation for the given enum value.
ToString returns the string representation of the given enum value.
TrueNameOf returns the name of the enum type.
UnmarshalJSON deserializes a string representation of an enum value from JSON.
UnmarshalXML parses the string representation back into an enum.
UnmarshalYAML deserializes a string representation of an enum value from YAML.
ValueSQL serializes an enum into a database-compatible format.

# Structs

Nullable allows handling nullable enums in JSON, YAML, and SQL.
SafeEnum defines a strong type-safe enum.

# Type aliases

WrapEnum provides a set of built-in methods to simplify working with int enums.
WrapFloatEnum provides a set of built-in methods to simplify working with float64 enums.
WrapUintEnum provides a set of built-in methods to simplify working with uint enums.