go-enum

An enum generator for go
How it works
go-enum will take a commented type declaration like this:
type ImageType int
and generate a file with the iota definition along various optional niceties that you may need:
const (
ImageTypeJpeg ImageType = iota
ImageTypeJpg
ImageTypePng
ImageTypeTiff
ImageTypeGif
)
func (x ImageType) String() string
func ParseImageType(name string) (ImageType, error)
func (x ImageType) MarshalText() ([]byte, error)
func (x *ImageType) UnmarshalText(text []byte) error
Fear not the fact that the MarshalText
and UnmarshalText
are generated rather than JSON methods... they will still be utilized by the default JSON encoding methods.
If you find that the options given are not adequate for your use case, there is an option to add a custom template (-t
flag) to the processing engine so that your custom code can be created!
Now with string typed enums
type StrState string
const (
StrStatePending StrState = "pending"
StrStateRunning StrState = "running"
StrStateCompleted StrState = "completed"
StrStateFailed StrState = "failed"
)
If you would like to get integer values in sql, but strings elsewhere, you can assign an int value in the declaration
like always, and specify the --sqlint
flag. Those values will be then used to convey the int value to sql, while allowing you to use only strings elsewhere.
This might be helpful for things like swagger docs where you want the same type being used on the api layer, as you do in the
sql layer, and not have swagger assume that your enumerations are integers, but are in fact strings!
type StrState string
Goal
The goal of go-enum is to create an easy to use enum generator that will take a decorated type declaration like type EnumName int
and create the associated constant values and funcs that will make life a little easier for adding new values.
It's not perfect, but I think it's useful.
I took the output of the Stringer command as the String()
method, and added a way to parse a string value.
Docker image
You can now use a docker image directly for running the command if you do not wish to install anything!
docker run -w /app -v $(pwd):/app abice/go-enum:$(GO_ENUM_VERSION)
Installation
You can now download a release directly from github and use that for generating your enums! (Thanks to GoReleaser)
I did not specify any overrides on the release binary names, so uname -s
and uname -m
should provide the correct version of the binary for your distro.
curl -fsSL "https://github.com/abice/go-enum/releases/download/$(GO_ENUM_VERSION)/go-enum_$(uname -s)_$(uname -m)" -o go-enum
Adding it to your project
Using go generate
- Add a go:generate line to your file like so...
//go:generate go-enum --marshal
- Run go generate like so
go generate ./...
- Enjoy your newly created Enumeration!
Using Makefile
If you prefer makefile stuff, you can always do something like this:
STANDARD_ENUMS = ./example/animal_enum.go \
./example/color_enum.go
NULLABLE_ENUMS = ./example/sql_enum.go
$(STANDARD_ENUMS): GO_ENUM_FLAGS=--nocase --marshal --names --ptr
$(NULLABLE_ENUMS): GO_ENUM_FLAGS=--nocase --marshal --names --sqlnullint --ptr
enums: $(STANDARD_ENUMS) $(NULLABLE_ENUMS)
%_enum.go: %.go $(GOENUM) Makefile
$(GOENUM) -f $*.go $(GO_ENUM_FLAGS)
Command options
go-enum --help
NAME:
go-enum - An enum generator for go
USAGE:
go-enum [global options] [arguments...]
VERSION:
example
GLOBAL OPTIONS:
--file value, -f value [ --file value, -f value ] The file(s) to generate enums. Use more than one flag for more files. [$GOFILE]
--noprefix Prevents the constants generated from having the Enum as a prefix. (default: false)
--lower Adds lowercase variants of the enum strings for lookup. (default: false)
--nocase Adds case insensitive parsing to the enumeration (forces lower flag). (default: false)
--marshal Adds text (and inherently json) marshalling functions. (default: false)
--sql Adds SQL database scan and value functions. (default: false)
--sqlint Tells the generator that a string typed enum should be stored in sql as an integer value. (default: false)
--flag Adds golang flag functions. (default: false)
--prefix value Adds a prefix with a user one. If you would like to replace the prefix, then combine this option with --noprefix.
--names Generates a 'Names() []string' function, and adds the possible enum values in the error response during parsing (default: false)
--values Generates a 'Values() []{{ENUM}}' function. (default: false)
--nocamel Removes the snake_case to CamelCase name changing (default: false)
--ptr Adds a pointer method to get a pointer from const values (default: false)
--sqlnullint Adds a Null{{ENUM}} type for marshalling a nullable int value to sql (default: false)
--sqlnullstr Adds a Null{{ENUM}} type for marshalling a nullable string value to sql. If sqlnullint is specified too, it will be Null{{ENUM}}Str (default: false)
--template value, -t value [ --template value, -t value ] Additional template file(s) to generate enums. Use more than one flag for more files. Templates will be executed in alphabetical order.
--alias value, -a value [ --alias value, -a value ] Adds or replaces aliases for a non alphanumeric value that needs to be accounted for. [Format should be "key:value,key2:value2", or specify multiple entries, or both!]
--mustparse Adds a Must version of the Parse that will panic on failure. (default: false)
--forcelower Forces a camel cased comment to generate lowercased names. (default: false)
--forceupper Forces a camel cased comment to generate uppercased names. (default: false)
--nocomments Removes auto generated comments. If you add your own comments, these will still be created. (default: false)
--buildtag value, -b value [ --buildtag value, -b value ] Adds build tags to a generated enum file.
--help, -h show help
--version, -v print the version
Syntax
The parser looks for comments on your type defs and parse the enum declarations from it.
The parser will look for ENUM(
and continue to look for comma separated values until it finds a )
. You can put values on the same line, or on multiple lines.
If you need to have a specific value jump in the enum, you can now specify that by adding =numericValue
to the enum declaration. Keep in mind, this resets the data for all following values. So if you specify 50
in the middle of an enum, each value after that will be 51, 52, 53...
Examples can be found in the example folder
You can use comments inside enum that start with //
The comment must be at the end of the same line as the comment value, only then it will be added as a comment to the generated constant.
type Commented int
The generated comments in code will look something like:
...
const (
CommentedValue1 Commented = iota
CommentedValue2
CommentedValue3
)
...
Example
There are a few examples in the example
directory.
I've included one here for easy access, but can't guarantee it's up to date.
type Color int32
The generated code will look something like:
package example
import (
"fmt"
"strings"
)
const (
ColorBlack Color = iota
ColorWhite
ColorRed
ColorGreen Color = iota + 30
ColorBlue
ColorGrey
ColorYellow
ColorBlueGreen
ColorRedOrange
ColorYellowGreen
ColorRedOrangeBlue
)
const _ColorName = "BlackWhiteRedGreenBluegreyyellowblue-greenred-orangeyellow_greenred-orange-blue"
var _ColorMap = map[Color]string{
ColorBlack: _ColorName[0:5],
ColorWhite: _ColorName[5:10],
ColorRed: _ColorName[10:13],
ColorGreen: _ColorName[13:18],
ColorBlue: _ColorName[18:22],
ColorGrey: _ColorName[22:26],
ColorYellow: _ColorName[26:32],
ColorBlueGreen: _ColorName[32:42],
ColorRedOrange: _ColorName[42:52],
ColorYellowGreen: _ColorName[52:64],
ColorRedOrangeBlue: _ColorName[64:79],
}
func (x Color) String() string {
if str, ok := _ColorMap[x]; ok {
return str
}
return fmt.Sprintf("Color(%d)", x)
}
var _ColorValue = map[string]Color{
_ColorName[0:5]: ColorBlack,
strings.ToLower(_ColorName[0:5]): ColorBlack,
_ColorName[5:10]: ColorWhite,
strings.ToLower(_ColorName[5:10]): ColorWhite,
_ColorName[10:13]: ColorRed,
strings.ToLower(_ColorName[10:13]): ColorRed,
_ColorName[13:18]: ColorGreen,
strings.ToLower(_ColorName[13:18]): ColorGreen,
_ColorName[18:22]: ColorBlue,
strings.ToLower(_ColorName[18:22]): ColorBlue,
_ColorName[22:26]: ColorGrey,
strings.ToLower(_ColorName[22:26]): ColorGrey,
_ColorName[26:32]: ColorYellow,
strings.ToLower(_ColorName[26:32]): ColorYellow,
_ColorName[32:42]: ColorBlueGreen,
strings.ToLower(_ColorName[32:42]): ColorBlueGreen,
_ColorName[42:52]: ColorRedOrange,
strings.ToLower(_ColorName[42:52]): ColorRedOrange,
_ColorName[52:64]: ColorYellowGreen,
strings.ToLower(_ColorName[52:64]): ColorYellowGreen,
_ColorName[64:79]: ColorRedOrangeBlue,
strings.ToLower(_ColorName[64:79]): ColorRedOrangeBlue,
}
func ParseColor(name string) (Color, error) {
if x, ok := _ColorValue[name]; ok {
return x, nil
}
return Color(0), fmt.Errorf("%s is not a valid Color", name)
}
func (x Color) Ptr() *Color {
return &x
}
func (x Color) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
func (x *Color) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParseColor(name)
if err != nil {
return err
}
*x = tmp
return nil
}