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:
package input
type Color int
const (
Green Color = iota
Blue
Red
)
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.
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)
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)
}
package input
type Color int
const (
Green Color = iota
Blue
Red
)
package output
type Color string
const (
Green Color = "green"
Blue Color = "blue"
Red Color = "red"
)
// 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)
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
}
package input
type Color int
const (
Green Color = iota
Blue
Red
)
package output
type Color string
const (
Green Color = "green"
Blue Color = "blue"
Red Color = "red"
)
// 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)
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
}
package input
type Color int
const (
Green Color = iota
Blue
Red
)
package output
type Color string
const (
Green Color = "green"
Blue Color = "blue"
Red Color = "red"
)
// 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)
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
}
package input
type Color int
const (
Green Color = iota
Blue
)
package output
type Color string
const (
Unknown Color = "unknown"
Green Color = "green"
Blue Color = "blue"
)
// 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)
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
}
package input
type Color int
const (
Green Color = 1
Gray Color = 3
)
package output
type Color string
const (
Green Color = "green"
Grey Color = "Grey"
)
// 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)
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
}
package input
type Color int
const (
Green Color = 1
Gray Color = 3
)
package output
type Color string
const (
Green Color = "green"
)
// 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)
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"
)
// 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)
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
)
// 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
.