An enum generator for go

go-enum

CircleCI Go Report Card Coverage Status GoDoc Mentioned in Awesome Go

An enum generator for go

How it works

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.

Command options

go-enum --help
Options:

  -h, --help       display help information
  -f, --file      *The file(s) to generate enums.  Use more than one flag for more files.
      --noprefix   Prevents the constants generated from having the Enum as a prefix.
      --lower      Adds lowercase variants of the enum strings for lookup.
      --marshal    Adds text (and inherently json) marshalling functions.
      --sql        Adds SQL database scan and value functions.
      --flag       Adds golang flag functions.
      --prefix     Replaces the prefix with a user one.
      --names      Generates a 'Names() []string' function, and adds the possible enum values in the error response during parsing
      --nocamel    Removes the snake_case to CamelCase name changing

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...

Comments

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.

// Commented is an enumeration of commented values
/*
ENUM(
value1 // Commented value 1
value2
value3 // Commented value 3
)
*/
type Commented int

The generated comments in code will look something like:

...
const (
    // CommentedValue1 is a Commented of type Value1
    // Commented value 1
    CommentedValue1 Commented = iota
    // CommentedValue2 is a Commented of type Value2
    CommentedValue2
    // CommentedValue3 is a Commented of type Value3
    // Commented value 3
    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.

// Color is an enumeration of colors that are allowed.
/* ENUM(
Black, White, Red
Green = 33 // Green starts with 33
*/
// Blue
// grey=
// yellow
// blue-green
// red-orange
// yellow_green
// red-orange-blue
// )
type Color int32

The generated code will look something like:

// Code generated by go-enum
// DO NOT EDIT!

package example

import (
    "fmt"
    "strings"
)

const (
    // ColorBlack is a Color of type Black
    ColorBlack Color = iota
    // ColorWhite is a Color of type White
    ColorWhite
    // ColorRed is a Color of type Red
    ColorRed
    // ColorGreen is a Color of type Green
    // Green starts with 33
    ColorGreen Color = iota + 30
    // ColorBlue is a Color of type Blue
    ColorBlue
    // ColorGrey is a Color of type Grey
    ColorGrey
    // ColorYellow is a Color of type Yellow
    ColorYellow
    // ColorBlueGreen is a Color of type Blue-Green
    ColorBlueGreen
    // ColorRedOrange is a Color of type Red-Orange
    ColorRedOrange
    // ColorYellowGreen is a Color of type Yellow_green
    ColorYellowGreen
    // ColorRedOrangeBlue is a Color of type Red-Orange-Blue
    ColorRedOrangeBlue
)

const _ColorName = "BlackWhiteRedGreenBluegreyyellowblue-greenred-orangeyellow_greenred-orange-blue"

var _ColorMap = map[Color]string{
    0:  _ColorName[0:5],
    1:  _ColorName[5:10],
    2:  _ColorName[10:13],
    33: _ColorName[13:18],
    34: _ColorName[18:22],
    35: _ColorName[22:26],
    36: _ColorName[26:32],
    37: _ColorName[32:42],
    38: _ColorName[42:52],
    39: _ColorName[52:64],
    40: _ColorName[64:79],
}

// String implements the Stringer interface.
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]:                    0,
    strings.ToLower(_ColorName[0:5]):   0,
    _ColorName[5:10]:                   1,
    strings.ToLower(_ColorName[5:10]):  1,
    _ColorName[10:13]:                  2,
    strings.ToLower(_ColorName[10:13]): 2,
    _ColorName[13:18]:                  33,
    strings.ToLower(_ColorName[13:18]): 33,
    _ColorName[18:22]:                  34,
    strings.ToLower(_ColorName[18:22]): 34,
    _ColorName[22:26]:                  35,
    strings.ToLower(_ColorName[22:26]): 35,
    _ColorName[26:32]:                  36,
    strings.ToLower(_ColorName[26:32]): 36,
    _ColorName[32:42]:                  37,
    strings.ToLower(_ColorName[32:42]): 37,
    _ColorName[42:52]:                  38,
    strings.ToLower(_ColorName[42:52]): 38,
    _ColorName[52:64]:                  39,
    strings.ToLower(_ColorName[52:64]): 39,
    _ColorName[64:79]:                  40,
    strings.ToLower(_ColorName[64:79]): 40,
}

// ParseColor attempts to convert a string to a Color
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)
}

// MarshalText implements the text marshaller method
func (x Color) MarshalText() ([]byte, error) {
    return []byte(x.String()), nil
}

// UnmarshalText implements the text unmarshaller method
func (x *Color) UnmarshalText(text []byte) error {
    name := string(text)
    tmp, err := ParseColor(name)
    if err != nil {
        return err
    }
    *x = tmp
    return nil
}

Adding it to your project

  1. go get github.com/abice/go-enum
  2. Add a go:generate line to your file like so... //go:generate go-enum -f=$GOFILE --marshal
  3. Run go generate like so go generate ./...
  4. Enjoy your newly created Enumeration
Comments
  • Question: How to support camel case format

    Question: How to support camel case format

    How do I define ENUM to achieve this?

    1. The text in name is in lower case
    2. Int values in enumeration are case sensitive
    const(
    xxxDataSwap = iota
    xxxBootNode
    )
    
    __Name="dataswapbootnode"
    

    Also, can I ignore Parse's error like this:

    ParseXXX(name)Type{
        //return a default value
    }
    
  • Fix deprecated function use, ignored return

    Fix deprecated function use, ignored return

    Hi, I saw some warnings in my IDE while browsing the code, and I thought why not submit some fix for it.

    One thing I didn't fix because it broke some of the tests was strings.Title.

    These are the tests which failed. IMO, the cases.Title is probably more correct than the strings.Title. I tested with the recommended cases.Title(language.Und, cases.NoLower).String().

        generator_test.go:61: snapshot not equal:
            --- Previous
            +++ Current
            @@ -1739,8 +1739,8 @@
               (string) (len=22) "\tSanitizingHyphenStart",
            -  (string) (len=72) "\t// Sanitizing_underscoreFirst is a Sanitizing of type _underscoreFirst.",
            -  (string) (len=27) "\tSanitizing_underscoreFirst",
            -  (string) (len=64) "\t// Sanitizing0numberFirst is a Sanitizing of type 0numberFirst.",
            -  (string) (len=23) "\tSanitizing0numberFirst",
            -  (string) (len=60) "\t// Sanitizing123456789a is a Sanitizing of type 123456789a.",
            -  (string) (len=21) "\tSanitizing123456789a",
            +  (string) (len=72) "\t// Sanitizing_UnderscoreFirst is a Sanitizing of type _UnderscoreFirst.",
            +  (string) (len=27) "\tSanitizing_UnderscoreFirst",
            +  (string) (len=64) "\t// Sanitizing0NumberFirst is a Sanitizing of type 0NumberFirst.",
            +  (string) (len=23) "\tSanitizing0NumberFirst",
            +  (string) (len=60) "\t// Sanitizing123456789A is a Sanitizing of type 123456789A.",
            +  (string) (len=21) "\tSanitizing123456789A",
               (string) (len=61) "\t// Sanitizing123123Asdf is a Sanitizing of type 123123-Asdf.",
            @@ -1773,5 +1773,5 @@
               (string) (len=52) "\tSanitizingHyphenStart:      _SanitizingName[11:23],",
            -  (string) (len=52) "\tSanitizing_underscoreFirst: _SanitizingName[23:39],",
            -  (string) (len=52) "\tSanitizing0numberFirst:     _SanitizingName[39:51],",
            -  (string) (len=52) "\tSanitizing123456789a:       _SanitizingName[51:61],",
            +  (string) (len=52) "\tSanitizing_UnderscoreFirst: _SanitizingName[23:39],",
            +  (string) (len=52) "\tSanitizing0NumberFirst:     _SanitizingName[39:51],",
            +  (string) (len=52) "\tSanitizing123456789A:       _SanitizingName[51:61],",
               (string) (len=52) "\tSanitizing123123Asdf:       _SanitizingName[61:72],",
            @@ -1793,8 +1793,8 @@
               (string) (len=64) "\tstrings.ToLower(_SanitizingName[11:23]): SanitizingHyphenStart,",
            -  (string) (len=69) "\t_SanitizingName[23:39]:                  Sanitizing_underscoreFirst,",
            -  (string) (len=69) "\tstrings.ToLower(_SanitizingName[23:39]): Sanitizing_underscoreFirst,",
            -  (string) (len=65) "\t_SanitizingName[39:51]:                  Sanitizing0numberFirst,",
            -  (string) (len=65) "\tstrings.ToLower(_SanitizingName[39:51]): Sanitizing0numberFirst,",
            -  (string) (len=63) "\t_SanitizingName[51:61]:                  Sanitizing123456789a,",
            -  (string) (len=63) "\tstrings.ToLower(_SanitizingName[51:61]): Sanitizing123456789a,",
            +  (string) (len=69) "\t_SanitizingName[23:39]:                  Sanitizing_UnderscoreFirst,",
            +  (string) (len=69) "\tstrings.ToLower(_SanitizingName[23:39]): Sanitizing_UnderscoreFirst,",
            +  (string) (len=65) "\t_SanitizingName[39:51]:                  Sanitizing0NumberFirst,",
            +  (string) (len=65) "\tstrings.ToLower(_SanitizingName[39:51]): Sanitizing0NumberFirst,",
            +  (string) (len=63) "\t_SanitizingName[51:61]:                  Sanitizing123456789A,",
            +  (string) (len=63) "\tstrings.ToLower(_SanitizingName[51:61]): Sanitizing123456789A,",
               (string) (len=63) "\t_SanitizingName[61:72]:                  Sanitizing123123Asdf,",
    
  • Custom prefix with `--prefix` is appending not replacing

    Custom prefix with `--prefix` is appending not replacing

    I'm using go-enum v0.5.0 Linux x86_64 I've created an enum as such:

    //go:generate go-enum -f=$GOFILE --marshal --names --nocamel --prefix TokenTypeEnum_
    package tokens
    /*
    ENUM(
    	AD_HOC_USER = 1
    	APP_USER
    	OTHER
    	RFID
    )
    */
    type TokenTypeEnum uint8
    

    By default, the --nocamel flag gives me TokenTypeEnum<name> but I want the constants to be named TokenTypeEnum_<name> for readability but instead I'm getting TokenTypeEnum_TokenTypeEnum<name>

    The README suggests the --prefix flag should be replacing the existing prefix but it's not. --noprefix removes both prefixes

  • Add snapshot testing for generated output

    Add snapshot testing for generated output

    I've recently written a go library (github.com/bradleyjkemp/cupaloy) to test exactly this sort of project!

    I see you already have a test input that you generate an enum for. This PR adds a snapshot test to check that the generated output doesn't change over time (if you're expecting the output to change as a result of a PR then you simply update the stored snapshot by setting the environment variable UPDATE_SNASPHOTS before running your tests)

  • Text after ) will be included in the last enum value

    Text after ) will be included in the last enum value

    When adhering to godot linter rules, I must add a trailing . to the end of the ENUM(...). But this will result in ). to be added to the string name:

    // ENUM(1).
    type Test int8
    

    generates

    const (
    	// Test1 is a Test of type 1)..
    	Test1 Test = iota
    )
    
    const _TestName = "1)."
    

    But even worse, on a multiline variant, it will add an empty item, colliding with the type:

    // ENUM(
    //   X
    //   Y
    //   Z
    // ).
    type Test int8
    

    generates

    const (
    	// TestX is a Test of type X.
    	TestX Test = iota
    	// TestY is a Test of type Y.
    	TestY
    	// TestZ is a Test of type Z.
    	TestZ
    	// Test is a Test of type )..
    	Test
    )
    
    const _TestName = "XYZ)."
    

    I tried (to no avail) to disable godot for the ENUM line, since I find it a bit pointless. However, this does mean that it's also not possible to add some text behind the closing ) which may be useful. Especially considering ENUM(...values...) kinda implies that parsing stops at )

  • Generates `foo_test_enum.go` from `foo_test.go` instead of `foo_enum_test.go`

    Generates `foo_test_enum.go` from `foo_test.go` instead of `foo_enum_test.go`

    When I have a file foos_test.go with:

    //go:generate go-enum --marshal
    package trygoenum
    
    // ENUM(bob, alice, john, sally)
    type People int
    

    and I run go generate ./... go-enum creates foos_test_enum.go.

    This causes go build ./... to fail:

    $ go build ./...
    # try-go-enum.invalid
    ./foos_test_enum.go:16:12: undefined: People
    ./foos_test_enum.go:29:22: undefined: People
    ./foos_test_enum.go:37:9: undefined: People
    ./foos_test_enum.go:44:31: undefined: People
    ./foos_test_enum.go:52:32: undefined: People
    ./foos_test_enum.go:56:9: undefined: People
    ./foos_test_enum.go:60:9: undefined: People
    ./foos_test_enum.go:65:10: undefined: People
    

    I would say ideally go-enum should put enums for _test.go files in _enum_test.go files.

  • Getting stringer error

    Getting stringer error

    Just tonight I am getting errors similar to Failed writing enum data for enum: "AssociationType": template: generator:306:32: executing "stringer" at : wrong number of args for stringify: want 2 got 1

    go 1.15 go-enum 0.3.10

    My Makefile simply states

    go get -u github.com/abice/go-enum
    go generate ./...
    

    That generates the errors as it is using V0.3.10

    if I do

    go get -u github.com/abice/[email protected]
    go generate ./...
    

    Then it all works as expected

  • Mapping enums to aliases/symbols

    Mapping enums to aliases/symbols

    I have a lot of enums which need to be mapped to arbitrary strings that don't match the enum name. Am I missing a good way to handle this in the library already?

    Would you be open to a PR adding optional mapping to symbols/aliases?

    var _{{.enum.Name}}Symbols = map[string]{{.enum.Name}}{
    {{- $vars := dict "lastoffset" 0 -}}
    {{ range $rIndex, $value := .enum.Values }}
    	{{- $lastOffset := pluck "lastoffset" $vars | first }}{{ $offset := sub $value.Value $rIndex }}
    	{{- if $value.Comment}}
    	"{{$value.Comment}}": {{$rIndex}},
    	{{- end}}
    {{- end}}
    }
    
    func Parse{{.enum.Name}}Symbol(bs []byte) ({{.enum.Name}}, error) {
        s := string(bs)
    	if s == " " {
    		s = "SPACE"
    	}
    	if val, ok := _{{.enum.Name}}Symbols[s]; ok {
    		return val, nil
    	}
    	return {{.enum.Name}}(0), fmt.Errorf("'%s' is not a valid {{.enum.Name}}", string(bs))
    }
    
    /*
    ENUM(
    AlphaNumeric // A/N
    Numeric // N
    Misc // SPACE
    )
    */
    type FieldType int
    
    var _FieldTypeSymbols = map[string]FieldType{
    	"A/N":   0,
    	"N":     1,
            "SPACE": 2,
    }
    
    func ParseFieldTypeSymbol(bs []byte) (FieldType, error) {
    	s := string(bs)
    	if s == " " {
    		s = "SPACE"
    	}
    	if val, ok := _FieldTypeSymbols[s]; ok {
    		return val, nil
    	}
    	return FieldType(0), fmt.Errorf("'%s' is not a valid FieldType", string(bs))
    }
    
  • Support interface for flag

    Support interface for flag

    Thanks for this handy tool. Do you think it is reasonable to implement the following interface so that it can be used as a Flag.

    // Value is the interface to the dynamic value stored in a flag.
    // (The default value is represented as a string.)
    type Value interface {
    	String() string
    	Set(string) error
    	Type() string
    }
    

    Currently I am manually implementing this.

  • Please check in your dependencies

    Please check in your dependencies

    Your package is not go-get-able if you don't check in your vendor directory. This is what happens right now if I try to go get your package:

    go get github.com/abice/go-enum
    # github.com/Masterminds/sprig
    ../../Masterminds/sprig/crypto.go:35:37: multiple-value uuid.NewV4() in single-value context
    
  • rename error from _XXXErrNilPtr to errXXXNilPtr.

    rename error from _XXXErrNilPtr to errXXXNilPtr.

    Please see https://staticcheck.io/docs/checks#ST1012

    When I use --sql to generate my enum files, go-enum add the _XXXErrNilPtr ERROR type.

    But when I use staticcheck to validate code, it tells me to try to use errXXXNilPtr instead of some other kind of naming rule.

    Can we do that?

  • Function for getting a list of values

    Function for getting a list of values

    --names adds ...Names() to get a list of enum names, but I would like to just get a list of enum values instead. I can kind of achieve the same thing by using MustParse...() with values from ...Names() but I would still be more comfortable just having ...Values().

    Mainly making this issue to see how you feel about the request or if there may already be a way to do it that I don't know of.

  • Use a config file instead of code for enum definitino

    Use a config file instead of code for enum definitino

    Hi. I like the project. I'm wondering if you have considered or would like to have a contribution where the enum definitions are in a config file instead of in a code stub?

    I think it would be quite interesting to have them defined in something similar to how go-swagger or sqlc does it.

    An idea of a possible configuration file:

    version: 1
    enums:
      - package: animal
        target: internal/animal
        name: Animal
        type: int
        alias:
          "+": Plus
          "#": Sharp
        values:
          cat:
          dog:
          fish:
          fish++:
          fish#:
      - package: color
        target: internal/color
        name: Color
        type: int
        values:
          Black:
          White:
          Red:
          Green:
            - value: 33
              comment: Green starts with 33
          Blue:
          grey:
          yellow:
          blue-green:
          red-orange:
          yellow_green:
          red-orange-blue:
        generate:
          marshal: true
          lower: true
          pointer: true
          mustparse: true
    
  • Specify custom value for enum string ?

    Specify custom value for enum string ?

    Is it possible to specify custom value for enum string ? Like this for example:

    /* ENUM(
    PROD = "https://prod",
    DEV = "http://dev"
    )
    */
    type Environment string
    

    Which could generate this

    const (
    	PROD Environment = "https://prod"
    	DEV Environment = "http://dev"
    )
    
  • Add support for case changing

    Add support for case changing

    The main reason behind this PR is I often have my enums defined as upper snake case like this: MY_ENUM. This means when I run the generator the constant generated is EnumTypeMYENUM, instead of EnumTypeMyEnum as expected.

    Note: the lowercase name transformation is applied before the CamelCase transformation.

    Example

    Declaration:

    // ENUM(MY_ENUM)
    type EnumType int
    

    Before this PR:

    Generated Constant

    const (
    	// EnumTypeMYENUM is a EnumType of type MY_ENUM.
    	EnumTypeMYENUM = iota
    )
    

    After this PR:

    Generated Constant

    const (
    	// EnumTypeMyEnum is a EnumType of type MY_ENUM.
    	EnumTypeMyEnum = iota
    )
  •  questions about empty strings

    questions about empty strings

    Two questions about empty strings:

    1. Enumeration values currently do not support empty strings? (because there is a valid empty string case)
    2. The mysql type is varchar, default: "". In this case, the Scanner and Valuer interfaces can only be implemented manually?
Jennifer is a code generator for Go

Jennifer Jennifer is a code generator for Go. package main import ( "fmt" . "github.com/dave/jennifer/jen" ) func main() { f := NewFile("m

Jan 4, 2023
Fast and scalable pseudorandom generator for Go

fastrand Fast pseudorandom number generator. Features Optimized for speed. Performance scales on multiple CPUs. How does it work? It abuses sync.Pool

Dec 22, 2022
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.

Clean-Swift source & test code auto generator Overview Run Output Basic Usage make config.yaml target_project_name: Miro // target project name copyri

Apr 13, 2022
Fast, scalable pseudo random number generator based on xxh3
Fast, scalable pseudo random number generator based on xxh3

XXH3-Based Pseudorandom Number Generator This package contains an experimental implementation of a noise based pseudorandom number generator that scal

Nov 24, 2022
Galvanity is Algorand vanity address generator written in Go

Galvanity Galvanity is Algorand vanity address generator written in Go Usage galvanity [search-type] <pattern> search-type is matching function to sea

Nov 14, 2022
xgen generator with extend features

XGen Plus Generator Introduction xgen is a library written in pure Go providing a set of functions that allow you to parse XSD (XML schema definition)

Dec 8, 2021
Color generator with golang
Color generator with golang

color-generator How to use this repo <img src="https://color-pallete-gen.herokuapp.com/hexCode" /> Like this: Getting Started Copy sample.env to .env

Oct 22, 2021
Rest Api Generator for Golang Programming Language

Rest Api Generator for Golang Programming Language

Nov 29, 2021
A prototype code-generator library for golang.

A prototype code-generator library for golang.

Jul 28, 2022
Julia Set Generator Written In Golang
 Julia Set Generator Written In Golang

Julia Set Generator Written In Golang This is a simple (naive) Julia Set generator written in Golang. The project utilizes concurrent workers to speed

Nov 5, 2021
Code generator that generates boilerplate code for a go http server

http-bootstrapper This is a code generator that uses go templates to generate a bootstrap code for a go http server. Usage Generate go http server cod

Nov 20, 2021
Transitland routes geometry generator from gtfs shapes

Transitland route geometry generator Generate your transitland route shapes from gtfs trips - WIP This project aims to generate transitland gtfs route

Dec 8, 2021
Golemon - A Go port of the lemon parser generator

Go lemon port A port of the Lemon Parser to Go. State This work was done entirel

Nov 3, 2022
Golang Fake data generator

Fake data generator. Written in Go Installation Faker requires Go > 1.17 go get

Nov 16, 2022
Rhythm - Euclidean Rhythm generator written in Go with nested circular lists 🤹

rhythm Euclidean Rhythm generator written in Go with nested circular lists ?? Us

Jan 31, 2022
An enum generator for go

go-enum An enum generator for go How it works The goal of go-enum is to create an easy to use enum generator that will take a decorated type declarati

Dec 22, 2022
Golang Sequel ORM that support Enum, JSON, Spatial and many more
Golang Sequel ORM that support Enum, JSON, Spatial and many more

sqlike A golang SQL ORM which anti toxic query and focus on latest features. Installation go get github.com/si3nloong/sqlike Fully compatible with nat

Nov 21, 2022
Go strcut based enum, support all types.

go-ultra-enum go-ultra-enum is an enum generator for Go. It is inspired by the powerful enum types found in Java. go-ultra-enum has the following capa

Dec 21, 2021
Go-enum-algorithm - Implement an enumeration algorithm in GO

go-enum-algorithm implement an enumeration algorithm in GO run the code go run m

Feb 15, 2022
DND-magic-item-Generator - D&D magic item generator like in Diablo

DND-magic-item-Generator D&D magic item generator like in Diablo Legendary items

Mar 28, 2022