Usage

Using goenums involves a simple workflow:

  1. Define your enum constants as you normally would with Go’s iota
  2. Add a go:generate directive to invoke goenums
  3. Run go generate to create the enum implementation
  4. Use the generated code in your project
$ goenums -h
   ____ _____  ___  ____  __  ______ ___  _____
  / __ '/ __ \/ _ \/ __ \/ / / / __ '__ \/ ___/
 / /_/ / /_/ /  __/ / / / /_/ / / / / / (__  ) 
 \__, /\____/\___/_/ /_/\__,_/_/ /_/ /_/____/  
/____/
Usage: goenums [options] file.go[,file2.go,...]
Options:
  -c
  -constraints
        Generate local numeric constraints instead of importing golang.org/x/exp/constraints (default: true)
  -x-exp-constraints
        Import golang.org/x/exp/constraints instead of generating local numeric constraints (default: false)
  -f
  -failfast
        Enable failfast mode - fail on generation of invalid enum while parsing (default: false)
  -h
  -help
        Print help information
  -i
  -insensitive
        Generate case insensitive string parsing (default: false)
  -interfaces string
        Generate only the listed interface handlers: json,text,yaml,sql,binary (default: all)
  -l
  -legacy
        Generate legacy code without Go 1.23+ iterator support (default: false)
  -legacy-text
        Generate legacy quoted MarshalText output (default: false)
  -o string
  -output string
        Specify the output format (default: go)
  -v
  -version
        Print version information
  -vv
  -verbose
        Enable verbose mode - prints out the generated code (default: false)

For users upgrading existing generated enums, see Migrating to goenums v0.6, including the -legacy-text compatibility option.

Adding a go:generate Directive

To use goenums, add a go:generate directive to your Go source file:

package validation

type status int

//go:generate goenums status.go
const (
    unknown   status = iota // invalid Unknown
    pending                 // Pending
    approved                // Approved
    rejected                // Rejected
    completed               // Completed
)

Running Code Generation

To generate the enum implementations, run the following command:

$ go generate ./...

This will create the enum implementations in the same directory as the source file.

Selecting generated interfaces

By default, goenums generates JSON, Text, YAML, SQL, and Binary interface methods. Use --interfaces to generate only specific interface implementations:

goenums --interfaces=json,sql status.go

Valid values are json, text, yaml, sql, and binary. If the flag is omitted, all interfaces are generated.

-legacy-text only affects generated Text handlers. If text is not selected, -legacy-text has no generated-code effect.

What Gets Generated

goenums will create a new file alongside your enum definition, named after the type. For example, a status type will generate a statuses_enums.go file in the same directory.

This file will contain:

  • A type-safe wrapper struct around your enum
  • A singleton container with all valid enum values
  • String conversion methods
  • Parsing functions for various input types (including numeric types)
  • JSON marshaling/unmarshaling
  • YAML marshaling/unmarshaling
  • Database scanning/valuing
  • Binary marshaling/unmarshaling
  • Text marshaling/unmarshaling
  • Validation functions
  • Iteration helpers
  • Compile-time validation
  • Compile-time exhaustiveness (Matcher interface, Match/MustMatch functions)

Using the Generated Code

After generating the enum implementations, you can use the generated code in your Go project.

// Import the package containing your enum
import "yourpackage/validation"

// Access enum values via the container
status := validation.Statuses.PASSED

// Convert to string
statusName := status.String() // "PASSED"

// Parse from string
parsed := validation.ParseStatus("FAILED")

// Check validity
if !parsed.IsValid() {
    // Handle invalid status
}

// JSON marshaling/unmarshaling
type Task struct {
    ID     int              `json:"id"`
    Status validation.Status `json:"status"`
}

// Use in exhaustive function (great for tests)
validation.ExhaustiveStatuses(func(status validation.Status) {
    // Process each status
    switch status {
    case validation.Statuses.PASSED:
        // Handle passed
    case validation.Statuses.FAILED:
        // Handle failed
    // ...handle other cases
    }
})

// Iterate using modern Go 1.23+ range-over-func
for status := range validation.Statuses.All() {
    fmt.Printf("Status: %s\n", status)
}
// Legacy iteration
for _, status := range validation.Statuses.All() {
    fmt.Printf("Status: %s\n", status)
}

Compile-time Exhaustiveness

goenums generates a Matcher interface and Match/MustMatch functions for each enum. The interface has one method per valid enum value. That means the Go compiler can enforce exhaustive handling for any matcher type you pass to Match or MustMatch.

type statusHandler struct{}

func (statusHandler) Pending()   {}
func (statusHandler) Approved()  {}
func (statusHandler) Rejected()  {}
func (statusHandler) Completed() {}

// Compile-time check: statusHandler must satisfy StatusMatcher.
// If a new enum value is added, this line fails to compile until the
// corresponding method is added to statusHandler.
var _ validation.StatusMatcher = statusHandler{}

func handleStatus(status validation.Status) error {
    // Match returns an error if the matcher is nil or the enum value is not handled.
    if err := validation.MatchStatus(status, statusHandler{}); err != nil {
        return err
    }

    // MustMatch panics instead of returning an error.
    validation.MustMatchStatus(status, statusHandler{})
    return nil
}

This is different from ExhaustiveStatuses, which iterates over every valid value. MatchStatus dispatches one enum value to a handler method, while the StatusMatcher interface gives compile-time coverage when enum values are added, removed, or renamed.

Here is some more Examples

Now you can use the generated status enums type in your code:

/// Access enum constants safely
myStatus := validation.Statuses.PASSED

// Convert to string
fmt.Println(myStatus.String()) // "PASSED"

// Parse from various sources
parsed, _ := validation.ParseStatus("SKIPPED")

// Validate enum values
if !parsed.IsValid() {
    fmt.Println("Invalid status")
}

Basic Command Syntax

goenums [options] file.go[,file2.go,...]

Where is the Go source file containing your enum definitions.

Adding a go:generate Directive

To use goenums, you need to add a go:generate directive to your Go source file.

package validation

type status int

//go:generate goenums status.go
const (
	unknown status = iota // invalid
	failed
	passed
	skipped
	scheduled
	running
	booked
)

Running Code Generation

To generate the enum implementations, run the following command:

go generate ./...

This will create the enum implementations in the same directory as the source file.

What Gets Generated

goenums will create a new file alongside your enum definition, named after the type. For example, a status type will generate a statuses_enums.go file in the same directory.

This file will contain:

  • A type-safe wrapper struct around your enum
  • A singleton container with all valid enum values
  • String conversion methods
  • Parsing functions for various input types (including numeric types)
  • JSON marshaling/unmarshaling
  • YAML marshaling/unmarshaling
  • Database scanning/valuing
  • Binary marshaling/unmarshaling
  • Text marshaling/unmarshaling
  • Validation functions
  • Iteration helpers
  • Compile-time validation
  • Compile-time exhaustiveness (Matcher interface, Match/MustMatch functions)

Using the Generated Code

After generating the enum implementations, you can use the generated code in your Go project.

// Import the package containing your enum
import "yourpackage/validation"

// Access enum values via the container
status := validation.Statuses.PASSED

// Convert to string
statusName := status.String() // "PASSED"

// Parse from string
parsed, err := validation.ParseStatus("FAILED")
if err != nil {
    // Handle error
}

// Check validity
if !parsed.IsValid() {
    // Handle invalid status
}

// JSON marshaling/unmarshaling
type Task struct {
    ID     int              `json:"id"`
    Status validation.Status `json:"status"`
}

// Use in exhaustive function (great for tests)
validation.ExhaustiveStatuses(func(status validation.Status) {
    // Process each status
    switch status {
    case validation.Statuses.PASSED:
        // Handle passed
    case validation.Statuses.FAILED:
        // Handle failed
    // ...handle other cases
    }
})

// Iterate using modern Go 1.23+ range-over-func
for status := range validation.Statuses.All() {
    fmt.Printf("Status: %s\n", status)
}

Here is some more Examples