Enumer is a tool to generate Go code that adds useful methods to Go enums (constants with a specific type). It started as a fork of Rob Pike’s Stringer tool.
Enumer can be installed as any other go command:
go get github.com/alvaroloes/enumer
After that, the enumer executable will be in "$GOPATH/bin" folder and you can use it with go generate
When Enumer is applied to a type, it will generate:
-
The following basic methods/functions:
- Method
String(): returns the string representation of the enum value. This makes the enum conform theStringerinterface, so whenever you print an enum value, you'll get the string name instead of a number. - Function
<Type>String(s string): returns the enum value from its string representation. This is useful when you need to read enum values from command line arguments, from a configuration file, or from a REST API request... In short, from those places where using the real enum value (an integer) would be almost meaningless or hard to trace or use by a human. - Function
<Type>Values(): returns a slice with all the values of the enum - Method
IsA<Type>(): returns true only if the current value is among the values of the enum. Useful for validations.
- Method
-
When the flag
jsonis provided, two additional methods will be generated,MarshalJSON()andUnmarshalJSON(). These make the enum conform to thejson.Marshalerandjson.Unmarshalerinterfaces. Very useful to use it in JSON APIs. -
When the flag
jsonv2is provided, two additional methods will be generated,MarshalJSONTo()andUnmarshalJSONFrom(). These make the enum conform to theencoding/json/v2.MarshalerToandencoding/json/v2.UnmarshalerFrominterfaces. These are the streaming interfaces preferred byencoding/json/v2, so they are both faster and more flexible than the v1 methods. The generated code importsencoding/json/jsontext, which requires Go 1.27 or newer (or Go 1.25/1.26 built withGOEXPERIMENT=jsonv2). Thejsonandjsonv2flags are independent and can be combined;encoding/json/v2prefers the v2 methods when a type implements both.jsonv2does not implyjson: withjsonv2alone, noMarshalJSON()/UnmarshalJSON()methods are generated. You usually don't need them anyway, because on any toolchain where the generated code compiles, thejsonv2experiment is active and the v1encoding/jsonpackage is implemented on top of v2 — sojson.Marshalandjson.Unmarshalhonor the v2 methods and the enum still encodes as its string name. Addjsonas well when something needs the v1 methods themselves rather than the encoding behavior: a library that type-asserts tojson.Marshaler/json.Unmarshaler, ajson.Marshalerinterface constraint, or code that callsMarshalJSON()/UnmarshalJSON()directly.Decoding
nullmirrors what the v1 methods do:encoding/jsonunmarshalednullinto a string as a no-op that left the string empty, sonulldecodes to the value whose name is the empty string when theemptyoption gives the type one, and is an error otherwise. -
When the flag
textis provided, two additional methods will be generated,MarshalText()andUnmarshalText(). These make the enum conform to theencoding.TextMarshalerandencoding.TextUnmarshalerinterfaces. Note: If you use your enum values as keys in a map and you encode the map as JSON, you need this flag set to true to properly convert the map keys to json (strings). If not, the numeric values will be used instead -
When the flag
yamlis provided, two additional methods will be generated,MarshalYAML()andUnmarshalYAML(). These make the enum conform to thegopkg.in/yaml.v2.Marshalerandgopkg.in/yaml.v2.Unmarshalerinterfaces. -
When the flag
sqlis provided, the methods for implementing the Scanner and Valuer interfaces will be also generated. Useful when storing the enum in a database.
For example, if we have an enum type called Pill,
type Pill int
const (
Placebo Pill = iota
Aspirin
Ibuprofen
Paracetamol
Acetaminophen = Paracetamol
)executing enumer -type=Pill -json will generate a new file with four basic methods and two extra for JSON:
func (i Pill) String() string {
//...
}
func PillString(s string) (Pill, error) {
//...
}
func PillValues() []Pill {
//...
}
func (i Pill) IsAPill() bool {
//...
}
func (i Pill) MarshalJSON() ([]byte, error) {
//...
}
func (i *Pill) UnmarshalJSON(data []byte) error {
//...
}From now on, we can:
// Convert any Pill value to string
var aspirinString string = Aspirin.String()
// (or use it in any place where a Stringer is accepted)
fmt.Println("I need ", Paracetamol) // Will print "I need Paracetamol"
// Convert a string with the enum name to the corresponding enum value
pill, err := PillString("Ibuprofen")
if err != nil {
fmt.Println("Unrecognized pill: ", err)
return
}
// Now pill == Ibuprofen
// Get all the values of the string
allPills := PillValues()
fmt.Println(allPills) // Will print [Placebo Aspirin Ibuprofen Paracetamol]
// Check if a value belongs to the Pill enum values
var notAPill Pill = 42
if (notAPill.IsAPill()) {
fmt.Println(notAPill, "is not a value of the Pill enum")
}
// Marshal/unmarshal to/from json strings, either directly or automatically when
// the enum is a field of a struct
pillJSON := Aspirin.MarshalJSON()
// Now pillJSON == `"Aspirin"`The generated code is exactly the same as the Stringer tool plus the mentioned additions, so you can use Enumer where you are already using Stringer without any code change.
By default, Enumer uses the same name of the enum value for generating the string representation (usually CamelCase in Go).
type MyType int
...
name := MyTypeValue.String() // name => "MyTypeValue"Sometimes you need to use some other string representation format than CamelCase (i.e. in JSON).
For example, the command enumer -type=MyType -json -transform=snake would generate the following string representation:
name := MyTypeValue.String() // name => "my_type_value"Note: The transformation only works form CamelCase to snake_case or kebab-case, not the other way around.
The usage of Enumer is the same as Stringer, so you can refer to the Stringer docs for more information.
There are five boolean flags: json, jsonv2, text, yaml and sql. You can use any combination of them (i.e. enumer -type=Pill -json -text),
To transform the enum string representation the transform and trimprefix flags
were added (i.e. enumer -type=MyType -json -transform=snake).
The possible transform values are:
-
lower - mytypevalue
-
upper - MYTYPEVALUE
-
json - myTypeValue
The json transform is initialism-aware: within a run of uppercase letters, the last letter starts the next word and the letters before it form an initialism, so
BBGunbecomesbbGun,AWSAccessKeybecomesawsAccessKey, andMyBBGunbecomesmyBbGun. -
snake - my_type_value
-
snakeu - MY_TYPE_VALUE
-
kebab - my-type-value
-
kebabu - MY-TYPE-VALUE
The default value for transform flag is noop which means no transformation will be performed.
If a prefix is provided via the trimprefix flag, it will be trimmed from the start of each name (before
it is transformed). If a name doesn't have the prefix it will be passed unchanged.
The -ignorecase and -numeric flags allow more permissive conversions from a string to an enum value:
-
ignorecase
Ignores the case of the input string. This won't work if any of your enum names differ only by case.
-
numeric
In addition to converting from a name string, the conversion will also handle an input string that is a number matching the enum value. So the string "4" would translate to the enum that has the value 4.