Skip to content

How to convert enums

Definition

The Go language doesn't explicit define enums. In the context of Goverter an enum type is defined as a named type with an underlying type of (float, string, or integer) having at least one constant defined in the same package.

These examples would qualify as enums:

go
package input

type Color int
const (
    Green Color = iota
    Blue
    Red
)
go
package output

type Color string
const (
    Green Color = "green"
    Blue  Color = "blue"
    Red   Color = "red"
)

Conversion

When goverter sees types which both qualify as enum according to the definition above then it tries to convert each key of the source enum to the target enum. When not all keys of the source enum can be mapped, goverter will error.

Unknown enum values

In Go it's possible to manually instantiate values of types qualifying as Enum. E.g.

go
func GetColor() Color { return Color("invalid") }

type Color string
const Green Color = "green"
const Blue  Color = "blue"

it's not clear how to handle the Color("invalid"), therefore you have to explicitly configure how to convert an invalid enum value. You can do this by configuring enum:unknown with one of these actions.

enum:unknown @error: return an error when an invalid value is encountered.

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/unknown/input"
	"github.com/jmattheis/goverter/example/enum/unknown/output"
)

// goverter:converter
// goverter:enum:unknown @error
type Converter interface {
	Convert(input.Color) (output.Color, error)
}
go
package input

type Color int
const (
    Green Color = iota
    Blue
    Red
)
go
package output

type Color string
const (
    Green Color = "green"
    Blue  Color = "blue"
    Red   Color = "red"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	"fmt"
	input "github.com/jmattheis/goverter/example/enum/unknown/input"
	output "github.com/jmattheis/goverter/example/enum/unknown/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) (output.Color, error) {
	var outputColor output.Color
	switch source {
	case input.Blue:
		outputColor = output.Blue
	case input.Green:
		outputColor = output.Green
	case input.Red:
		outputColor = output.Red
	default:
		return outputColor, fmt.Errorf("unexpected enum element: %v", source)
	}
	return outputColor, nil
}

enum:unknown @ignore: ignore invalid values and return the zero value of the enum type.

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/unknown/input"
	"github.com/jmattheis/goverter/example/enum/unknown/output"
)

// goverter:converter
// goverter:enum:unknown @ignore
type Converter interface {
	Convert(input.Color) output.Color
}
go
package input

type Color int
const (
    Green Color = iota
    Blue
    Red
)
go
package output

type Color string
const (
    Green Color = "green"
    Blue  Color = "blue"
    Red   Color = "red"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	input "github.com/jmattheis/goverter/example/enum/unknown/input"
	output "github.com/jmattheis/goverter/example/enum/unknown/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) output.Color {
	var outputColor output.Color
	switch source {
	case input.Blue:
		outputColor = output.Blue
	case input.Green:
		outputColor = output.Green
	case input.Red:
		outputColor = output.Red
	default: // ignored
	}
	return outputColor
}

enum:unknown @panic: panic when an invalid value is encountered

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/unknown/input"
	"github.com/jmattheis/goverter/example/enum/unknown/output"
)

// goverter:converter
// goverter:enum:unknown @panic
type Converter interface {
	Convert(input.Color) output.Color
}
go
package input

type Color int
const (
    Green Color = iota
    Blue
    Red
)
go
package output

type Color string
const (
    Green Color = "green"
    Blue  Color = "blue"
    Red   Color = "red"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	"fmt"
	input "github.com/jmattheis/goverter/example/enum/unknown/input"
	output "github.com/jmattheis/goverter/example/enum/unknown/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) output.Color {
	var outputColor output.Color
	switch source {
	case input.Blue:
		outputColor = output.Blue
	case input.Green:
		outputColor = output.Green
	case input.Red:
		outputColor = output.Red
	default:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	}
	return outputColor
}

enum:unknown KEY: use an enum key when an invalid value in encountered

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/unknown/key/input"
	"github.com/jmattheis/goverter/example/enum/unknown/key/output"
)

// goverter:converter
type Converter interface {
    // goverter:enum:unknown Unknown
    Convert(input.Color) output.Color
}
go
package input

type Color int

const (
    Green Color = iota
    Blue
)
go
package output

type Color string
const (
    Unknown Color = "unknown"
    Green   Color = "green"
    Blue    Color = "blue"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	input "github.com/jmattheis/goverter/example/enum/unknown/key/input"
	output "github.com/jmattheis/goverter/example/enum/unknown/key/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) output.Color {
	var outputColor output.Color
	switch source {
	case input.Blue:
		outputColor = output.Blue
	case input.Green:
		outputColor = output.Green
	default:
		outputColor = output.Unknown
	}
	return outputColor
}

Mapping enum keys

If your source and target enum have differently named keys, you can use enum:map to define the mapping.

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/map/input"
	"github.com/jmattheis/goverter/example/enum/map/output"
)

// goverter:converter
// goverter:enum:unknown @panic
type Converter interface {
    // goverter:enum:map Gray Grey
    Convert(input.Color) output.Color
}
go
package input

type Color int

const (
    Green Color = 1
    Gray  Color = 3
)
go
package output

type Color string
const (
    Green Color = "green"
    Grey  Color = "Grey"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	"fmt"
	input "github.com/jmattheis/goverter/example/enum/map/input"
	output "github.com/jmattheis/goverter/example/enum/map/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) output.Color {
	var outputColor output.Color
	switch source {
	case input.Gray:
		outputColor = output.Grey
	case input.Green:
		outputColor = output.Green
	default:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	}
	return outputColor
}

You can also use any of the @actions from enum:unknown. E.g.

Example (click me)
go
package example

import (
	"github.com/jmattheis/goverter/example/enum/map-panic/input"
	"github.com/jmattheis/goverter/example/enum/map-panic/output"
)

// goverter:converter
// goverter:enum:unknown @panic
type Converter interface {
    // goverter:enum:map Gray @panic
    Convert(input.Color) output.Color
}
go
package input

type Color int

const (
    Green Color = 1
    Gray  Color = 3
)
go
package output

type Color string
const (
    Green Color = "green"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	"fmt"
	input "github.com/jmattheis/goverter/example/enum/map-panic/input"
	output "github.com/jmattheis/goverter/example/enum/map-panic/output"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source input.Color) output.Color {
	var outputColor output.Color
	switch source {
	case input.Gray:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	case input.Green:
		outputColor = output.Green
	default:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	}
	return outputColor
}

If you have keys that differ in the same format you can use the transformer enum:transform regex to do a regex search and replace.

Example (click me)
go
package example

// goverter:converter
// goverter:enum:unknown @panic
type Converter interface {
	// goverter:enum:transform regex Col(\w+) ${1}Color
	Convert(InputColor) OutputColor
	// goverter:enum:transform regex (\w+)Color Col${1}
	Convert2(OutputColor) InputColor
}

type InputColor int

const (
	ColGreen InputColor = iota
	ColBlue
	ColRed
)

type OutputColor string

const (
	GreenColor OutputColor = "green"
	BlueColor  OutputColor = "blue"
	RedColor   OutputColor = "red"
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	"fmt"
	transformregex "github.com/jmattheis/goverter/example/enum/transform-regex"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source transformregex.InputColor) transformregex.OutputColor {
	var exampleOutputColor transformregex.OutputColor
	switch source {
	case transformregex.ColBlue:
		exampleOutputColor = transformregex.BlueColor
	case transformregex.ColGreen:
		exampleOutputColor = transformregex.GreenColor
	case transformregex.ColRed:
		exampleOutputColor = transformregex.RedColor
	default:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	}
	return exampleOutputColor
}
func (c *ConverterImpl) Convert2(source transformregex.OutputColor) transformregex.InputColor {
	var exampleInputColor transformregex.InputColor
	switch source {
	case transformregex.BlueColor:
		exampleInputColor = transformregex.ColBlue
	case transformregex.GreenColor:
		exampleInputColor = transformregex.ColGreen
	case transformregex.RedColor:
		exampleInputColor = transformregex.ColRed
	default:
		panic(fmt.Sprintf("unexpected enum element: %v", source))
	}
	return exampleInputColor
}

If that's still not powerful enough for you, then you can define a custom enum transformer

Disable enum detection and conversion

To disable enum detection and conversion, add enum no to the converter or a method converting enums.

Example (click me)
go
package example

import (
	"time"
)

// goverter:converter
// goverter:enum no
type Converter interface {
	Convert(MyDuration) time.Duration
}

type MyDuration int64

const (
	Nanoseconds  MyDuration = 1
	Microseconds MyDuration = 1000 * Nanoseconds
)
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	disable "github.com/jmattheis/goverter/example/enum/disable"
	"time"
)

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source disable.MyDuration) time.Duration {
	return time.Duration(source)
}

Handle false positives

You can exclude wrongly detected enums via enum:exclude.