├── .github ├── dependabot.yml └── workflows │ ├── build_and_test.yml │ ├── release.yml │ └── release_check.yml ├── .gitignore ├── .goreleaser.yml ├── .ko.yaml ├── CODEOWNERS ├── LICENSE ├── Makefile ├── README.md ├── doc.go ├── example ├── animal.go ├── animal_enum.go ├── animal_test.go ├── color.go ├── color_enum.go ├── color_test.go ├── commented.go ├── commented_enum.go ├── commented_test.go ├── custom_prefix.go ├── custom_prefix_enum.go ├── custom_prefix_test.go ├── diff_base.go ├── diff_base_enum.go ├── diff_base_test.go ├── enum_32_bit.go ├── enum_32_bit_enum.go ├── enum_32_bit_test.go ├── enum_64_bit.go ├── enum_64_bit_enum.go ├── enum_64_bit_test.go ├── example.go ├── example_enum.go ├── example_test.go ├── force_lower.go ├── force_lower_enum.go ├── force_lower_test.go ├── force_upper.go ├── force_upper_enum.go ├── force_upper_test.go ├── globs │ ├── gen.go │ ├── letter.go │ ├── letter_enum.go │ ├── number.go │ └── number_enum.go ├── go_enum_enum_test.go ├── go_enum_test.go ├── negative.go ├── negative_enum.go ├── negative_test.go ├── replace_prefix.go ├── replace_prefix_enum.go ├── replace_prefix_int.go ├── replace_prefix_int_enum.go ├── sql.go ├── sql_1_11_test.go ├── sql_enum.go ├── sql_int.go ├── sql_int_enum.go ├── sql_int_enum_test.go ├── sql_mock_test.go ├── sql_str.go ├── sql_str_enum.go ├── sql_str_enum_test.go ├── sql_str_int.go ├── sql_str_int_enum.go ├── sql_str_int_enum_test.go ├── sql_test.go ├── strings_only.go ├── strings_only_enum.go ├── strings_only_test.go ├── suffix.enum.gen.go ├── suffix.enum.gen_test.go ├── suffix.go ├── suffix_test.go ├── user_globbed.tmpl ├── user_globbed2.tmpl ├── user_template.go ├── user_template.tmpl ├── user_template_enum.go └── user_template_test.go ├── generator ├── .snapshots │ ├── Test118CustomPrefixExampleFile-1.18 │ ├── Test118CustomPrefixExampleFile-og │ ├── Test118ExampleFile-1.18 │ ├── Test118ExampleFile-og │ ├── Test118ExampleFileMoreOptions-1.18 │ ├── Test118ExampleFileMoreOptions-og │ ├── Test118NoPrefixExampleFile-1.18 │ ├── Test118NoPrefixExampleFile-og │ ├── Test118NoPrefixExampleFileWithSnakeToCamel-1.18 │ ├── Test118NoPrefixExampleFileWithSnakeToCamel-og │ ├── TestCustomPrefixExampleFile │ ├── TestExampleFile │ ├── TestExampleFileMoreOptions │ ├── TestExampleFileMoreOptionsWithForceUpper │ ├── TestNoPrefixExampleFile │ ├── TestNoPrefixExampleFileWithSnakeToCamel │ └── TestReplacePrefixExampleFile ├── embedded_1.16.go ├── enum.tmpl ├── enum_string.tmpl ├── example_1.18_test.go ├── example_test.go ├── generator.go ├── generator_1.18_test.go ├── generator_test.go └── template_funcs.go ├── go.mod ├── go.sum ├── main.go ├── tools.go └── update-snapshots.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | groups: 8 | major: 9 | update-types: 10 | - major 11 | minor: 12 | update-types: 13 | - minor 14 | - patch 15 | - package-ecosystem: github-actions 16 | directory: "/" 17 | schedule: 18 | interval: weekly 19 | groups: 20 | actions: 21 | update-types: 22 | - major 23 | - minor 24 | - patch 25 | -------------------------------------------------------------------------------- /.github/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | name: build and test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | go: 16 | - "1.20" 17 | - "1.21" 18 | name: run tests with go version ${{ matrix.go }} 19 | steps: 20 | - name: install go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version: ${{ matrix.go }} 24 | 25 | - name: checkout code 26 | uses: actions/checkout@v4 27 | 28 | - name: Build 29 | run: make build 30 | 31 | - name: Test 32 | env: 33 | COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} 34 | run: | 35 | make cover 36 | 37 | - name: Coveralls 38 | if: (matrix.go == '1.21') 39 | env: 40 | COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} 41 | run: | 42 | if [[ -n "$COVERALLS_TOKEN" ]]; then 43 | make coveralls 44 | fi 45 | 46 | - name: Upload coverage 47 | if: (matrix.go == '1.21') 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: coverage 51 | path: coverage.* 52 | 53 | - name: Assert no changes 54 | run: make assert-no-changes 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | release: 5 | types: 6 | - "created" 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: ">=1.20.2" 21 | - name: Ensure Go 22 | run: go version 23 | 24 | - name: Install GoReleaser 25 | uses: goreleaser/goreleaser-action@v5 26 | with: 27 | install-only: true 28 | - name: Show GoReleaser version 29 | run: goreleaser -v 30 | 31 | - name: Login to Docker Hub 32 | uses: docker/login-action@v3 33 | with: 34 | username: ${{ secrets.DOCKERHUB_USERNAME }} 35 | password: ${{ secrets.DOCKERHUB_TOKEN }} 36 | 37 | - name: Run GoReleaser 38 | uses: goreleaser/goreleaser-action@v5 39 | if: success() && startsWith(github.ref, 'refs/tags/') 40 | with: 41 | # either 'goreleaser' (default) or 'goreleaser-pro' 42 | distribution: goreleaser 43 | version: latest 44 | args: release --clean 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | -------------------------------------------------------------------------------- /.github/workflows/release_check.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: {} 5 | 6 | jobs: 7 | goreleaser: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Set up Go 16 | uses: actions/setup-go@v5 17 | with: 18 | go-version: ">=1.20.2" 19 | - name: Ensure Go 20 | run: go version 21 | 22 | - name: Install GoReleaser 23 | uses: goreleaser/goreleaser-action@v5 24 | with: 25 | install-only: true 26 | - name: Show GoReleaser version 27 | run: goreleaser -v 28 | 29 | - name: Run GoReleaser 30 | uses: goreleaser/goreleaser-action@v5 31 | with: 32 | # either 'goreleaser' (default) or 'goreleaser-pro' 33 | distribution: goreleaser 34 | version: latest 35 | args: release --clean --snapshot 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | /bin 17 | /coverage 18 | vendor/ 19 | coverage.out 20 | coverage.html 21 | 22 | dist/ 23 | .idea/ -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | - CGO_ENABLED=0 4 | goos: 5 | - linux 6 | - windows 7 | - darwin 8 | 9 | checksum: 10 | name_template: "{{ .ProjectName }}_checksums.txt" 11 | 12 | archives: 13 | # Distribute just the binaries by default 14 | - format: binary 15 | name_template: >- 16 | {{ .ProjectName }}_ 17 | {{- title .Os }}_ 18 | {{- if eq .Arch "amd64" }}x86_64 19 | {{- else if eq .Arch "386" }}i386 20 | {{- else }}{{ .Arch }}{{ end }} 21 | 22 | kos: 23 | - repository: abice/go-enum 24 | base_image: cgr.dev/chainguard/go # need the go binary for formatting 🔧 purposes 🫤 25 | tags: 26 | - "{{.Version}}" 27 | - latest 28 | bare: true 29 | preserve_import_paths: false 30 | platforms: 31 | - linux/amd64 32 | - linux/arm64 33 | 34 | snapshot: 35 | name_template: "{{ incpatch .Version }}-next" 36 | changelog: 37 | sort: asc 38 | filters: 39 | exclude: 40 | - "^docs:" 41 | - "^test:" 42 | -------------------------------------------------------------------------------- /.ko.yaml: -------------------------------------------------------------------------------- 1 | defaultBaseImage: cgr.dev/chainguard/go # For now, we need the go binary in order to format go files. 2 | defaultPlatforms: 3 | - linux/arm64 4 | - linux/amd64 5 | 6 | builds: 7 | - id: go-enum 8 | dir: . # default is . 9 | main: . 10 | flags: 11 | - -tags 12 | - netgo 13 | ldflags: 14 | - -s -w 15 | - -extldflags "-static" 16 | - -X "main.version={{.Env.VERSION}}" 17 | - -X "main.commit={{.Env.COMMIT}}" 18 | - -X "main.date={{.Env.DATE}}" 19 | - -X "main.builtBy={{.Env.BUILT_BY}}" 20 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @abice 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alex Bice 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL:=all 2 | ifdef VERBOSE 3 | V = -v 4 | else 5 | .SILENT: 6 | endif 7 | 8 | GO ?= go 9 | COVERAGEDIR= coverage 10 | SERVICE=local 11 | 12 | ifdef GITHUB_ACTIONS 13 | SERVICE=github-actions 14 | endif 15 | 16 | DATE := $(shell date -u '+%FT%T%z') 17 | GITHUB_SHA ?= $(shell git rev-parse HEAD) 18 | GITHUB_REF ?= local 19 | 20 | LDFLAGS += -X "main.version=$(GITHUB_REF)" 21 | LDFLAGS += -X "main.commit=$(GITHUB_SHA)" 22 | LDFLAGS += -X "main.date=$(DATE)" 23 | LDFLAGS += -X "main.builtBy=$(USER)" 24 | LDFLAGS += -extldflags '-static' 25 | 26 | define goinstall 27 | mkdir -p $(shell pwd)/bin 28 | echo "Installing $(1)" 29 | GOBIN=$(shell pwd)/bin go install $(1) 30 | endef 31 | 32 | GOIMPORTS=bin/goimports 33 | GOVERALLS=bin/goveralls 34 | MOCKGEN=bin/mockgen 35 | deps: $(MOCKGEN) 36 | deps: $(GOIMPORTS) 37 | 38 | PACKAGES='./generator' './example' 39 | 40 | .PHONY: all 41 | all: build fmt test example cover install 42 | 43 | build: deps 44 | $(GO) generate ./generator 45 | if [ ! -d bin ]; then mkdir bin; fi 46 | $(GO) build -v -o bin/go-enum -ldflags='-X "main.version=example" -X "main.commit=example" -X "main.date=example" -X "main.builtBy=example"' . 47 | 48 | fmt: 49 | -$(GO) fmt ./... 50 | 51 | test: gen-test generate 52 | $(GO) test -v -race -shuffle on -coverprofile=coverage.out ./... 53 | $(GO) test -v -race -shuffle on --tags=example ./example 54 | 55 | cover: gen-test test 56 | $(GO) tool cover -html=coverage.out -o coverage.html 57 | 58 | tc: test cover 59 | coveralls: $(GOVERALLS) 60 | $(GOVERALLS) -coverprofile=coverage.out -service=$(SERVICE) -repotoken=$(COVERALLS_TOKEN) 61 | 62 | clean: 63 | rm -f bin/go-enum 64 | rm -rf coverage/ 65 | rm -rf bin/ 66 | rm -rf dist/ 67 | $(GO) clean 68 | 69 | .PHONY: assert-no-changes 70 | assert-no-changes: 71 | @if [ -n "$(shell git status --porcelain)" ]; then \ 72 | echo "git changes found: $(shell git status --porcelain)"; \ 73 | exit 1; \ 74 | fi 75 | 76 | .PHONY: generate 77 | generate: 78 | $(GO) generate --tags=example $(PACKAGES) 79 | 80 | gen-test: build 81 | $(GO) generate --tags=example $(PACKAGES) 82 | 83 | install: 84 | $(GO) install 85 | 86 | phony: clean tc build 87 | 88 | .PHONY: example 89 | example: 90 | $(GO) generate ./example/... 91 | 92 | bin/goimports: go.sum 93 | $(call goinstall,golang.org/x/tools/cmd/goimports) 94 | 95 | bin/mockgen: go.sum 96 | $(call goinstall,github.com/golang/mock/mockgen) 97 | 98 | bin/goveralls: go.sum 99 | $(call goinstall,github.com/mattn/goveralls) 100 | 101 | # snapshots: snapshots_1.17 102 | snapshots: snapshots_1.21 103 | 104 | snapshots_%: clean 105 | echo "##### updating snapshots for golang $* #####" 106 | docker run -i -t -w /app -v $(shell pwd):/app --entrypoint /bin/sh golang:$* -c './update-snapshots.sh || true' 107 | 108 | .PHONY: ci 109 | ci: docker_1.20 110 | ci: docker_1.21 111 | 112 | docker_%: 113 | echo "##### testing golang $* #####" 114 | docker run -i -t -w /app -v $(shell pwd):/app --entrypoint /bin/sh golang:$* -c 'make clean && make' 115 | 116 | .PHONY: pullimages 117 | pullimages: pullimage_1.20 118 | pullimages: pullimage_1.21 119 | 120 | pullimage_%: 121 | docker pull golang:$* 122 | 123 | build_docker: 124 | KO_DOCKER_REPO=abice/go-enum VERSION=$(GITHUB_REF) COMMIT=$(GITHUB_SHA) DATE=$(DATE) BUILT_BY=$(USER) ko build --bare --local 125 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Alex Bice 2 | 3 | // go-enum is a utility for generating a more functional version of 4 | // enumerations in go. 5 | // 6 | // The generator looks for the exact string `ENUM(` and will continue 7 | // to store comma separated values until it finds a `)`. Those values 8 | // can be on one line, or separate lines; they can include `_` in order 9 | // to skip a value in the enum that won't be an allowed value. 10 | // 11 | // Installation 12 | // 13 | // go get github.com/abice/go-enum 14 | // 15 | // Usage: 16 | // Sample File 17 | // 18 | // //go:generate go-enum -f=myenum.go --marshal -- 19 | // 20 | // package mypackage 21 | // 22 | // // MyEnum docs here 23 | // // ENUM(Value1, Value2 24 | // // Value3,_, 25 | // // Value4) 26 | // type MyEnum int 27 | // 28 | // Command to generate your enum 29 | // 30 | // go generate ./ 31 | package main 32 | -------------------------------------------------------------------------------- /example/animal.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | //go:generate ../bin/go-enum -a "+:Plus,#:Sharp" -b example 5 | 6 | package example 7 | 8 | // Animal x ENUM( 9 | // Cat, 10 | // Dog, 11 | // Fish 12 | // Fish++ 13 | // Fish# 14 | // ). 15 | type Animal int32 16 | -------------------------------------------------------------------------------- /example/animal_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // AnimalCat is a Animal of type Cat. 19 | AnimalCat Animal = iota 20 | // AnimalDog is a Animal of type Dog. 21 | AnimalDog 22 | // AnimalFish is a Animal of type Fish. 23 | AnimalFish 24 | // AnimalFishPlusPlus is a Animal of type Fish++. 25 | AnimalFishPlusPlus 26 | // AnimalFishSharp is a Animal of type Fish#. 27 | AnimalFishSharp 28 | ) 29 | 30 | var ErrInvalidAnimal = errors.New("not a valid Animal") 31 | 32 | const _AnimalName = "CatDogFishFish++Fish#" 33 | 34 | var _AnimalMap = map[Animal]string{ 35 | AnimalCat: _AnimalName[0:3], 36 | AnimalDog: _AnimalName[3:6], 37 | AnimalFish: _AnimalName[6:10], 38 | AnimalFishPlusPlus: _AnimalName[10:16], 39 | AnimalFishSharp: _AnimalName[16:21], 40 | } 41 | 42 | // String implements the Stringer interface. 43 | func (x Animal) String() string { 44 | if str, ok := _AnimalMap[x]; ok { 45 | return str 46 | } 47 | return fmt.Sprintf("Animal(%d)", x) 48 | } 49 | 50 | // IsValid provides a quick way to determine if the typed value is 51 | // part of the allowed enumerated values 52 | func (x Animal) IsValid() bool { 53 | _, ok := _AnimalMap[x] 54 | return ok 55 | } 56 | 57 | var _AnimalValue = map[string]Animal{ 58 | _AnimalName[0:3]: AnimalCat, 59 | _AnimalName[3:6]: AnimalDog, 60 | _AnimalName[6:10]: AnimalFish, 61 | _AnimalName[10:16]: AnimalFishPlusPlus, 62 | _AnimalName[16:21]: AnimalFishSharp, 63 | } 64 | 65 | // ParseAnimal attempts to convert a string to a Animal. 66 | func ParseAnimal(name string) (Animal, error) { 67 | if x, ok := _AnimalValue[name]; ok { 68 | return x, nil 69 | } 70 | return Animal(0), fmt.Errorf("%s is %w", name, ErrInvalidAnimal) 71 | } 72 | -------------------------------------------------------------------------------- /example/animal_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | type animalData struct { 15 | AnimalX Animal `json:"animal"` 16 | } 17 | 18 | func TestAnimalString(t *testing.T) { 19 | x := Animal(109) 20 | assert.Equal(t, "Animal(109)", x.String()) 21 | x = Animal(1) 22 | assert.Equal(t, "Dog", x.String()) 23 | 24 | y, err := ParseAnimal("Cat") 25 | require.NoError(t, err, "Failed parsing cat") 26 | assert.Equal(t, AnimalCat, y) 27 | 28 | z, err := ParseAnimal("Snake") 29 | require.Error(t, err, "Shouldn't parse a snake") 30 | assert.Equal(t, Animal(0), z) 31 | } 32 | 33 | func TestAnimalUnmarshal(t *testing.T) { 34 | tests := []struct { 35 | name string 36 | input string 37 | output *animalData 38 | errorExpected bool 39 | err error 40 | }{ 41 | { 42 | name: "cat", 43 | input: `{"animal":0}`, 44 | output: &animalData{AnimalX: AnimalCat}, 45 | errorExpected: false, 46 | err: nil, 47 | }, 48 | { 49 | name: "dog", 50 | input: `{"animal":1}`, 51 | output: &animalData{AnimalX: AnimalDog}, 52 | errorExpected: false, 53 | err: nil, 54 | }, 55 | { 56 | name: "fish", 57 | input: `{"animal":2}`, 58 | output: &animalData{AnimalX: AnimalFish}, 59 | errorExpected: false, 60 | err: nil, 61 | }, 62 | { 63 | name: "notananimal", 64 | input: `{"animal":22}`, 65 | output: &animalData{AnimalX: Animal(22)}, 66 | errorExpected: false, 67 | err: nil, 68 | }, 69 | } 70 | 71 | for _, test := range tests { 72 | t.Run(test.name, func(tt *testing.T) { 73 | x := &animalData{} 74 | err := json.Unmarshal([]byte(test.input), x) 75 | if !test.errorExpected { 76 | require.NoError(tt, err, "failed unmarshalling the json.") 77 | assert.Equal(tt, test.output.AnimalX, x.AnimalX) 78 | } else { 79 | require.Error(tt, err) 80 | assert.EqualError(tt, err, test.err.Error()) 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func TestAnimalMarshal(t *testing.T) { 87 | tests := []struct { 88 | name string 89 | input *animalData 90 | output string 91 | errorExpected bool 92 | err error 93 | }{ 94 | { 95 | name: "cat", 96 | output: `{"animal":0}`, 97 | input: &animalData{AnimalX: AnimalCat}, 98 | errorExpected: false, 99 | err: nil, 100 | }, 101 | { 102 | name: "dog", 103 | output: `{"animal":1}`, 104 | input: &animalData{AnimalX: AnimalDog}, 105 | errorExpected: false, 106 | err: nil, 107 | }, 108 | { 109 | name: "fish", 110 | output: `{"animal":2}`, 111 | input: &animalData{AnimalX: AnimalFish}, 112 | errorExpected: false, 113 | err: nil, 114 | }, 115 | } 116 | 117 | for _, test := range tests { 118 | t.Run(test.name, func(tt *testing.T) { 119 | raw, err := json.Marshal(test.input) 120 | require.NoError(tt, err, "failed marshalling to json") 121 | assert.JSONEq(tt, test.output, string(raw)) 122 | }) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /example/color.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | //go:generate ../bin/go-enum --marshal --lower --ptr --mustparse -b example 5 | 6 | package example 7 | 8 | // Color is an enumeration of colors that are allowed. 9 | /* ENUM( 10 | Black, White, Red 11 | Green = 33 // Green starts with 33 12 | */ 13 | // Blue 14 | // grey= 15 | // yellow 16 | // blue-green 17 | // red-orange 18 | // yellow_green 19 | // red-orange-blue 20 | // ) 21 | type Color int 22 | -------------------------------------------------------------------------------- /example/color_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | const ( 19 | // ColorBlack is a Color of type Black. 20 | ColorBlack Color = iota 21 | // ColorWhite is a Color of type White. 22 | ColorWhite 23 | // ColorRed is a Color of type Red. 24 | ColorRed 25 | // ColorGreen is a Color of type Green. 26 | // Green starts with 33 27 | ColorGreen Color = iota + 30 28 | // ColorBlue is a Color of type Blue. 29 | ColorBlue 30 | // ColorGrey is a Color of type Grey. 31 | ColorGrey 32 | // ColorYellow is a Color of type Yellow. 33 | ColorYellow 34 | // ColorBlueGreen is a Color of type Blue-Green. 35 | ColorBlueGreen 36 | // ColorRedOrange is a Color of type Red-Orange. 37 | ColorRedOrange 38 | // ColorYellowGreen is a Color of type Yellow_green. 39 | ColorYellowGreen 40 | // ColorRedOrangeBlue is a Color of type Red-Orange-Blue. 41 | ColorRedOrangeBlue 42 | ) 43 | 44 | var ErrInvalidColor = errors.New("not a valid Color") 45 | 46 | const _ColorName = "BlackWhiteRedGreenBluegreyyellowblue-greenred-orangeyellow_greenred-orange-blue" 47 | 48 | var _ColorMap = map[Color]string{ 49 | ColorBlack: _ColorName[0:5], 50 | ColorWhite: _ColorName[5:10], 51 | ColorRed: _ColorName[10:13], 52 | ColorGreen: _ColorName[13:18], 53 | ColorBlue: _ColorName[18:22], 54 | ColorGrey: _ColorName[22:26], 55 | ColorYellow: _ColorName[26:32], 56 | ColorBlueGreen: _ColorName[32:42], 57 | ColorRedOrange: _ColorName[42:52], 58 | ColorYellowGreen: _ColorName[52:64], 59 | ColorRedOrangeBlue: _ColorName[64:79], 60 | } 61 | 62 | // String implements the Stringer interface. 63 | func (x Color) String() string { 64 | if str, ok := _ColorMap[x]; ok { 65 | return str 66 | } 67 | return fmt.Sprintf("Color(%d)", x) 68 | } 69 | 70 | // IsValid provides a quick way to determine if the typed value is 71 | // part of the allowed enumerated values 72 | func (x Color) IsValid() bool { 73 | _, ok := _ColorMap[x] 74 | return ok 75 | } 76 | 77 | var _ColorValue = map[string]Color{ 78 | _ColorName[0:5]: ColorBlack, 79 | strings.ToLower(_ColorName[0:5]): ColorBlack, 80 | _ColorName[5:10]: ColorWhite, 81 | strings.ToLower(_ColorName[5:10]): ColorWhite, 82 | _ColorName[10:13]: ColorRed, 83 | strings.ToLower(_ColorName[10:13]): ColorRed, 84 | _ColorName[13:18]: ColorGreen, 85 | strings.ToLower(_ColorName[13:18]): ColorGreen, 86 | _ColorName[18:22]: ColorBlue, 87 | strings.ToLower(_ColorName[18:22]): ColorBlue, 88 | _ColorName[22:26]: ColorGrey, 89 | strings.ToLower(_ColorName[22:26]): ColorGrey, 90 | _ColorName[26:32]: ColorYellow, 91 | strings.ToLower(_ColorName[26:32]): ColorYellow, 92 | _ColorName[32:42]: ColorBlueGreen, 93 | strings.ToLower(_ColorName[32:42]): ColorBlueGreen, 94 | _ColorName[42:52]: ColorRedOrange, 95 | strings.ToLower(_ColorName[42:52]): ColorRedOrange, 96 | _ColorName[52:64]: ColorYellowGreen, 97 | strings.ToLower(_ColorName[52:64]): ColorYellowGreen, 98 | _ColorName[64:79]: ColorRedOrangeBlue, 99 | strings.ToLower(_ColorName[64:79]): ColorRedOrangeBlue, 100 | } 101 | 102 | // ParseColor attempts to convert a string to a Color. 103 | func ParseColor(name string) (Color, error) { 104 | if x, ok := _ColorValue[name]; ok { 105 | return x, nil 106 | } 107 | return Color(0), fmt.Errorf("%s is %w", name, ErrInvalidColor) 108 | } 109 | 110 | // MustParseColor converts a string to a Color, and panics if is not valid. 111 | func MustParseColor(name string) Color { 112 | val, err := ParseColor(name) 113 | if err != nil { 114 | panic(err) 115 | } 116 | return val 117 | } 118 | 119 | func (x Color) Ptr() *Color { 120 | return &x 121 | } 122 | 123 | // MarshalText implements the text marshaller method. 124 | func (x Color) MarshalText() ([]byte, error) { 125 | return []byte(x.String()), nil 126 | } 127 | 128 | // UnmarshalText implements the text unmarshaller method. 129 | func (x *Color) UnmarshalText(text []byte) error { 130 | name := string(text) 131 | tmp, err := ParseColor(name) 132 | if err != nil { 133 | return err 134 | } 135 | *x = tmp 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /example/color_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | type testData struct { 17 | ColorX Color `json:"color"` 18 | } 19 | 20 | func TestColorString(t *testing.T) { 21 | x := Color(109) 22 | assert.Equal(t, "Color(109)", x.String()) 23 | 24 | assert.Equal(t, Color(33), ColorGreen) 25 | assert.Equal(t, Color(34), ColorBlue) 26 | assert.Equal(t, &x, Color(109).Ptr()) 27 | } 28 | 29 | func TestColorMustParse(t *testing.T) { 30 | x := `avocadogreen` 31 | 32 | assert.PanicsWithError(t, x+" is not a valid Color", func() { MustParseColor(x) }) 33 | assert.NotPanics(t, func() { MustParseColor(ColorGreen.String()) }) 34 | } 35 | 36 | func TestColorUnmarshal(t *testing.T) { 37 | tests := []struct { 38 | name string 39 | input string 40 | output *testData 41 | errorExpected bool 42 | err error 43 | }{ 44 | { 45 | name: "black", 46 | input: `{"color":"Black"}`, 47 | output: &testData{ColorX: ColorBlack}, 48 | errorExpected: false, 49 | err: nil, 50 | }, 51 | { 52 | name: "blacklower", 53 | input: `{"color":"black"}`, 54 | output: &testData{ColorX: ColorBlack}, 55 | errorExpected: false, 56 | err: nil, 57 | }, 58 | { 59 | name: "white", 60 | input: `{"color":"White"}`, 61 | output: &testData{ColorX: ColorWhite}, 62 | errorExpected: false, 63 | err: nil, 64 | }, 65 | { 66 | name: "whitelower", 67 | input: `{"color":"white"}`, 68 | output: &testData{ColorX: ColorWhite}, 69 | errorExpected: false, 70 | err: nil, 71 | }, 72 | { 73 | name: "red", 74 | input: `{"color":"Red"}`, 75 | output: &testData{ColorX: ColorRed}, 76 | errorExpected: false, 77 | err: nil, 78 | }, 79 | { 80 | name: "redlower", 81 | input: `{"color":"red"}`, 82 | output: &testData{ColorX: ColorRed}, 83 | errorExpected: false, 84 | err: nil, 85 | }, 86 | { 87 | name: "green", 88 | input: `{"color":"Green"}`, 89 | output: &testData{ColorX: ColorGreen}, 90 | errorExpected: false, 91 | err: nil, 92 | }, 93 | { 94 | name: "greenlower", 95 | input: `{"color":"green"}`, 96 | output: &testData{ColorX: ColorGreen}, 97 | errorExpected: false, 98 | err: nil, 99 | }, 100 | { 101 | name: "blue", 102 | input: `{"color":"Blue"}`, 103 | output: &testData{ColorX: ColorBlue}, 104 | errorExpected: false, 105 | err: nil, 106 | }, 107 | { 108 | name: "bluelower", 109 | input: `{"color":"blue"}`, 110 | output: &testData{ColorX: ColorBlue}, 111 | errorExpected: false, 112 | err: nil, 113 | }, 114 | { 115 | name: "grey", 116 | input: `{"color":"grey"}`, 117 | output: &testData{ColorX: ColorGrey}, 118 | errorExpected: false, 119 | err: nil, 120 | }, 121 | { 122 | name: "greylower", 123 | input: `{"color":"grey"}`, 124 | output: &testData{ColorX: ColorGrey}, 125 | errorExpected: false, 126 | err: nil, 127 | }, 128 | { 129 | name: "yellow", 130 | input: `{"color":"yellow"}`, 131 | output: &testData{ColorX: ColorYellow}, 132 | errorExpected: false, 133 | err: nil, 134 | }, 135 | { 136 | name: "yellowlower", 137 | input: `{"color":"yellow"}`, 138 | output: &testData{ColorX: ColorYellow}, 139 | errorExpected: false, 140 | err: nil, 141 | }, 142 | { 143 | name: "yellow_green", 144 | input: `{"color":"yellow_green"}`, 145 | output: &testData{ColorX: ColorYellowGreen}, 146 | errorExpected: false, 147 | err: nil, 148 | }, 149 | { 150 | name: "magenta", 151 | input: `{"color":"Magenta"}`, 152 | output: &testData{ColorX: ColorYellow}, 153 | errorExpected: true, 154 | err: errors.New("Magenta is not a valid Color"), 155 | }, 156 | } 157 | 158 | for _, test := range tests { 159 | t.Run(test.name, func(tt *testing.T) { 160 | x := &testData{} 161 | err := json.Unmarshal([]byte(test.input), x) 162 | if !test.errorExpected { 163 | require.NoError(tt, err, "failed unmarshalling the json.") 164 | assert.Equal(tt, test.output.ColorX, x.ColorX) 165 | } else { 166 | require.Error(tt, err) 167 | assert.EqualError(tt, err, test.err.Error()) 168 | } 169 | }) 170 | } 171 | } 172 | 173 | func TestColorMarshal(t *testing.T) { 174 | tests := []struct { 175 | name string 176 | input *testData 177 | output string 178 | errorExpected bool 179 | err error 180 | }{ 181 | { 182 | name: "black", 183 | output: `{"color":"Black"}`, 184 | input: &testData{ColorX: ColorBlack}, 185 | errorExpected: false, 186 | err: nil, 187 | }, 188 | { 189 | name: "white", 190 | output: `{"color":"White"}`, 191 | input: &testData{ColorX: ColorWhite}, 192 | errorExpected: false, 193 | err: nil, 194 | }, 195 | { 196 | name: "red", 197 | output: `{"color":"Red"}`, 198 | input: &testData{ColorX: ColorRed}, 199 | errorExpected: false, 200 | err: nil, 201 | }, 202 | { 203 | name: "green", 204 | output: `{"color":"Green"}`, 205 | input: &testData{ColorX: ColorGreen}, 206 | errorExpected: false, 207 | err: nil, 208 | }, 209 | { 210 | name: "blue", 211 | output: `{"color":"Blue"}`, 212 | input: &testData{ColorX: ColorBlue}, 213 | errorExpected: false, 214 | err: nil, 215 | }, 216 | { 217 | name: "grey", 218 | output: `{"color":"grey"}`, 219 | input: &testData{ColorX: ColorGrey}, 220 | errorExpected: false, 221 | err: nil, 222 | }, 223 | { 224 | name: "yellow", 225 | output: `{"color":"yellow"}`, 226 | input: &testData{ColorX: ColorYellow}, 227 | errorExpected: false, 228 | err: nil, 229 | }, 230 | } 231 | 232 | for _, test := range tests { 233 | t.Run(test.name, func(tt *testing.T) { 234 | raw, err := json.Marshal(test.input) 235 | require.NoError(tt, err, "failed marshalling to json") 236 | assert.JSONEq(tt, test.output, string(raw)) 237 | }) 238 | } 239 | } 240 | 241 | func BenchmarkColorParse(b *testing.B) { 242 | knownItems := []string{ 243 | ColorRedOrangeBlue.String(), 244 | strings.ToLower(ColorRedOrangeBlue.String()), 245 | // "2", Leave this in to add an int as string parsing option in future. 246 | } 247 | 248 | var err error 249 | for _, item := range knownItems { 250 | b.Run(item, func(b *testing.B) { 251 | b.ReportAllocs() 252 | b.ResetTimer() 253 | for i := 0; i < b.N; i++ { 254 | _, err = ParseColor(item) 255 | assert.NoError(b, err) 256 | } 257 | }) 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /example/commented.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --marshal --lower -b example 2 | 3 | package example 4 | 5 | // Commented is an enumeration of commented values 6 | /* 7 | ENUM( 8 | value1 // Commented value 1 9 | value2 10 | value3 // Commented value 3 11 | ) 12 | */ 13 | type Commented int 14 | 15 | // ComplexCommented has some extra complicated parsing rules. 16 | /* 17 | ENUM( 18 | _, // Placeholder with a ',' in it. (for harder testing) 19 | value1 // Commented value 1 20 | value2, 21 | value3 // Commented value 3 22 | ) 23 | */ 24 | type ComplexCommented int 25 | -------------------------------------------------------------------------------- /example/commented_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | const ( 19 | // CommentedValue1 is a Commented of type Value1. 20 | // Commented value 1 21 | CommentedValue1 Commented = iota 22 | // CommentedValue2 is a Commented of type Value2. 23 | CommentedValue2 24 | // CommentedValue3 is a Commented of type Value3. 25 | // Commented value 3 26 | CommentedValue3 27 | ) 28 | 29 | var ErrInvalidCommented = errors.New("not a valid Commented") 30 | 31 | const _CommentedName = "value1value2value3" 32 | 33 | var _CommentedMap = map[Commented]string{ 34 | CommentedValue1: _CommentedName[0:6], 35 | CommentedValue2: _CommentedName[6:12], 36 | CommentedValue3: _CommentedName[12:18], 37 | } 38 | 39 | // String implements the Stringer interface. 40 | func (x Commented) String() string { 41 | if str, ok := _CommentedMap[x]; ok { 42 | return str 43 | } 44 | return fmt.Sprintf("Commented(%d)", x) 45 | } 46 | 47 | // IsValid provides a quick way to determine if the typed value is 48 | // part of the allowed enumerated values 49 | func (x Commented) IsValid() bool { 50 | _, ok := _CommentedMap[x] 51 | return ok 52 | } 53 | 54 | var _CommentedValue = map[string]Commented{ 55 | _CommentedName[0:6]: CommentedValue1, 56 | strings.ToLower(_CommentedName[0:6]): CommentedValue1, 57 | _CommentedName[6:12]: CommentedValue2, 58 | strings.ToLower(_CommentedName[6:12]): CommentedValue2, 59 | _CommentedName[12:18]: CommentedValue3, 60 | strings.ToLower(_CommentedName[12:18]): CommentedValue3, 61 | } 62 | 63 | // ParseCommented attempts to convert a string to a Commented. 64 | func ParseCommented(name string) (Commented, error) { 65 | if x, ok := _CommentedValue[name]; ok { 66 | return x, nil 67 | } 68 | return Commented(0), fmt.Errorf("%s is %w", name, ErrInvalidCommented) 69 | } 70 | 71 | // MarshalText implements the text marshaller method. 72 | func (x Commented) MarshalText() ([]byte, error) { 73 | return []byte(x.String()), nil 74 | } 75 | 76 | // UnmarshalText implements the text unmarshaller method. 77 | func (x *Commented) UnmarshalText(text []byte) error { 78 | name := string(text) 79 | tmp, err := ParseCommented(name) 80 | if err != nil { 81 | return err 82 | } 83 | *x = tmp 84 | return nil 85 | } 86 | 87 | const ( 88 | // Skipped value. 89 | // Placeholder with a ',' in it. (for harder testing) 90 | _ ComplexCommented = iota 91 | // ComplexCommentedValue1 is a ComplexCommented of type Value1. 92 | // Commented value 1 93 | ComplexCommentedValue1 94 | // ComplexCommentedValue2 is a ComplexCommented of type Value2. 95 | ComplexCommentedValue2 96 | // ComplexCommentedValue3 is a ComplexCommented of type Value3. 97 | // Commented value 3 98 | ComplexCommentedValue3 99 | ) 100 | 101 | var ErrInvalidComplexCommented = errors.New("not a valid ComplexCommented") 102 | 103 | const _ComplexCommentedName = "value1value2value3" 104 | 105 | var _ComplexCommentedMap = map[ComplexCommented]string{ 106 | ComplexCommentedValue1: _ComplexCommentedName[0:6], 107 | ComplexCommentedValue2: _ComplexCommentedName[6:12], 108 | ComplexCommentedValue3: _ComplexCommentedName[12:18], 109 | } 110 | 111 | // String implements the Stringer interface. 112 | func (x ComplexCommented) String() string { 113 | if str, ok := _ComplexCommentedMap[x]; ok { 114 | return str 115 | } 116 | return fmt.Sprintf("ComplexCommented(%d)", x) 117 | } 118 | 119 | // IsValid provides a quick way to determine if the typed value is 120 | // part of the allowed enumerated values 121 | func (x ComplexCommented) IsValid() bool { 122 | _, ok := _ComplexCommentedMap[x] 123 | return ok 124 | } 125 | 126 | var _ComplexCommentedValue = map[string]ComplexCommented{ 127 | _ComplexCommentedName[0:6]: ComplexCommentedValue1, 128 | strings.ToLower(_ComplexCommentedName[0:6]): ComplexCommentedValue1, 129 | _ComplexCommentedName[6:12]: ComplexCommentedValue2, 130 | strings.ToLower(_ComplexCommentedName[6:12]): ComplexCommentedValue2, 131 | _ComplexCommentedName[12:18]: ComplexCommentedValue3, 132 | strings.ToLower(_ComplexCommentedName[12:18]): ComplexCommentedValue3, 133 | } 134 | 135 | // ParseComplexCommented attempts to convert a string to a ComplexCommented. 136 | func ParseComplexCommented(name string) (ComplexCommented, error) { 137 | if x, ok := _ComplexCommentedValue[name]; ok { 138 | return x, nil 139 | } 140 | return ComplexCommented(0), fmt.Errorf("%s is %w", name, ErrInvalidComplexCommented) 141 | } 142 | 143 | // MarshalText implements the text marshaller method. 144 | func (x ComplexCommented) MarshalText() ([]byte, error) { 145 | return []byte(x.String()), nil 146 | } 147 | 148 | // UnmarshalText implements the text unmarshaller method. 149 | func (x *ComplexCommented) UnmarshalText(text []byte) error { 150 | name := string(text) 151 | tmp, err := ParseComplexCommented(name) 152 | if err != nil { 153 | return err 154 | } 155 | *x = tmp 156 | return nil 157 | } 158 | -------------------------------------------------------------------------------- /example/commented_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | type commentedData struct { 16 | CommentedX Commented `json:"Commented"` 17 | } 18 | 19 | func TestCommentedEnumString(t *testing.T) { 20 | x := Commented(109) 21 | assert.Equal(t, "Commented(109)", x.String()) 22 | x = Commented(1) 23 | assert.Equal(t, "value2", x.String()) 24 | 25 | y, err := ParseCommented("value3") 26 | require.NoError(t, err, "Failed parsing cat") 27 | assert.Equal(t, CommentedValue3, y) 28 | 29 | z, err := ParseCommented("value4") 30 | require.Error(t, err, "Shouldn't parse a snake") 31 | assert.Equal(t, Commented(0), z) 32 | } 33 | 34 | func TestCommentedUnmarshal(t *testing.T) { 35 | tests := []struct { 36 | name string 37 | input string 38 | output *commentedData 39 | errorExpected bool 40 | err error 41 | }{ 42 | { 43 | name: "value1", 44 | input: `{"Commented":"value1"}`, 45 | output: &commentedData{CommentedX: CommentedValue1}, 46 | errorExpected: false, 47 | err: nil, 48 | }, 49 | { 50 | name: "value2", 51 | input: `{"Commented":"value2"}`, 52 | output: &commentedData{CommentedX: CommentedValue2}, 53 | errorExpected: false, 54 | err: nil, 55 | }, 56 | { 57 | name: "value3", 58 | input: `{"Commented":"value3"}`, 59 | output: &commentedData{CommentedX: CommentedValue3}, 60 | errorExpected: false, 61 | err: nil, 62 | }, 63 | { 64 | name: "notanCommented", 65 | input: `{"Commented":"value4"}`, 66 | output: nil, 67 | errorExpected: true, 68 | err: errors.New("value4 is not a valid Commented"), 69 | }, 70 | } 71 | 72 | for _, test := range tests { 73 | t.Run(test.name, func(tt *testing.T) { 74 | x := &commentedData{} 75 | err := json.Unmarshal([]byte(test.input), x) 76 | if !test.errorExpected { 77 | require.NoError(tt, err, "failed unmarshalling the json.") 78 | assert.Equal(tt, test.output.CommentedX, x.CommentedX) 79 | raw, err := json.Marshal(test.output) 80 | require.NoError(tt, err, "failed marshalling back to json") 81 | require.JSONEq(tt, test.input, string(raw), "json didn't match") 82 | } else { 83 | require.Error(tt, err) 84 | assert.EqualError(tt, err, test.err.Error()) 85 | } 86 | }) 87 | } 88 | } 89 | 90 | type complexCommentedData struct { 91 | ComplexCommentedX ComplexCommented `json:"ComplexCommented,omitempty"` 92 | } 93 | 94 | func TestComplexCommentedEnumString(t *testing.T) { 95 | x := ComplexCommented(109) 96 | assert.Equal(t, "ComplexCommented(109)", x.String()) 97 | x = ComplexCommented(1) 98 | assert.Equal(t, "value1", x.String()) 99 | 100 | y, err := ParseComplexCommented("value3") 101 | require.NoError(t, err, "Failed parsing value3") 102 | assert.Equal(t, ComplexCommentedValue3, y) 103 | 104 | z, err := ParseComplexCommented("value4") 105 | require.Error(t, err, "Shouldn't parse a value4") 106 | assert.Equal(t, ComplexCommented(0), z) 107 | } 108 | 109 | func TestComplexCommentedUnmarshal(t *testing.T) { 110 | tests := []struct { 111 | name string 112 | input string 113 | output *complexCommentedData 114 | errorExpected bool 115 | err error 116 | }{ 117 | { 118 | name: "value1", 119 | input: `{"ComplexCommented":"value1"}`, 120 | output: &complexCommentedData{ComplexCommentedX: ComplexCommentedValue1}, 121 | errorExpected: false, 122 | err: nil, 123 | }, 124 | { 125 | name: "value2", 126 | input: `{"ComplexCommented":"value2"}`, 127 | output: &complexCommentedData{ComplexCommentedX: ComplexCommentedValue2}, 128 | errorExpected: false, 129 | err: nil, 130 | }, 131 | { 132 | name: "value3", 133 | input: `{"ComplexCommented":"value3"}`, 134 | output: &complexCommentedData{ComplexCommentedX: ComplexCommentedValue3}, 135 | errorExpected: false, 136 | err: nil, 137 | }, 138 | { 139 | name: "notanCommented", 140 | input: `{"ComplexCommented":"value4"}`, 141 | output: nil, 142 | errorExpected: true, 143 | err: errors.New("value4 is not a valid ComplexCommented"), 144 | }, 145 | } 146 | 147 | for _, test := range tests { 148 | t.Run(test.name, func(tt *testing.T) { 149 | x := &complexCommentedData{} 150 | err := json.Unmarshal([]byte(test.input), x) 151 | if !test.errorExpected { 152 | require.NoError(tt, err, "failed unmarshalling the json.") 153 | assert.Equal(tt, test.output.ComplexCommentedX, x.ComplexCommentedX) 154 | raw, err := json.Marshal(test.output) 155 | require.NoError(tt, err, "failed marshalling back to json") 156 | require.JSONEq(tt, test.input, string(raw), "json didn't match") 157 | } else { 158 | require.Error(tt, err) 159 | assert.EqualError(tt, err, test.err.Error()) 160 | } 161 | }) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /example/custom_prefix.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --prefix=AcmeInc -b example 2 | 3 | package example 4 | 5 | // Products of AcmeInc ENUM( 6 | // Anvil, 7 | // Dynamite, 8 | // Glue 9 | // ) 10 | type Product int32 11 | -------------------------------------------------------------------------------- /example/custom_prefix_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // AcmeIncProductAnvil is a Product of type Anvil. 19 | AcmeIncProductAnvil Product = iota 20 | // AcmeIncProductDynamite is a Product of type Dynamite. 21 | AcmeIncProductDynamite 22 | // AcmeIncProductGlue is a Product of type Glue. 23 | AcmeIncProductGlue 24 | ) 25 | 26 | var ErrInvalidProduct = errors.New("not a valid Product") 27 | 28 | const _ProductName = "AnvilDynamiteGlue" 29 | 30 | var _ProductMap = map[Product]string{ 31 | AcmeIncProductAnvil: _ProductName[0:5], 32 | AcmeIncProductDynamite: _ProductName[5:13], 33 | AcmeIncProductGlue: _ProductName[13:17], 34 | } 35 | 36 | // String implements the Stringer interface. 37 | func (x Product) String() string { 38 | if str, ok := _ProductMap[x]; ok { 39 | return str 40 | } 41 | return fmt.Sprintf("Product(%d)", x) 42 | } 43 | 44 | // IsValid provides a quick way to determine if the typed value is 45 | // part of the allowed enumerated values 46 | func (x Product) IsValid() bool { 47 | _, ok := _ProductMap[x] 48 | return ok 49 | } 50 | 51 | var _ProductValue = map[string]Product{ 52 | _ProductName[0:5]: AcmeIncProductAnvil, 53 | _ProductName[5:13]: AcmeIncProductDynamite, 54 | _ProductName[13:17]: AcmeIncProductGlue, 55 | } 56 | 57 | // ParseProduct attempts to convert a string to a Product. 58 | func ParseProduct(name string) (Product, error) { 59 | if x, ok := _ProductValue[name]; ok { 60 | return x, nil 61 | } 62 | return Product(0), fmt.Errorf("%s is %w", name, ErrInvalidProduct) 63 | } 64 | -------------------------------------------------------------------------------- /example/custom_prefix_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | type productData struct { 15 | ProductX Product `json:"product"` 16 | } 17 | 18 | func TestProductString(t *testing.T) { 19 | x := Product(109) 20 | assert.Equal(t, "Product(109)", x.String()) 21 | x = Product(1) 22 | assert.Equal(t, "Dynamite", x.String()) 23 | 24 | y, err := ParseProduct("Anvil") 25 | require.NoError(t, err, "Failed parsing anvil") 26 | assert.Equal(t, AcmeIncProductAnvil, y) 27 | 28 | z, err := ParseProduct("Snake") 29 | require.Error(t, err, "Shouldn't parse a snake") 30 | assert.Equal(t, Product(0), z) 31 | } 32 | 33 | func TestProductUnmarshal(t *testing.T) { 34 | tests := []struct { 35 | name string 36 | input string 37 | output *productData 38 | errorExpected bool 39 | err error 40 | }{ 41 | { 42 | name: "anvil", 43 | input: `{"product":0}`, 44 | output: &productData{ProductX: AcmeIncProductAnvil}, 45 | errorExpected: false, 46 | err: nil, 47 | }, 48 | { 49 | name: "dynamite", 50 | input: `{"product":1}`, 51 | output: &productData{ProductX: AcmeIncProductDynamite}, 52 | errorExpected: false, 53 | err: nil, 54 | }, 55 | { 56 | name: "glue", 57 | input: `{"product":2}`, 58 | output: &productData{ProductX: AcmeIncProductGlue}, 59 | errorExpected: false, 60 | err: nil, 61 | }, 62 | { 63 | name: "notanproduct", 64 | input: `{"product":22}`, 65 | output: &productData{ProductX: Product(22)}, 66 | errorExpected: false, 67 | err: nil, 68 | }, 69 | } 70 | 71 | for _, test := range tests { 72 | t.Run(test.name, func(tt *testing.T) { 73 | x := &productData{} 74 | err := json.Unmarshal([]byte(test.input), x) 75 | if !test.errorExpected { 76 | require.NoError(tt, err, "failed unmarshalling the json.") 77 | assert.Equal(tt, test.output.ProductX, x.ProductX) 78 | } else { 79 | require.Error(tt, err) 80 | assert.EqualError(tt, err, test.err.Error()) 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func TestProductMarshal(t *testing.T) { 87 | tests := []struct { 88 | name string 89 | input *productData 90 | output string 91 | errorExpected bool 92 | err error 93 | }{ 94 | { 95 | name: "anvil", 96 | output: `{"product":0}`, 97 | input: &productData{ProductX: AcmeIncProductAnvil}, 98 | errorExpected: false, 99 | err: nil, 100 | }, 101 | { 102 | name: "dynamite", 103 | output: `{"product":1}`, 104 | input: &productData{ProductX: AcmeIncProductDynamite}, 105 | errorExpected: false, 106 | err: nil, 107 | }, 108 | { 109 | name: "glue", 110 | output: `{"product":2}`, 111 | input: &productData{ProductX: AcmeIncProductGlue}, 112 | errorExpected: false, 113 | err: nil, 114 | }, 115 | } 116 | 117 | for _, test := range tests { 118 | t.Run(test.name, func(tt *testing.T) { 119 | raw, err := json.Marshal(test.input) 120 | require.NoError(tt, err, "failed marshalling to json") 121 | assert.JSONEq(tt, test.output, string(raw)) 122 | }) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /example/diff_base.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | //go:generate ../bin/go-enum --forcelower -b example 4 | 5 | /* 6 | ENUM( 7 | 8 | B3 = 03 9 | B4 = 04 10 | B5 = 5 11 | B6 = 0b110 12 | B7 = 0b111 13 | B8 = 0x08 14 | B9 = 0x09 15 | B10 = 0x0B 16 | B11 = 0x2B 17 | 18 | ) 19 | */ 20 | type DiffBase int 21 | -------------------------------------------------------------------------------- /example/diff_base_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // DiffBaseB3 is a DiffBase of type B3. 19 | DiffBaseB3 DiffBase = iota + 3 20 | // DiffBaseB4 is a DiffBase of type B4. 21 | DiffBaseB4 22 | // DiffBaseB5 is a DiffBase of type B5. 23 | DiffBaseB5 24 | // DiffBaseB6 is a DiffBase of type B6. 25 | DiffBaseB6 26 | // DiffBaseB7 is a DiffBase of type B7. 27 | DiffBaseB7 28 | // DiffBaseB8 is a DiffBase of type B8. 29 | DiffBaseB8 30 | // DiffBaseB9 is a DiffBase of type B9. 31 | DiffBaseB9 32 | // DiffBaseB10 is a DiffBase of type B10. 33 | DiffBaseB10 DiffBase = iota + 4 34 | // DiffBaseB11 is a DiffBase of type B11. 35 | DiffBaseB11 DiffBase = iota + 35 36 | ) 37 | 38 | var ErrInvalidDiffBase = errors.New("not a valid DiffBase") 39 | 40 | const _DiffBaseName = "b3b4b5b6b7b8b9b10b11" 41 | 42 | var _DiffBaseMap = map[DiffBase]string{ 43 | DiffBaseB3: _DiffBaseName[0:2], 44 | DiffBaseB4: _DiffBaseName[2:4], 45 | DiffBaseB5: _DiffBaseName[4:6], 46 | DiffBaseB6: _DiffBaseName[6:8], 47 | DiffBaseB7: _DiffBaseName[8:10], 48 | DiffBaseB8: _DiffBaseName[10:12], 49 | DiffBaseB9: _DiffBaseName[12:14], 50 | DiffBaseB10: _DiffBaseName[14:17], 51 | DiffBaseB11: _DiffBaseName[17:20], 52 | } 53 | 54 | // String implements the Stringer interface. 55 | func (x DiffBase) String() string { 56 | if str, ok := _DiffBaseMap[x]; ok { 57 | return str 58 | } 59 | return fmt.Sprintf("DiffBase(%d)", x) 60 | } 61 | 62 | // IsValid provides a quick way to determine if the typed value is 63 | // part of the allowed enumerated values 64 | func (x DiffBase) IsValid() bool { 65 | _, ok := _DiffBaseMap[x] 66 | return ok 67 | } 68 | 69 | var _DiffBaseValue = map[string]DiffBase{ 70 | _DiffBaseName[0:2]: DiffBaseB3, 71 | _DiffBaseName[2:4]: DiffBaseB4, 72 | _DiffBaseName[4:6]: DiffBaseB5, 73 | _DiffBaseName[6:8]: DiffBaseB6, 74 | _DiffBaseName[8:10]: DiffBaseB7, 75 | _DiffBaseName[10:12]: DiffBaseB8, 76 | _DiffBaseName[12:14]: DiffBaseB9, 77 | _DiffBaseName[14:17]: DiffBaseB10, 78 | _DiffBaseName[17:20]: DiffBaseB11, 79 | } 80 | 81 | // ParseDiffBase attempts to convert a string to a DiffBase. 82 | func ParseDiffBase(name string) (DiffBase, error) { 83 | if x, ok := _DiffBaseValue[name]; ok { 84 | return x, nil 85 | } 86 | return DiffBase(0), fmt.Errorf("%s is %w", name, ErrInvalidDiffBase) 87 | } 88 | -------------------------------------------------------------------------------- /example/diff_base_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestDiffBase(t *testing.T) { 13 | tests := map[string]struct { 14 | actual int 15 | expected DiffBase 16 | }{ 17 | "DiffBaseB3": { 18 | actual: 3, 19 | expected: DiffBaseB3, 20 | }, 21 | "DiffBaseB4": { 22 | actual: 4, 23 | expected: DiffBaseB4, 24 | }, 25 | "DiffBaseB5": { 26 | actual: 5, 27 | expected: DiffBaseB5, 28 | }, 29 | "DiffBaseB6": { 30 | actual: 6, 31 | expected: DiffBaseB6, 32 | }, 33 | "DiffBaseB7": { 34 | actual: 7, 35 | expected: DiffBaseB7, 36 | }, 37 | "DiffBaseB8": { 38 | actual: 8, 39 | expected: DiffBaseB8, 40 | }, 41 | "DiffBaseB9": { 42 | actual: 9, 43 | expected: DiffBaseB9, 44 | }, 45 | "DiffBaseB10": { 46 | actual: 11, 47 | expected: DiffBaseB10, 48 | }, 49 | "DiffBaseB11": { 50 | actual: 43, 51 | expected: DiffBaseB11, 52 | }, 53 | } 54 | 55 | for name, tc := range tests { 56 | t.Run(name, func(t *testing.T) { 57 | assert.Equal(t, int(tc.expected), tc.actual) 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/enum_32_bit.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --names -b example 2 | 3 | package example 4 | 5 | /* 6 | ENUM( 7 | 8 | Unkno = 0 9 | E2P15 = 32768 10 | E2P16 = 65536 11 | E2P17 = 131072 12 | E2P18 = 262144 13 | E2P19 = 524288 14 | E2P20 = 1048576 15 | E2P21 = 2097152 16 | E2P22 = 33554432 17 | E2P23 = 67108864 18 | E2P28 = 536870912 19 | E2P30 = 1073741824 20 | 21 | ) 22 | */ 23 | type Enum32bit uint32 24 | -------------------------------------------------------------------------------- /example/enum_32_bit_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "fmt" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | // Enum32bitUnkno is a Enum32bit of type Unkno. 19 | Enum32bitUnkno Enum32bit = iota 20 | // Enum32bitE2P15 is a Enum32bit of type E2P15. 21 | Enum32bitE2P15 Enum32bit = iota + 32767 22 | // Enum32bitE2P16 is a Enum32bit of type E2P16. 23 | Enum32bitE2P16 Enum32bit = iota + 65534 24 | // Enum32bitE2P17 is a Enum32bit of type E2P17. 25 | Enum32bitE2P17 Enum32bit = iota + 131069 26 | // Enum32bitE2P18 is a Enum32bit of type E2P18. 27 | Enum32bitE2P18 Enum32bit = iota + 262140 28 | // Enum32bitE2P19 is a Enum32bit of type E2P19. 29 | Enum32bitE2P19 Enum32bit = iota + 524283 30 | // Enum32bitE2P20 is a Enum32bit of type E2P20. 31 | Enum32bitE2P20 Enum32bit = iota + 1048570 32 | // Enum32bitE2P21 is a Enum32bit of type E2P21. 33 | Enum32bitE2P21 Enum32bit = iota + 2097145 34 | // Enum32bitE2P22 is a Enum32bit of type E2P22. 35 | Enum32bitE2P22 Enum32bit = iota + 33554424 36 | // Enum32bitE2P23 is a Enum32bit of type E2P23. 37 | Enum32bitE2P23 Enum32bit = iota + 67108855 38 | // Enum32bitE2P28 is a Enum32bit of type E2P28. 39 | Enum32bitE2P28 Enum32bit = iota + 536870902 40 | // Enum32bitE2P30 is a Enum32bit of type E2P30. 41 | Enum32bitE2P30 Enum32bit = iota + 1073741813 42 | ) 43 | 44 | var ErrInvalidEnum32bit = fmt.Errorf("not a valid Enum32bit, try [%s]", strings.Join(_Enum32bitNames, ", ")) 45 | 46 | const _Enum32bitName = "UnknoE2P15E2P16E2P17E2P18E2P19E2P20E2P21E2P22E2P23E2P28E2P30" 47 | 48 | var _Enum32bitNames = []string{ 49 | _Enum32bitName[0:5], 50 | _Enum32bitName[5:10], 51 | _Enum32bitName[10:15], 52 | _Enum32bitName[15:20], 53 | _Enum32bitName[20:25], 54 | _Enum32bitName[25:30], 55 | _Enum32bitName[30:35], 56 | _Enum32bitName[35:40], 57 | _Enum32bitName[40:45], 58 | _Enum32bitName[45:50], 59 | _Enum32bitName[50:55], 60 | _Enum32bitName[55:60], 61 | } 62 | 63 | // Enum32bitNames returns a list of possible string values of Enum32bit. 64 | func Enum32bitNames() []string { 65 | tmp := make([]string, len(_Enum32bitNames)) 66 | copy(tmp, _Enum32bitNames) 67 | return tmp 68 | } 69 | 70 | var _Enum32bitMap = map[Enum32bit]string{ 71 | Enum32bitUnkno: _Enum32bitName[0:5], 72 | Enum32bitE2P15: _Enum32bitName[5:10], 73 | Enum32bitE2P16: _Enum32bitName[10:15], 74 | Enum32bitE2P17: _Enum32bitName[15:20], 75 | Enum32bitE2P18: _Enum32bitName[20:25], 76 | Enum32bitE2P19: _Enum32bitName[25:30], 77 | Enum32bitE2P20: _Enum32bitName[30:35], 78 | Enum32bitE2P21: _Enum32bitName[35:40], 79 | Enum32bitE2P22: _Enum32bitName[40:45], 80 | Enum32bitE2P23: _Enum32bitName[45:50], 81 | Enum32bitE2P28: _Enum32bitName[50:55], 82 | Enum32bitE2P30: _Enum32bitName[55:60], 83 | } 84 | 85 | // String implements the Stringer interface. 86 | func (x Enum32bit) String() string { 87 | if str, ok := _Enum32bitMap[x]; ok { 88 | return str 89 | } 90 | return fmt.Sprintf("Enum32bit(%d)", x) 91 | } 92 | 93 | // IsValid provides a quick way to determine if the typed value is 94 | // part of the allowed enumerated values 95 | func (x Enum32bit) IsValid() bool { 96 | _, ok := _Enum32bitMap[x] 97 | return ok 98 | } 99 | 100 | var _Enum32bitValue = map[string]Enum32bit{ 101 | _Enum32bitName[0:5]: Enum32bitUnkno, 102 | _Enum32bitName[5:10]: Enum32bitE2P15, 103 | _Enum32bitName[10:15]: Enum32bitE2P16, 104 | _Enum32bitName[15:20]: Enum32bitE2P17, 105 | _Enum32bitName[20:25]: Enum32bitE2P18, 106 | _Enum32bitName[25:30]: Enum32bitE2P19, 107 | _Enum32bitName[30:35]: Enum32bitE2P20, 108 | _Enum32bitName[35:40]: Enum32bitE2P21, 109 | _Enum32bitName[40:45]: Enum32bitE2P22, 110 | _Enum32bitName[45:50]: Enum32bitE2P23, 111 | _Enum32bitName[50:55]: Enum32bitE2P28, 112 | _Enum32bitName[55:60]: Enum32bitE2P30, 113 | } 114 | 115 | // ParseEnum32bit attempts to convert a string to a Enum32bit. 116 | func ParseEnum32bit(name string) (Enum32bit, error) { 117 | if x, ok := _Enum32bitValue[name]; ok { 118 | return x, nil 119 | } 120 | return Enum32bit(0), fmt.Errorf("%s is %w", name, ErrInvalidEnum32bit) 121 | } 122 | -------------------------------------------------------------------------------- /example/enum_32_bit_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEnum32Bit(t *testing.T) { 13 | tests := map[string]struct { 14 | input string 15 | output Enum32bit 16 | }{ 17 | "E2P15": { 18 | input: `E2P15`, 19 | output: Enum32bitE2P15, 20 | }, 21 | "E2P30": { 22 | input: `E2P30`, 23 | output: Enum32bitE2P30, 24 | }, 25 | } 26 | 27 | for name, tc := range tests { 28 | t.Run(name, func(t *testing.T) { 29 | output, err := ParseEnum32bit(tc.input) 30 | assert.NoError(t, err) 31 | assert.Equal(t, tc.output, output) 32 | 33 | assert.Equal(t, tc.input, output.String()) 34 | }) 35 | } 36 | 37 | t.Run("basics", func(t *testing.T) { 38 | assert.Equal(t, "E2P23", Enum32bitE2P23.String()) 39 | assert.Equal(t, "Enum32bit(99)", Enum32bit(99).String()) 40 | _, err := ParseEnum32bit("-1") 41 | assert.Error(t, err) 42 | 43 | names := Enum32bitNames() 44 | assert.Len(t, names, 12) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /example/enum_64_bit.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --names -b example 2 | 3 | package example 4 | 5 | /* 6 | ENUM( 7 | Unkno = 0 8 | E2P15 = 32768 9 | E2P16 = 65536 10 | E2P17 = 131072 11 | E2P18 = 262144 12 | E2P19 = 524288 13 | E2P20 = 1048576 14 | E2P21 = 2097152 15 | E2P22 = 33554432 16 | E2P23 = 67108864 17 | E2P28 = 536870912 18 | E2P30 = 1073741824 19 | E2P31 = 2147483648 20 | E2P32 = 4294967296 21 | E2P33 = 8454967296 22 | E2P63 = 18446744073709551615 23 | ) 24 | */ 25 | type Enum64bit uint64 26 | -------------------------------------------------------------------------------- /example/enum_64_bit_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "fmt" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | // Enum64bitUnkno is a Enum64bit of type Unkno. 19 | Enum64bitUnkno Enum64bit = iota 20 | // Enum64bitE2P15 is a Enum64bit of type E2P15. 21 | Enum64bitE2P15 Enum64bit = iota + 32767 22 | // Enum64bitE2P16 is a Enum64bit of type E2P16. 23 | Enum64bitE2P16 Enum64bit = iota + 65534 24 | // Enum64bitE2P17 is a Enum64bit of type E2P17. 25 | Enum64bitE2P17 Enum64bit = iota + 131069 26 | // Enum64bitE2P18 is a Enum64bit of type E2P18. 27 | Enum64bitE2P18 Enum64bit = iota + 262140 28 | // Enum64bitE2P19 is a Enum64bit of type E2P19. 29 | Enum64bitE2P19 Enum64bit = iota + 524283 30 | // Enum64bitE2P20 is a Enum64bit of type E2P20. 31 | Enum64bitE2P20 Enum64bit = iota + 1048570 32 | // Enum64bitE2P21 is a Enum64bit of type E2P21. 33 | Enum64bitE2P21 Enum64bit = iota + 2097145 34 | // Enum64bitE2P22 is a Enum64bit of type E2P22. 35 | Enum64bitE2P22 Enum64bit = iota + 33554424 36 | // Enum64bitE2P23 is a Enum64bit of type E2P23. 37 | Enum64bitE2P23 Enum64bit = iota + 67108855 38 | // Enum64bitE2P28 is a Enum64bit of type E2P28. 39 | Enum64bitE2P28 Enum64bit = iota + 536870902 40 | // Enum64bitE2P30 is a Enum64bit of type E2P30. 41 | Enum64bitE2P30 Enum64bit = iota + 1073741813 42 | // Enum64bitE2P31 is a Enum64bit of type E2P31. 43 | Enum64bitE2P31 Enum64bit = iota + 2147483636 44 | // Enum64bitE2P32 is a Enum64bit of type E2P32. 45 | Enum64bitE2P32 Enum64bit = iota + 4294967283 46 | // Enum64bitE2P33 is a Enum64bit of type E2P33. 47 | Enum64bitE2P33 Enum64bit = iota + 8454967282 48 | // Enum64bitE2P63 is a Enum64bit of type E2P63. 49 | Enum64bitE2P63 Enum64bit = iota + 18446744073709551600 50 | ) 51 | 52 | var ErrInvalidEnum64bit = fmt.Errorf("not a valid Enum64bit, try [%s]", strings.Join(_Enum64bitNames, ", ")) 53 | 54 | const _Enum64bitName = "UnknoE2P15E2P16E2P17E2P18E2P19E2P20E2P21E2P22E2P23E2P28E2P30E2P31E2P32E2P33E2P63" 55 | 56 | var _Enum64bitNames = []string{ 57 | _Enum64bitName[0:5], 58 | _Enum64bitName[5:10], 59 | _Enum64bitName[10:15], 60 | _Enum64bitName[15:20], 61 | _Enum64bitName[20:25], 62 | _Enum64bitName[25:30], 63 | _Enum64bitName[30:35], 64 | _Enum64bitName[35:40], 65 | _Enum64bitName[40:45], 66 | _Enum64bitName[45:50], 67 | _Enum64bitName[50:55], 68 | _Enum64bitName[55:60], 69 | _Enum64bitName[60:65], 70 | _Enum64bitName[65:70], 71 | _Enum64bitName[70:75], 72 | _Enum64bitName[75:80], 73 | } 74 | 75 | // Enum64bitNames returns a list of possible string values of Enum64bit. 76 | func Enum64bitNames() []string { 77 | tmp := make([]string, len(_Enum64bitNames)) 78 | copy(tmp, _Enum64bitNames) 79 | return tmp 80 | } 81 | 82 | var _Enum64bitMap = map[Enum64bit]string{ 83 | Enum64bitUnkno: _Enum64bitName[0:5], 84 | Enum64bitE2P15: _Enum64bitName[5:10], 85 | Enum64bitE2P16: _Enum64bitName[10:15], 86 | Enum64bitE2P17: _Enum64bitName[15:20], 87 | Enum64bitE2P18: _Enum64bitName[20:25], 88 | Enum64bitE2P19: _Enum64bitName[25:30], 89 | Enum64bitE2P20: _Enum64bitName[30:35], 90 | Enum64bitE2P21: _Enum64bitName[35:40], 91 | Enum64bitE2P22: _Enum64bitName[40:45], 92 | Enum64bitE2P23: _Enum64bitName[45:50], 93 | Enum64bitE2P28: _Enum64bitName[50:55], 94 | Enum64bitE2P30: _Enum64bitName[55:60], 95 | Enum64bitE2P31: _Enum64bitName[60:65], 96 | Enum64bitE2P32: _Enum64bitName[65:70], 97 | Enum64bitE2P33: _Enum64bitName[70:75], 98 | Enum64bitE2P63: _Enum64bitName[75:80], 99 | } 100 | 101 | // String implements the Stringer interface. 102 | func (x Enum64bit) String() string { 103 | if str, ok := _Enum64bitMap[x]; ok { 104 | return str 105 | } 106 | return fmt.Sprintf("Enum64bit(%d)", x) 107 | } 108 | 109 | // IsValid provides a quick way to determine if the typed value is 110 | // part of the allowed enumerated values 111 | func (x Enum64bit) IsValid() bool { 112 | _, ok := _Enum64bitMap[x] 113 | return ok 114 | } 115 | 116 | var _Enum64bitValue = map[string]Enum64bit{ 117 | _Enum64bitName[0:5]: Enum64bitUnkno, 118 | _Enum64bitName[5:10]: Enum64bitE2P15, 119 | _Enum64bitName[10:15]: Enum64bitE2P16, 120 | _Enum64bitName[15:20]: Enum64bitE2P17, 121 | _Enum64bitName[20:25]: Enum64bitE2P18, 122 | _Enum64bitName[25:30]: Enum64bitE2P19, 123 | _Enum64bitName[30:35]: Enum64bitE2P20, 124 | _Enum64bitName[35:40]: Enum64bitE2P21, 125 | _Enum64bitName[40:45]: Enum64bitE2P22, 126 | _Enum64bitName[45:50]: Enum64bitE2P23, 127 | _Enum64bitName[50:55]: Enum64bitE2P28, 128 | _Enum64bitName[55:60]: Enum64bitE2P30, 129 | _Enum64bitName[60:65]: Enum64bitE2P31, 130 | _Enum64bitName[65:70]: Enum64bitE2P32, 131 | _Enum64bitName[70:75]: Enum64bitE2P33, 132 | _Enum64bitName[75:80]: Enum64bitE2P63, 133 | } 134 | 135 | // ParseEnum64bit attempts to convert a string to a Enum64bit. 136 | func ParseEnum64bit(name string) (Enum64bit, error) { 137 | if x, ok := _Enum64bitValue[name]; ok { 138 | return x, nil 139 | } 140 | return Enum64bit(0), fmt.Errorf("%s is %w", name, ErrInvalidEnum64bit) 141 | } 142 | -------------------------------------------------------------------------------- /example/enum_64_bit_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEnum64Bit(t *testing.T) { 13 | tests := map[string]struct { 14 | input string 15 | output Enum64bit 16 | }{ 17 | "E2P15": { 18 | input: `E2P15`, 19 | output: Enum64bitE2P15, 20 | }, 21 | "E2P63": { 22 | input: `E2P63`, 23 | output: Enum64bitE2P63, 24 | }, 25 | } 26 | 27 | for name, tc := range tests { 28 | t.Run(name, func(t *testing.T) { 29 | output, err := ParseEnum64bit(tc.input) 30 | assert.NoError(t, err) 31 | assert.Equal(t, tc.output, output) 32 | 33 | assert.Equal(t, tc.input, output.String()) 34 | }) 35 | } 36 | 37 | t.Run("basics", func(t *testing.T) { 38 | assert.Equal(t, "E2P23", Enum64bitE2P23.String()) 39 | assert.Equal(t, "Enum64bit(99)", Enum64bit(99).String()) 40 | _, err := ParseEnum64bit("-1") 41 | assert.Error(t, err) 42 | 43 | names := Enum64bitNames() 44 | assert.Len(t, names, 16) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --marshal --nocase --flag --names --values -b example 2 | 3 | package example 4 | 5 | // X is doc'ed 6 | type X struct{} 7 | 8 | // Make x ENUM(Toyota,_,Chevy,_,Ford,_,Tesla,_,Hyundai,_,Nissan,_,Jaguar,_,Audi,_,BMW,_,Mercedes-Benz,_,Volkswagon) 9 | type Make int32 10 | 11 | // Make x ENUM(start=20,middle,end,ps,pps,ppps) 12 | type NoZeros int32 13 | -------------------------------------------------------------------------------- /example/example_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "fmt" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | // MakeToyota is a Make of type Toyota. 19 | MakeToyota Make = iota 20 | // Skipped value. 21 | _ 22 | // MakeChevy is a Make of type Chevy. 23 | MakeChevy 24 | // Skipped value. 25 | _ 26 | // MakeFord is a Make of type Ford. 27 | MakeFord 28 | // Skipped value. 29 | _ 30 | // MakeTesla is a Make of type Tesla. 31 | MakeTesla 32 | // Skipped value. 33 | _ 34 | // MakeHyundai is a Make of type Hyundai. 35 | MakeHyundai 36 | // Skipped value. 37 | _ 38 | // MakeNissan is a Make of type Nissan. 39 | MakeNissan 40 | // Skipped value. 41 | _ 42 | // MakeJaguar is a Make of type Jaguar. 43 | MakeJaguar 44 | // Skipped value. 45 | _ 46 | // MakeAudi is a Make of type Audi. 47 | MakeAudi 48 | // Skipped value. 49 | _ 50 | // MakeBMW is a Make of type BMW. 51 | MakeBMW 52 | // Skipped value. 53 | _ 54 | // MakeMercedesBenz is a Make of type Mercedes-Benz. 55 | MakeMercedesBenz 56 | // Skipped value. 57 | _ 58 | // MakeVolkswagon is a Make of type Volkswagon. 59 | MakeVolkswagon 60 | ) 61 | 62 | var ErrInvalidMake = fmt.Errorf("not a valid Make, try [%s]", strings.Join(_MakeNames, ", ")) 63 | 64 | const _MakeName = "ToyotaChevyFordTeslaHyundaiNissanJaguarAudiBMWMercedes-BenzVolkswagon" 65 | 66 | var _MakeNames = []string{ 67 | _MakeName[0:6], 68 | _MakeName[6:11], 69 | _MakeName[11:15], 70 | _MakeName[15:20], 71 | _MakeName[20:27], 72 | _MakeName[27:33], 73 | _MakeName[33:39], 74 | _MakeName[39:43], 75 | _MakeName[43:46], 76 | _MakeName[46:59], 77 | _MakeName[59:69], 78 | } 79 | 80 | // MakeNames returns a list of possible string values of Make. 81 | func MakeNames() []string { 82 | tmp := make([]string, len(_MakeNames)) 83 | copy(tmp, _MakeNames) 84 | return tmp 85 | } 86 | 87 | // MakeValues returns a list of the values for Make 88 | func MakeValues() []Make { 89 | return []Make{ 90 | MakeToyota, 91 | MakeChevy, 92 | MakeFord, 93 | MakeTesla, 94 | MakeHyundai, 95 | MakeNissan, 96 | MakeJaguar, 97 | MakeAudi, 98 | MakeBMW, 99 | MakeMercedesBenz, 100 | MakeVolkswagon, 101 | } 102 | } 103 | 104 | var _MakeMap = map[Make]string{ 105 | MakeToyota: _MakeName[0:6], 106 | MakeChevy: _MakeName[6:11], 107 | MakeFord: _MakeName[11:15], 108 | MakeTesla: _MakeName[15:20], 109 | MakeHyundai: _MakeName[20:27], 110 | MakeNissan: _MakeName[27:33], 111 | MakeJaguar: _MakeName[33:39], 112 | MakeAudi: _MakeName[39:43], 113 | MakeBMW: _MakeName[43:46], 114 | MakeMercedesBenz: _MakeName[46:59], 115 | MakeVolkswagon: _MakeName[59:69], 116 | } 117 | 118 | // String implements the Stringer interface. 119 | func (x Make) String() string { 120 | if str, ok := _MakeMap[x]; ok { 121 | return str 122 | } 123 | return fmt.Sprintf("Make(%d)", x) 124 | } 125 | 126 | // IsValid provides a quick way to determine if the typed value is 127 | // part of the allowed enumerated values 128 | func (x Make) IsValid() bool { 129 | _, ok := _MakeMap[x] 130 | return ok 131 | } 132 | 133 | var _MakeValue = map[string]Make{ 134 | _MakeName[0:6]: MakeToyota, 135 | strings.ToLower(_MakeName[0:6]): MakeToyota, 136 | _MakeName[6:11]: MakeChevy, 137 | strings.ToLower(_MakeName[6:11]): MakeChevy, 138 | _MakeName[11:15]: MakeFord, 139 | strings.ToLower(_MakeName[11:15]): MakeFord, 140 | _MakeName[15:20]: MakeTesla, 141 | strings.ToLower(_MakeName[15:20]): MakeTesla, 142 | _MakeName[20:27]: MakeHyundai, 143 | strings.ToLower(_MakeName[20:27]): MakeHyundai, 144 | _MakeName[27:33]: MakeNissan, 145 | strings.ToLower(_MakeName[27:33]): MakeNissan, 146 | _MakeName[33:39]: MakeJaguar, 147 | strings.ToLower(_MakeName[33:39]): MakeJaguar, 148 | _MakeName[39:43]: MakeAudi, 149 | strings.ToLower(_MakeName[39:43]): MakeAudi, 150 | _MakeName[43:46]: MakeBMW, 151 | strings.ToLower(_MakeName[43:46]): MakeBMW, 152 | _MakeName[46:59]: MakeMercedesBenz, 153 | strings.ToLower(_MakeName[46:59]): MakeMercedesBenz, 154 | _MakeName[59:69]: MakeVolkswagon, 155 | strings.ToLower(_MakeName[59:69]): MakeVolkswagon, 156 | } 157 | 158 | // ParseMake attempts to convert a string to a Make. 159 | func ParseMake(name string) (Make, error) { 160 | if x, ok := _MakeValue[name]; ok { 161 | return x, nil 162 | } 163 | // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. 164 | if x, ok := _MakeValue[strings.ToLower(name)]; ok { 165 | return x, nil 166 | } 167 | return Make(0), fmt.Errorf("%s is %w", name, ErrInvalidMake) 168 | } 169 | 170 | // MarshalText implements the text marshaller method. 171 | func (x Make) MarshalText() ([]byte, error) { 172 | return []byte(x.String()), nil 173 | } 174 | 175 | // UnmarshalText implements the text unmarshaller method. 176 | func (x *Make) UnmarshalText(text []byte) error { 177 | name := string(text) 178 | tmp, err := ParseMake(name) 179 | if err != nil { 180 | return err 181 | } 182 | *x = tmp 183 | return nil 184 | } 185 | 186 | // Set implements the Golang flag.Value interface func. 187 | func (x *Make) Set(val string) error { 188 | v, err := ParseMake(val) 189 | *x = v 190 | return err 191 | } 192 | 193 | // Get implements the Golang flag.Getter interface func. 194 | func (x *Make) Get() interface{} { 195 | return *x 196 | } 197 | 198 | // Type implements the github.com/spf13/pFlag Value interface. 199 | func (x *Make) Type() string { 200 | return "Make" 201 | } 202 | 203 | const ( 204 | // NoZerosStart is a NoZeros of type Start. 205 | NoZerosStart NoZeros = iota + 20 206 | // NoZerosMiddle is a NoZeros of type Middle. 207 | NoZerosMiddle 208 | // NoZerosEnd is a NoZeros of type End. 209 | NoZerosEnd 210 | // NoZerosPs is a NoZeros of type Ps. 211 | NoZerosPs 212 | // NoZerosPps is a NoZeros of type Pps. 213 | NoZerosPps 214 | // NoZerosPpps is a NoZeros of type Ppps. 215 | NoZerosPpps 216 | ) 217 | 218 | var ErrInvalidNoZeros = fmt.Errorf("not a valid NoZeros, try [%s]", strings.Join(_NoZerosNames, ", ")) 219 | 220 | const _NoZerosName = "startmiddleendpsppsppps" 221 | 222 | var _NoZerosNames = []string{ 223 | _NoZerosName[0:5], 224 | _NoZerosName[5:11], 225 | _NoZerosName[11:14], 226 | _NoZerosName[14:16], 227 | _NoZerosName[16:19], 228 | _NoZerosName[19:23], 229 | } 230 | 231 | // NoZerosNames returns a list of possible string values of NoZeros. 232 | func NoZerosNames() []string { 233 | tmp := make([]string, len(_NoZerosNames)) 234 | copy(tmp, _NoZerosNames) 235 | return tmp 236 | } 237 | 238 | // NoZerosValues returns a list of the values for NoZeros 239 | func NoZerosValues() []NoZeros { 240 | return []NoZeros{ 241 | NoZerosStart, 242 | NoZerosMiddle, 243 | NoZerosEnd, 244 | NoZerosPs, 245 | NoZerosPps, 246 | NoZerosPpps, 247 | } 248 | } 249 | 250 | var _NoZerosMap = map[NoZeros]string{ 251 | NoZerosStart: _NoZerosName[0:5], 252 | NoZerosMiddle: _NoZerosName[5:11], 253 | NoZerosEnd: _NoZerosName[11:14], 254 | NoZerosPs: _NoZerosName[14:16], 255 | NoZerosPps: _NoZerosName[16:19], 256 | NoZerosPpps: _NoZerosName[19:23], 257 | } 258 | 259 | // String implements the Stringer interface. 260 | func (x NoZeros) String() string { 261 | if str, ok := _NoZerosMap[x]; ok { 262 | return str 263 | } 264 | return fmt.Sprintf("NoZeros(%d)", x) 265 | } 266 | 267 | // IsValid provides a quick way to determine if the typed value is 268 | // part of the allowed enumerated values 269 | func (x NoZeros) IsValid() bool { 270 | _, ok := _NoZerosMap[x] 271 | return ok 272 | } 273 | 274 | var _NoZerosValue = map[string]NoZeros{ 275 | _NoZerosName[0:5]: NoZerosStart, 276 | strings.ToLower(_NoZerosName[0:5]): NoZerosStart, 277 | _NoZerosName[5:11]: NoZerosMiddle, 278 | strings.ToLower(_NoZerosName[5:11]): NoZerosMiddle, 279 | _NoZerosName[11:14]: NoZerosEnd, 280 | strings.ToLower(_NoZerosName[11:14]): NoZerosEnd, 281 | _NoZerosName[14:16]: NoZerosPs, 282 | strings.ToLower(_NoZerosName[14:16]): NoZerosPs, 283 | _NoZerosName[16:19]: NoZerosPps, 284 | strings.ToLower(_NoZerosName[16:19]): NoZerosPps, 285 | _NoZerosName[19:23]: NoZerosPpps, 286 | strings.ToLower(_NoZerosName[19:23]): NoZerosPpps, 287 | } 288 | 289 | // ParseNoZeros attempts to convert a string to a NoZeros. 290 | func ParseNoZeros(name string) (NoZeros, error) { 291 | if x, ok := _NoZerosValue[name]; ok { 292 | return x, nil 293 | } 294 | // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. 295 | if x, ok := _NoZerosValue[strings.ToLower(name)]; ok { 296 | return x, nil 297 | } 298 | return NoZeros(0), fmt.Errorf("%s is %w", name, ErrInvalidNoZeros) 299 | } 300 | 301 | // MarshalText implements the text marshaller method. 302 | func (x NoZeros) MarshalText() ([]byte, error) { 303 | return []byte(x.String()), nil 304 | } 305 | 306 | // UnmarshalText implements the text unmarshaller method. 307 | func (x *NoZeros) UnmarshalText(text []byte) error { 308 | name := string(text) 309 | tmp, err := ParseNoZeros(name) 310 | if err != nil { 311 | return err 312 | } 313 | *x = tmp 314 | return nil 315 | } 316 | 317 | // Set implements the Golang flag.Value interface func. 318 | func (x *NoZeros) Set(val string) error { 319 | v, err := ParseNoZeros(val) 320 | *x = v 321 | return err 322 | } 323 | 324 | // Get implements the Golang flag.Getter interface func. 325 | func (x *NoZeros) Get() interface{} { 326 | return *x 327 | } 328 | 329 | // Type implements the github.com/spf13/pFlag Value interface. 330 | func (x *NoZeros) Type() string { 331 | return "NoZeros" 332 | } 333 | -------------------------------------------------------------------------------- /example/example_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "strings" 12 | "testing" 13 | 14 | "github.com/spf13/pflag" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | type makeTest struct { 20 | M Make `json:"make"` 21 | } 22 | 23 | func TestMake(t *testing.T) { 24 | assert.Equal(t, "Ford", MakeFord.String()) 25 | assert.Equal(t, "Make(99)", Make(99).String()) 26 | ford := MakeFord 27 | assert.Implements(t, (*flag.Value)(nil), &ford) 28 | assert.Implements(t, (*flag.Getter)(nil), &ford) 29 | assert.Implements(t, (*pflag.Value)(nil), &ford) 30 | 31 | names := MakeNames() 32 | assert.Len(t, names, 11) 33 | } 34 | 35 | var makeTests = []struct { 36 | name string 37 | input string 38 | output *makeTest 39 | errorExpected bool 40 | err error 41 | caseChanged bool 42 | }{ 43 | { 44 | name: "toyota", 45 | input: `{"make":"Toyota"}`, 46 | output: &makeTest{M: MakeToyota}, 47 | errorExpected: false, 48 | err: nil, 49 | }, 50 | { 51 | name: "chevy", 52 | input: `{"make":"Chevy"}`, 53 | output: &makeTest{M: MakeChevy}, 54 | errorExpected: false, 55 | err: nil, 56 | }, 57 | { 58 | name: "ford", 59 | input: `{"make":"Ford"}`, 60 | output: &makeTest{M: MakeFord}, 61 | errorExpected: false, 62 | err: nil, 63 | }, 64 | { 65 | name: "tesla", 66 | input: `{"make":"Tesla"}`, 67 | output: &makeTest{M: MakeTesla}, 68 | errorExpected: false, 69 | err: nil, 70 | }, 71 | { 72 | name: "hyundai", 73 | input: `{"make":"Hyundai"}`, 74 | output: &makeTest{M: MakeHyundai}, 75 | errorExpected: false, 76 | err: nil, 77 | }, 78 | { 79 | name: "nissan", 80 | input: `{"make":"Nissan"}`, 81 | output: &makeTest{M: MakeNissan}, 82 | errorExpected: false, 83 | err: nil, 84 | }, 85 | { 86 | name: "jaguar", 87 | input: `{"make":"Jaguar"}`, 88 | output: &makeTest{M: MakeJaguar}, 89 | errorExpected: false, 90 | err: nil, 91 | }, 92 | { 93 | name: "audi", 94 | input: `{"make":"Audi"}`, 95 | output: &makeTest{M: MakeAudi}, 96 | errorExpected: false, 97 | err: nil, 98 | }, 99 | { 100 | name: "AUDI", 101 | input: `{"make":"AUDI"}`, 102 | output: &makeTest{M: MakeAudi}, 103 | errorExpected: false, 104 | err: nil, 105 | caseChanged: true, 106 | }, 107 | { 108 | name: "bmw", 109 | input: `{"make":"BMW"}`, 110 | output: &makeTest{M: MakeBMW}, 111 | errorExpected: false, 112 | err: nil, 113 | }, 114 | { 115 | name: "mercedes", 116 | input: `{"make":"Mercedes-Benz"}`, 117 | output: &makeTest{M: MakeMercedesBenz}, 118 | errorExpected: false, 119 | err: nil, 120 | }, 121 | { 122 | name: "volkswagon", 123 | input: `{"make":"Volkswagon"}`, 124 | output: &makeTest{M: MakeVolkswagon}, 125 | errorExpected: false, 126 | err: nil, 127 | }, 128 | { 129 | name: "porsche", 130 | input: `{"make":"Porsche"}`, 131 | output: &makeTest{M: MakeVolkswagon}, 132 | errorExpected: true, 133 | err: errors.New("Porsche is not a valid Make, try [Toyota, Chevy, Ford, Tesla, Hyundai, Nissan, Jaguar, Audi, BMW, Mercedes-Benz, Volkswagon]"), 134 | }, 135 | } 136 | 137 | func TestMakeUnmarshal(t *testing.T) { 138 | for _, test := range makeTests { 139 | t.Run(test.name, func(tt *testing.T) { 140 | x := &makeTest{} 141 | err := json.Unmarshal([]byte(test.input), x) 142 | if !test.errorExpected { 143 | require.NoError(tt, err, "failed unmarshalling the json.") 144 | assert.Equal(tt, test.output.M, x.M) 145 | 146 | // Values won't be exactly the same, so we just validate that it was unmarshalled correctly. 147 | if !test.caseChanged { 148 | // Marshal back 149 | raw, err := json.Marshal(test.output) 150 | require.NoError(tt, err, "failed marshalling back to json") 151 | require.JSONEq(tt, test.input, string(raw), "json didn't match") 152 | } 153 | } else { 154 | require.Error(tt, err) 155 | assert.EqualError(tt, err, test.err.Error()) 156 | } 157 | }) 158 | } 159 | } 160 | 161 | func TestFlagInterface(t *testing.T) { 162 | for _, test := range makeTests { 163 | t.Run(test.name, func(tt *testing.T) { 164 | if test.output != nil { 165 | var tmp Make 166 | m := &tmp 167 | require.Equal(tt, m.Type(), "Make") 168 | require.Equal(tt, Make(0), m.Get(), "Unset value should be default") 169 | require.NoError(tt, m.Set(test.output.M.String()), "failed setting flag value") 170 | } 171 | }) 172 | } 173 | } 174 | 175 | func TestNoZeroValues(t *testing.T) { 176 | t.Run("base", func(tt *testing.T) { 177 | assert.Equal(tt, 20, int(NoZerosStart)) 178 | assert.Equal(tt, 21, int(NoZerosMiddle)) 179 | assert.Equal(tt, 22, int(NoZerosEnd)) 180 | assert.Equal(tt, 23, int(NoZerosPs)) 181 | assert.Equal(tt, 24, int(NoZerosPps)) 182 | assert.Equal(tt, 25, int(NoZerosPpps)) 183 | assert.Equal(tt, "ppps", NoZerosPpps.String()) 184 | assert.Equal(tt, "NoZeros(4)", NoZeros(4).String()) 185 | 186 | assert.Len(tt, NoZerosNames(), 6) 187 | 188 | _, err := ParseNoZeros("pppps") 189 | assert.EqualError(tt, err, "pppps is not a valid NoZeros, try [start, middle, end, ps, pps, ppps]") 190 | 191 | tmp, _ := ParseNoZeros("ppps") 192 | assert.Equal(tt, NoZerosPpps, tmp) 193 | 194 | tmp, _ = ParseNoZeros("PppS") 195 | assert.Equal(tt, NoZerosPpps, tmp) 196 | 197 | val := map[string]*NoZeros{} 198 | 199 | err = json.Unmarshal([]byte(`{"nz":"pppps"}`), &val) 200 | assert.EqualError(tt, err, "pppps is not a valid NoZeros, try [start, middle, end, ps, pps, ppps]") 201 | }) 202 | 203 | for _, name := range NoZerosNames() { 204 | t.Run(name, func(tt *testing.T) { 205 | val := map[string]*NoZeros{} 206 | 207 | rawJSON := fmt.Sprintf(`{"val":"%s"}`, name) 208 | 209 | require.NoError(tt, json.Unmarshal([]byte(rawJSON), &val), "Failed unmarshalling no zero") 210 | marshalled, err := json.Marshal(val) 211 | require.NoError(tt, err, "failed marshalling back to json") 212 | require.JSONEq(tt, rawJSON, string(marshalled), "marshalled json did not match") 213 | 214 | // Flag 215 | var tmp NoZeros 216 | nz := &tmp 217 | require.Equal(tt, nz.Type(), "NoZeros") 218 | require.Equal(tt, NoZeros(0), nz.Get(), "Unset value should be default") 219 | require.NoError(tt, nz.Set(name), "failed setting flag value") 220 | }) 221 | } 222 | } 223 | 224 | func BenchmarkMakeParse(b *testing.B) { 225 | knownItems := map[string]struct { 226 | input string 227 | output Make 228 | err error 229 | }{ 230 | "cased lookup": { 231 | input: MakeAudi.String(), 232 | output: MakeAudi, 233 | }, 234 | "lowercase lookup": { 235 | input: strings.ToLower(MakeVolkswagon.String()), 236 | output: MakeVolkswagon, 237 | }, 238 | "hyphenated upper": { 239 | input: strings.ToUpper(MakeMercedesBenz.String()), 240 | output: MakeMercedesBenz, 241 | }, 242 | // Leave this in to add an int as string parsing option in future. 243 | // "numeric": { 244 | // input: "2", 245 | // output: MakeChevy, 246 | // }, 247 | // "last numeric": { 248 | // input: "20", 249 | // output: MakeVolkswagon, 250 | // }, 251 | "failure": { 252 | input: "xyz", 253 | output: MakeToyota, 254 | err: errors.New("xyz is not a valid Make, try [Toyota, Chevy, Ford, Tesla, Hyundai, Nissan, Jaguar, Audi, BMW, Mercedes-Benz, Volkswagon]"), 255 | }, 256 | } 257 | 258 | for name, tc := range knownItems { 259 | b.Run(name, func(b *testing.B) { 260 | b.ReportAllocs() 261 | 262 | b.ResetTimer() 263 | for i := 0; i < b.N; i++ { 264 | out, err := ParseMake(tc.input) 265 | assert.Equal(b, tc.output, out) 266 | if tc.err != nil { 267 | assert.Error(b, err) 268 | } else { 269 | assert.NoError(b, err) 270 | } 271 | } 272 | }) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /example/force_lower.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --forcelower -b example 2 | 3 | package example 4 | 5 | // ENUM( 6 | // DataSwap, 7 | // BootNode, 8 | // ) 9 | type ForceLowerType int 10 | -------------------------------------------------------------------------------- /example/force_lower_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // ForceLowerTypeDataSwap is a ForceLowerType of type DataSwap. 19 | ForceLowerTypeDataSwap ForceLowerType = iota 20 | // ForceLowerTypeBootNode is a ForceLowerType of type BootNode. 21 | ForceLowerTypeBootNode 22 | ) 23 | 24 | var ErrInvalidForceLowerType = errors.New("not a valid ForceLowerType") 25 | 26 | const _ForceLowerTypeName = "dataswapbootnode" 27 | 28 | var _ForceLowerTypeMap = map[ForceLowerType]string{ 29 | ForceLowerTypeDataSwap: _ForceLowerTypeName[0:8], 30 | ForceLowerTypeBootNode: _ForceLowerTypeName[8:16], 31 | } 32 | 33 | // String implements the Stringer interface. 34 | func (x ForceLowerType) String() string { 35 | if str, ok := _ForceLowerTypeMap[x]; ok { 36 | return str 37 | } 38 | return fmt.Sprintf("ForceLowerType(%d)", x) 39 | } 40 | 41 | // IsValid provides a quick way to determine if the typed value is 42 | // part of the allowed enumerated values 43 | func (x ForceLowerType) IsValid() bool { 44 | _, ok := _ForceLowerTypeMap[x] 45 | return ok 46 | } 47 | 48 | var _ForceLowerTypeValue = map[string]ForceLowerType{ 49 | _ForceLowerTypeName[0:8]: ForceLowerTypeDataSwap, 50 | _ForceLowerTypeName[8:16]: ForceLowerTypeBootNode, 51 | } 52 | 53 | // ParseForceLowerType attempts to convert a string to a ForceLowerType. 54 | func ParseForceLowerType(name string) (ForceLowerType, error) { 55 | if x, ok := _ForceLowerTypeValue[name]; ok { 56 | return x, nil 57 | } 58 | return ForceLowerType(0), fmt.Errorf("%s is %w", name, ErrInvalidForceLowerType) 59 | } 60 | -------------------------------------------------------------------------------- /example/force_lower_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestForceLowerString(t *testing.T) { 13 | tests := map[string]struct { 14 | input string 15 | output ForceLowerType 16 | }{ 17 | "dataswap": { 18 | input: `dataswap`, 19 | output: ForceLowerTypeDataSwap, 20 | }, 21 | "bootnode": { 22 | input: `bootnode`, 23 | output: ForceLowerTypeBootNode, 24 | }, 25 | } 26 | 27 | for name, tc := range tests { 28 | t.Run(name, func(t *testing.T) { 29 | output, err := ParseForceLowerType(tc.input) 30 | assert.NoError(t, err) 31 | assert.Equal(t, tc.output, output) 32 | 33 | assert.Equal(t, tc.input, output.String()) 34 | }) 35 | } 36 | 37 | t.Run("failures", func(t *testing.T) { 38 | assert.Equal(t, "ForceLowerType(99)", ForceLowerType(99).String()) 39 | _, err := ParseForceLowerType("-1") 40 | assert.Error(t, err) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /example/force_upper.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --forceupper -b example 2 | 3 | package example 4 | 5 | // ENUM( 6 | // DataSwap, 7 | // BootNode, 8 | // ) 9 | type ForceUpperType int 10 | -------------------------------------------------------------------------------- /example/force_upper_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // ForceUpperTypeDataSwap is a ForceUpperType of type DataSwap. 19 | ForceUpperTypeDataSwap ForceUpperType = iota 20 | // ForceUpperTypeBootNode is a ForceUpperType of type BootNode. 21 | ForceUpperTypeBootNode 22 | ) 23 | 24 | var ErrInvalidForceUpperType = errors.New("not a valid ForceUpperType") 25 | 26 | const _ForceUpperTypeName = "DATASWAPBOOTNODE" 27 | 28 | var _ForceUpperTypeMap = map[ForceUpperType]string{ 29 | ForceUpperTypeDataSwap: _ForceUpperTypeName[0:8], 30 | ForceUpperTypeBootNode: _ForceUpperTypeName[8:16], 31 | } 32 | 33 | // String implements the Stringer interface. 34 | func (x ForceUpperType) String() string { 35 | if str, ok := _ForceUpperTypeMap[x]; ok { 36 | return str 37 | } 38 | return fmt.Sprintf("ForceUpperType(%d)", x) 39 | } 40 | 41 | // IsValid provides a quick way to determine if the typed value is 42 | // part of the allowed enumerated values 43 | func (x ForceUpperType) IsValid() bool { 44 | _, ok := _ForceUpperTypeMap[x] 45 | return ok 46 | } 47 | 48 | var _ForceUpperTypeValue = map[string]ForceUpperType{ 49 | _ForceUpperTypeName[0:8]: ForceUpperTypeDataSwap, 50 | _ForceUpperTypeName[8:16]: ForceUpperTypeBootNode, 51 | } 52 | 53 | // ParseForceUpperType attempts to convert a string to a ForceUpperType. 54 | func ParseForceUpperType(name string) (ForceUpperType, error) { 55 | if x, ok := _ForceUpperTypeValue[name]; ok { 56 | return x, nil 57 | } 58 | return ForceUpperType(0), fmt.Errorf("%s is %w", name, ErrInvalidForceUpperType) 59 | } 60 | -------------------------------------------------------------------------------- /example/force_upper_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestForceUpperString(t *testing.T) { 13 | tests := map[string]struct { 14 | input string 15 | output ForceUpperType 16 | }{ 17 | "dataswap": { 18 | input: `DATASWAP`, 19 | output: ForceUpperTypeDataSwap, 20 | }, 21 | "bootnode": { 22 | input: `BOOTNODE`, 23 | output: ForceUpperTypeBootNode, 24 | }, 25 | } 26 | 27 | for name, tc := range tests { 28 | t.Run(name, func(t *testing.T) { 29 | output, err := ParseForceUpperType(tc.input) 30 | assert.NoError(t, err) 31 | assert.Equal(t, tc.output, output) 32 | 33 | assert.Equal(t, tc.input, output.String()) 34 | }) 35 | } 36 | 37 | t.Run("failures", func(t *testing.T) { 38 | assert.Equal(t, "ForceUpperType(99)", ForceUpperType(99).String()) 39 | _, err := ParseForceUpperType("-1") 40 | assert.Error(t, err) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /example/globs/gen.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | //go:generate ../../bin/go-enum -f=*.go -b example 5 | 6 | package globs 7 | -------------------------------------------------------------------------------- /example/globs/letter.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package globs 5 | 6 | /* 7 | * 8 | ENUM( 9 | 10 | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z 11 | 12 | ) 13 | */ 14 | type Letter int 15 | -------------------------------------------------------------------------------- /example/globs/letter_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package globs 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // LetterA is a Letter of type A. 19 | LetterA Letter = iota 20 | // LetterB is a Letter of type B. 21 | LetterB 22 | // LetterC is a Letter of type C. 23 | LetterC 24 | // LetterD is a Letter of type D. 25 | LetterD 26 | // LetterE is a Letter of type E. 27 | LetterE 28 | // LetterF is a Letter of type F. 29 | LetterF 30 | // LetterG is a Letter of type G. 31 | LetterG 32 | // LetterH is a Letter of type H. 33 | LetterH 34 | // LetterI is a Letter of type I. 35 | LetterI 36 | // LetterJ is a Letter of type J. 37 | LetterJ 38 | // LetterK is a Letter of type K. 39 | LetterK 40 | // LetterL is a Letter of type L. 41 | LetterL 42 | // LetterM is a Letter of type M. 43 | LetterM 44 | // LetterN is a Letter of type N. 45 | LetterN 46 | // LetterO is a Letter of type O. 47 | LetterO 48 | // LetterP is a Letter of type P. 49 | LetterP 50 | // LetterQ is a Letter of type Q. 51 | LetterQ 52 | // LetterR is a Letter of type R. 53 | LetterR 54 | // LetterS is a Letter of type S. 55 | LetterS 56 | // LetterT is a Letter of type T. 57 | LetterT 58 | // LetterU is a Letter of type U. 59 | LetterU 60 | // LetterV is a Letter of type V. 61 | LetterV 62 | // LetterW is a Letter of type W. 63 | LetterW 64 | // LetterX is a Letter of type X. 65 | LetterX 66 | // LetterY is a Letter of type Y. 67 | LetterY 68 | // LetterZ is a Letter of type Z. 69 | LetterZ 70 | ) 71 | 72 | var ErrInvalidLetter = errors.New("not a valid Letter") 73 | 74 | const _LetterName = "abcdefghijklmnopqrstuvwxyz" 75 | 76 | var _LetterMap = map[Letter]string{ 77 | LetterA: _LetterName[0:1], 78 | LetterB: _LetterName[1:2], 79 | LetterC: _LetterName[2:3], 80 | LetterD: _LetterName[3:4], 81 | LetterE: _LetterName[4:5], 82 | LetterF: _LetterName[5:6], 83 | LetterG: _LetterName[6:7], 84 | LetterH: _LetterName[7:8], 85 | LetterI: _LetterName[8:9], 86 | LetterJ: _LetterName[9:10], 87 | LetterK: _LetterName[10:11], 88 | LetterL: _LetterName[11:12], 89 | LetterM: _LetterName[12:13], 90 | LetterN: _LetterName[13:14], 91 | LetterO: _LetterName[14:15], 92 | LetterP: _LetterName[15:16], 93 | LetterQ: _LetterName[16:17], 94 | LetterR: _LetterName[17:18], 95 | LetterS: _LetterName[18:19], 96 | LetterT: _LetterName[19:20], 97 | LetterU: _LetterName[20:21], 98 | LetterV: _LetterName[21:22], 99 | LetterW: _LetterName[22:23], 100 | LetterX: _LetterName[23:24], 101 | LetterY: _LetterName[24:25], 102 | LetterZ: _LetterName[25:26], 103 | } 104 | 105 | // String implements the Stringer interface. 106 | func (x Letter) String() string { 107 | if str, ok := _LetterMap[x]; ok { 108 | return str 109 | } 110 | return fmt.Sprintf("Letter(%d)", x) 111 | } 112 | 113 | // IsValid provides a quick way to determine if the typed value is 114 | // part of the allowed enumerated values 115 | func (x Letter) IsValid() bool { 116 | _, ok := _LetterMap[x] 117 | return ok 118 | } 119 | 120 | var _LetterValue = map[string]Letter{ 121 | _LetterName[0:1]: LetterA, 122 | _LetterName[1:2]: LetterB, 123 | _LetterName[2:3]: LetterC, 124 | _LetterName[3:4]: LetterD, 125 | _LetterName[4:5]: LetterE, 126 | _LetterName[5:6]: LetterF, 127 | _LetterName[6:7]: LetterG, 128 | _LetterName[7:8]: LetterH, 129 | _LetterName[8:9]: LetterI, 130 | _LetterName[9:10]: LetterJ, 131 | _LetterName[10:11]: LetterK, 132 | _LetterName[11:12]: LetterL, 133 | _LetterName[12:13]: LetterM, 134 | _LetterName[13:14]: LetterN, 135 | _LetterName[14:15]: LetterO, 136 | _LetterName[15:16]: LetterP, 137 | _LetterName[16:17]: LetterQ, 138 | _LetterName[17:18]: LetterR, 139 | _LetterName[18:19]: LetterS, 140 | _LetterName[19:20]: LetterT, 141 | _LetterName[20:21]: LetterU, 142 | _LetterName[21:22]: LetterV, 143 | _LetterName[22:23]: LetterW, 144 | _LetterName[23:24]: LetterX, 145 | _LetterName[24:25]: LetterY, 146 | _LetterName[25:26]: LetterZ, 147 | } 148 | 149 | // ParseLetter attempts to convert a string to a Letter. 150 | func ParseLetter(name string) (Letter, error) { 151 | if x, ok := _LetterValue[name]; ok { 152 | return x, nil 153 | } 154 | return Letter(0), fmt.Errorf("%s is %w", name, ErrInvalidLetter) 155 | } 156 | -------------------------------------------------------------------------------- /example/globs/number.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package globs 5 | 6 | /* 7 | * 8 | ENUM( 9 | 10 | 0,1,2,3,4,5,6,7,8,9 11 | 12 | ) 13 | */ 14 | type Number int 15 | -------------------------------------------------------------------------------- /example/globs/number_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package globs 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // Number0 is a Number of type 0. 19 | Number0 Number = iota 20 | // Number1 is a Number of type 1. 21 | Number1 22 | // Number2 is a Number of type 2. 23 | Number2 24 | // Number3 is a Number of type 3. 25 | Number3 26 | // Number4 is a Number of type 4. 27 | Number4 28 | // Number5 is a Number of type 5. 29 | Number5 30 | // Number6 is a Number of type 6. 31 | Number6 32 | // Number7 is a Number of type 7. 33 | Number7 34 | // Number8 is a Number of type 8. 35 | Number8 36 | // Number9 is a Number of type 9. 37 | Number9 38 | ) 39 | 40 | var ErrInvalidNumber = errors.New("not a valid Number") 41 | 42 | const _NumberName = "0123456789" 43 | 44 | var _NumberMap = map[Number]string{ 45 | Number0: _NumberName[0:1], 46 | Number1: _NumberName[1:2], 47 | Number2: _NumberName[2:3], 48 | Number3: _NumberName[3:4], 49 | Number4: _NumberName[4:5], 50 | Number5: _NumberName[5:6], 51 | Number6: _NumberName[6:7], 52 | Number7: _NumberName[7:8], 53 | Number8: _NumberName[8:9], 54 | Number9: _NumberName[9:10], 55 | } 56 | 57 | // String implements the Stringer interface. 58 | func (x Number) String() string { 59 | if str, ok := _NumberMap[x]; ok { 60 | return str 61 | } 62 | return fmt.Sprintf("Number(%d)", x) 63 | } 64 | 65 | // IsValid provides a quick way to determine if the typed value is 66 | // part of the allowed enumerated values 67 | func (x Number) IsValid() bool { 68 | _, ok := _NumberMap[x] 69 | return ok 70 | } 71 | 72 | var _NumberValue = map[string]Number{ 73 | _NumberName[0:1]: Number0, 74 | _NumberName[1:2]: Number1, 75 | _NumberName[2:3]: Number2, 76 | _NumberName[3:4]: Number3, 77 | _NumberName[4:5]: Number4, 78 | _NumberName[5:6]: Number5, 79 | _NumberName[6:7]: Number6, 80 | _NumberName[7:8]: Number7, 81 | _NumberName[8:9]: Number8, 82 | _NumberName[9:10]: Number9, 83 | } 84 | 85 | // ParseNumber attempts to convert a string to a Number. 86 | func ParseNumber(name string) (Number, error) { 87 | if x, ok := _NumberValue[name]; ok { 88 | return x, nil 89 | } 90 | return Number(0), fmt.Errorf("%s is %w", name, ErrInvalidNumber) 91 | } 92 | -------------------------------------------------------------------------------- /example/go_enum_enum_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | package example 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | ) 13 | 14 | const ( 15 | // TestOnlyEnumABCDX is a TestOnlyEnum of type ABCD (x). 16 | TestOnlyEnumABCDX TestOnlyEnum = "ABCD (x)" 17 | // TestOnlyEnumEFGHY is a TestOnlyEnum of type EFGH (y). 18 | TestOnlyEnumEFGHY TestOnlyEnum = "EFGH (y)" 19 | ) 20 | 21 | var ErrInvalidTestOnlyEnum = errors.New("not a valid TestOnlyEnum") 22 | 23 | // String implements the Stringer interface. 24 | func (x TestOnlyEnum) String() string { 25 | return string(x) 26 | } 27 | 28 | // IsValid provides a quick way to determine if the typed value is 29 | // part of the allowed enumerated values 30 | func (x TestOnlyEnum) IsValid() bool { 31 | _, err := ParseTestOnlyEnum(string(x)) 32 | return err == nil 33 | } 34 | 35 | var _TestOnlyEnumValue = map[string]TestOnlyEnum{ 36 | "ABCD (x)": TestOnlyEnumABCDX, 37 | "EFGH (y)": TestOnlyEnumEFGHY, 38 | } 39 | 40 | // ParseTestOnlyEnum attempts to convert a string to a TestOnlyEnum. 41 | func ParseTestOnlyEnum(name string) (TestOnlyEnum, error) { 42 | if x, ok := _TestOnlyEnumValue[name]; ok { 43 | return x, nil 44 | } 45 | return TestOnlyEnum(""), fmt.Errorf("%s is %w", name, ErrInvalidTestOnlyEnum) 46 | } 47 | -------------------------------------------------------------------------------- /example/go_enum_test.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum 2 | package example 3 | 4 | /* 5 | ENUM( 6 | 7 | ABCD (x), 8 | EFGH (y), 9 | 10 | ) 11 | */ 12 | type TestOnlyEnum string 13 | -------------------------------------------------------------------------------- /example/negative.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --nocase -b example 2 | 3 | package example 4 | 5 | /* 6 | ENUM( 7 | Unknown = -1, 8 | Good, 9 | Bad 10 | ). 11 | */ 12 | type Status int 13 | 14 | /* 15 | ENUM( 16 | Unknown = -5, 17 | Good, 18 | Bad, 19 | Ugly 20 | ). 21 | */ 22 | type AllNegative int 23 | -------------------------------------------------------------------------------- /example/negative_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | "strings" 16 | ) 17 | 18 | const ( 19 | // AllNegativeUnknown is a AllNegative of type Unknown. 20 | AllNegativeUnknown AllNegative = iota + -5 21 | // AllNegativeGood is a AllNegative of type Good. 22 | AllNegativeGood 23 | // AllNegativeBad is a AllNegative of type Bad. 24 | AllNegativeBad 25 | // AllNegativeUgly is a AllNegative of type Ugly. 26 | AllNegativeUgly 27 | ) 28 | 29 | var ErrInvalidAllNegative = errors.New("not a valid AllNegative") 30 | 31 | const _AllNegativeName = "UnknownGoodBadUgly" 32 | 33 | var _AllNegativeMap = map[AllNegative]string{ 34 | AllNegativeUnknown: _AllNegativeName[0:7], 35 | AllNegativeGood: _AllNegativeName[7:11], 36 | AllNegativeBad: _AllNegativeName[11:14], 37 | AllNegativeUgly: _AllNegativeName[14:18], 38 | } 39 | 40 | // String implements the Stringer interface. 41 | func (x AllNegative) String() string { 42 | if str, ok := _AllNegativeMap[x]; ok { 43 | return str 44 | } 45 | return fmt.Sprintf("AllNegative(%d)", x) 46 | } 47 | 48 | // IsValid provides a quick way to determine if the typed value is 49 | // part of the allowed enumerated values 50 | func (x AllNegative) IsValid() bool { 51 | _, ok := _AllNegativeMap[x] 52 | return ok 53 | } 54 | 55 | var _AllNegativeValue = map[string]AllNegative{ 56 | _AllNegativeName[0:7]: AllNegativeUnknown, 57 | strings.ToLower(_AllNegativeName[0:7]): AllNegativeUnknown, 58 | _AllNegativeName[7:11]: AllNegativeGood, 59 | strings.ToLower(_AllNegativeName[7:11]): AllNegativeGood, 60 | _AllNegativeName[11:14]: AllNegativeBad, 61 | strings.ToLower(_AllNegativeName[11:14]): AllNegativeBad, 62 | _AllNegativeName[14:18]: AllNegativeUgly, 63 | strings.ToLower(_AllNegativeName[14:18]): AllNegativeUgly, 64 | } 65 | 66 | // ParseAllNegative attempts to convert a string to a AllNegative. 67 | func ParseAllNegative(name string) (AllNegative, error) { 68 | if x, ok := _AllNegativeValue[name]; ok { 69 | return x, nil 70 | } 71 | // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. 72 | if x, ok := _AllNegativeValue[strings.ToLower(name)]; ok { 73 | return x, nil 74 | } 75 | return AllNegative(0), fmt.Errorf("%s is %w", name, ErrInvalidAllNegative) 76 | } 77 | 78 | const ( 79 | // StatusUnknown is a Status of type Unknown. 80 | StatusUnknown Status = iota + -1 81 | // StatusGood is a Status of type Good. 82 | StatusGood 83 | // StatusBad is a Status of type Bad. 84 | StatusBad 85 | ) 86 | 87 | var ErrInvalidStatus = errors.New("not a valid Status") 88 | 89 | const _StatusName = "UnknownGoodBad" 90 | 91 | var _StatusMap = map[Status]string{ 92 | StatusUnknown: _StatusName[0:7], 93 | StatusGood: _StatusName[7:11], 94 | StatusBad: _StatusName[11:14], 95 | } 96 | 97 | // String implements the Stringer interface. 98 | func (x Status) String() string { 99 | if str, ok := _StatusMap[x]; ok { 100 | return str 101 | } 102 | return fmt.Sprintf("Status(%d)", x) 103 | } 104 | 105 | // IsValid provides a quick way to determine if the typed value is 106 | // part of the allowed enumerated values 107 | func (x Status) IsValid() bool { 108 | _, ok := _StatusMap[x] 109 | return ok 110 | } 111 | 112 | var _StatusValue = map[string]Status{ 113 | _StatusName[0:7]: StatusUnknown, 114 | strings.ToLower(_StatusName[0:7]): StatusUnknown, 115 | _StatusName[7:11]: StatusGood, 116 | strings.ToLower(_StatusName[7:11]): StatusGood, 117 | _StatusName[11:14]: StatusBad, 118 | strings.ToLower(_StatusName[11:14]): StatusBad, 119 | } 120 | 121 | // ParseStatus attempts to convert a string to a Status. 122 | func ParseStatus(name string) (Status, error) { 123 | if x, ok := _StatusValue[name]; ok { 124 | return x, nil 125 | } 126 | // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. 127 | if x, ok := _StatusValue[strings.ToLower(name)]; ok { 128 | return x, nil 129 | } 130 | return Status(0), fmt.Errorf("%s is %w", name, ErrInvalidStatus) 131 | } 132 | -------------------------------------------------------------------------------- /example/negative_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestStatusString(t *testing.T) { 13 | tests := map[string]struct { 14 | input string 15 | output Status 16 | }{ 17 | "bad": { 18 | input: `Bad`, 19 | output: StatusBad, 20 | }, 21 | "unknown": { 22 | input: `Unknown`, 23 | output: StatusUnknown, 24 | }, 25 | "good": { 26 | input: `Good`, 27 | output: StatusGood, 28 | }, 29 | } 30 | 31 | for name, tc := range tests { 32 | t.Run(name, func(t *testing.T) { 33 | output, err := ParseStatus(tc.input) 34 | assert.NoError(t, err) 35 | assert.Equal(t, tc.output, output) 36 | 37 | assert.Equal(t, tc.input, output.String()) 38 | }) 39 | } 40 | 41 | t.Run("failures", func(t *testing.T) { 42 | assert.Equal(t, "Status(99)", Status(99).String()) 43 | failedStatus, err := ParseStatus("") 44 | assert.Error(t, err) 45 | 46 | assert.Equal(t, Status(0), failedStatus) 47 | t.Run("cased", func(t *testing.T) { 48 | actual, err := ParseStatus("BAD") 49 | assert.NoError(t, err) 50 | assert.Equal(t, StatusBad, actual) 51 | }) 52 | }) 53 | } 54 | 55 | func TestNegativeString(t *testing.T) { 56 | tests := map[string]struct { 57 | input string 58 | output AllNegative 59 | }{ 60 | "unknown": { 61 | input: `Unknown`, 62 | output: AllNegativeUnknown, 63 | }, 64 | "good": { 65 | input: `Good`, 66 | output: AllNegativeGood, 67 | }, 68 | "bad": { 69 | input: `Bad`, 70 | output: AllNegativeBad, 71 | }, 72 | "ugly": { 73 | input: `Ugly`, 74 | output: AllNegativeUgly, 75 | }, 76 | } 77 | 78 | for name, tc := range tests { 79 | t.Run(name, func(t *testing.T) { 80 | output, err := ParseAllNegative(tc.input) 81 | assert.NoError(t, err) 82 | assert.Equal(t, tc.output, output) 83 | 84 | assert.Equal(t, tc.input, output.String()) 85 | }) 86 | } 87 | 88 | t.Run("failures", func(t *testing.T) { 89 | assert.Equal(t, "AllNegative(99)", AllNegative(99).String()) 90 | allN, err := ParseAllNegative("") 91 | assert.Error(t, err) 92 | 93 | assert.Equal(t, AllNegative(0), allN) 94 | }) 95 | t.Run("cased", func(t *testing.T) { 96 | actual, err := ParseAllNegative("UGLY") 97 | assert.NoError(t, err) 98 | assert.Equal(t, AllNegativeUgly, actual) 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /example/replace_prefix.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --marshal --prefix=AcmeInc_ --noprefix --nocamel --names -b example 2 | 3 | package example 4 | 5 | // Shops ENUM( 6 | // SOME_PLACE_AWESOME, 7 | // SomewhereElse, 8 | // LocationUnknown 9 | // ) 10 | type Shop string 11 | -------------------------------------------------------------------------------- /example/replace_prefix_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "fmt" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | // AcmeInc_SOME_PLACE_AWESOME is a Shop of type SOME_PLACE_AWESOME. 19 | AcmeInc_SOME_PLACE_AWESOME Shop = "SOME_PLACE_AWESOME" 20 | // AcmeInc_SomewhereElse is a Shop of type SomewhereElse. 21 | AcmeInc_SomewhereElse Shop = "SomewhereElse" 22 | // AcmeInc_LocationUnknown is a Shop of type LocationUnknown. 23 | AcmeInc_LocationUnknown Shop = "LocationUnknown" 24 | ) 25 | 26 | var ErrInvalidShop = fmt.Errorf("not a valid Shop, try [%s]", strings.Join(_ShopNames, ", ")) 27 | 28 | var _ShopNames = []string{ 29 | string(AcmeInc_SOME_PLACE_AWESOME), 30 | string(AcmeInc_SomewhereElse), 31 | string(AcmeInc_LocationUnknown), 32 | } 33 | 34 | // ShopNames returns a list of possible string values of Shop. 35 | func ShopNames() []string { 36 | tmp := make([]string, len(_ShopNames)) 37 | copy(tmp, _ShopNames) 38 | return tmp 39 | } 40 | 41 | // String implements the Stringer interface. 42 | func (x Shop) String() string { 43 | return string(x) 44 | } 45 | 46 | // IsValid provides a quick way to determine if the typed value is 47 | // part of the allowed enumerated values 48 | func (x Shop) IsValid() bool { 49 | _, err := ParseShop(string(x)) 50 | return err == nil 51 | } 52 | 53 | var _ShopValue = map[string]Shop{ 54 | "SOME_PLACE_AWESOME": AcmeInc_SOME_PLACE_AWESOME, 55 | "SomewhereElse": AcmeInc_SomewhereElse, 56 | "LocationUnknown": AcmeInc_LocationUnknown, 57 | } 58 | 59 | // ParseShop attempts to convert a string to a Shop. 60 | func ParseShop(name string) (Shop, error) { 61 | if x, ok := _ShopValue[name]; ok { 62 | return x, nil 63 | } 64 | return Shop(""), fmt.Errorf("%s is %w", name, ErrInvalidShop) 65 | } 66 | 67 | // MarshalText implements the text marshaller method. 68 | func (x Shop) MarshalText() ([]byte, error) { 69 | return []byte(string(x)), nil 70 | } 71 | 72 | // UnmarshalText implements the text unmarshaller method. 73 | func (x *Shop) UnmarshalText(text []byte) error { 74 | tmp, err := ParseShop(string(text)) 75 | if err != nil { 76 | return err 77 | } 78 | *x = tmp 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /example/replace_prefix_int.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --marshal --prefix=AcmeInt_ --noprefix --nocamel --names -b example 2 | 3 | package example 4 | 5 | // Shops ENUM( 6 | // SOME_PLACE_AWESOME, 7 | // SomewhereElse, 8 | // LocationUnknown 9 | // ) 10 | type IntShop int 11 | -------------------------------------------------------------------------------- /example/replace_prefix_int_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "fmt" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | // AcmeInt_SOME_PLACE_AWESOME is a IntShop of type SOME_PLACE_AWESOME. 19 | AcmeInt_SOME_PLACE_AWESOME IntShop = iota 20 | // AcmeInt_SomewhereElse is a IntShop of type SomewhereElse. 21 | AcmeInt_SomewhereElse 22 | // AcmeInt_LocationUnknown is a IntShop of type LocationUnknown. 23 | AcmeInt_LocationUnknown 24 | ) 25 | 26 | var ErrInvalidIntShop = fmt.Errorf("not a valid IntShop, try [%s]", strings.Join(_IntShopNames, ", ")) 27 | 28 | const _IntShopName = "SOME_PLACE_AWESOMESomewhereElseLocationUnknown" 29 | 30 | var _IntShopNames = []string{ 31 | _IntShopName[0:18], 32 | _IntShopName[18:31], 33 | _IntShopName[31:46], 34 | } 35 | 36 | // IntShopNames returns a list of possible string values of IntShop. 37 | func IntShopNames() []string { 38 | tmp := make([]string, len(_IntShopNames)) 39 | copy(tmp, _IntShopNames) 40 | return tmp 41 | } 42 | 43 | var _IntShopMap = map[IntShop]string{ 44 | AcmeInt_SOME_PLACE_AWESOME: _IntShopName[0:18], 45 | AcmeInt_SomewhereElse: _IntShopName[18:31], 46 | AcmeInt_LocationUnknown: _IntShopName[31:46], 47 | } 48 | 49 | // String implements the Stringer interface. 50 | func (x IntShop) String() string { 51 | if str, ok := _IntShopMap[x]; ok { 52 | return str 53 | } 54 | return fmt.Sprintf("IntShop(%d)", x) 55 | } 56 | 57 | // IsValid provides a quick way to determine if the typed value is 58 | // part of the allowed enumerated values 59 | func (x IntShop) IsValid() bool { 60 | _, ok := _IntShopMap[x] 61 | return ok 62 | } 63 | 64 | var _IntShopValue = map[string]IntShop{ 65 | _IntShopName[0:18]: AcmeInt_SOME_PLACE_AWESOME, 66 | _IntShopName[18:31]: AcmeInt_SomewhereElse, 67 | _IntShopName[31:46]: AcmeInt_LocationUnknown, 68 | } 69 | 70 | // ParseIntShop attempts to convert a string to a IntShop. 71 | func ParseIntShop(name string) (IntShop, error) { 72 | if x, ok := _IntShopValue[name]; ok { 73 | return x, nil 74 | } 75 | return IntShop(0), fmt.Errorf("%s is %w", name, ErrInvalidIntShop) 76 | } 77 | 78 | // MarshalText implements the text marshaller method. 79 | func (x IntShop) MarshalText() ([]byte, error) { 80 | return []byte(x.String()), nil 81 | } 82 | 83 | // UnmarshalText implements the text unmarshaller method. 84 | func (x *IntShop) UnmarshalText(text []byte) error { 85 | name := string(text) 86 | tmp, err := ParseIntShop(name) 87 | if err != nil { 88 | return err 89 | } 90 | *x = tmp 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /example/sql.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --sql --sqlnullstr --sqlnullint --ptr --marshal --nocomments -b example 2 | 3 | package example 4 | 5 | // ENUM(pending, inWork, completed, rejected) 6 | type ProjectStatus int 7 | -------------------------------------------------------------------------------- /example/sql_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "database/sql/driver" 14 | "encoding/json" 15 | "errors" 16 | "fmt" 17 | "strconv" 18 | ) 19 | 20 | const ( 21 | ProjectStatusPending ProjectStatus = iota 22 | ProjectStatusInWork 23 | ProjectStatusCompleted 24 | ProjectStatusRejected 25 | ) 26 | 27 | var ErrInvalidProjectStatus = errors.New("not a valid ProjectStatus") 28 | 29 | const _ProjectStatusName = "pendinginWorkcompletedrejected" 30 | 31 | var _ProjectStatusMap = map[ProjectStatus]string{ 32 | ProjectStatusPending: _ProjectStatusName[0:7], 33 | ProjectStatusInWork: _ProjectStatusName[7:13], 34 | ProjectStatusCompleted: _ProjectStatusName[13:22], 35 | ProjectStatusRejected: _ProjectStatusName[22:30], 36 | } 37 | 38 | // String implements the Stringer interface. 39 | func (x ProjectStatus) String() string { 40 | if str, ok := _ProjectStatusMap[x]; ok { 41 | return str 42 | } 43 | return fmt.Sprintf("ProjectStatus(%d)", x) 44 | } 45 | 46 | // IsValid provides a quick way to determine if the typed value is 47 | // part of the allowed enumerated values 48 | func (x ProjectStatus) IsValid() bool { 49 | _, ok := _ProjectStatusMap[x] 50 | return ok 51 | } 52 | 53 | var _ProjectStatusValue = map[string]ProjectStatus{ 54 | _ProjectStatusName[0:7]: ProjectStatusPending, 55 | _ProjectStatusName[7:13]: ProjectStatusInWork, 56 | _ProjectStatusName[13:22]: ProjectStatusCompleted, 57 | _ProjectStatusName[22:30]: ProjectStatusRejected, 58 | } 59 | 60 | // ParseProjectStatus attempts to convert a string to a ProjectStatus. 61 | func ParseProjectStatus(name string) (ProjectStatus, error) { 62 | if x, ok := _ProjectStatusValue[name]; ok { 63 | return x, nil 64 | } 65 | return ProjectStatus(0), fmt.Errorf("%s is %w", name, ErrInvalidProjectStatus) 66 | } 67 | 68 | func (x ProjectStatus) Ptr() *ProjectStatus { 69 | return &x 70 | } 71 | 72 | // MarshalText implements the text marshaller method. 73 | func (x ProjectStatus) MarshalText() ([]byte, error) { 74 | return []byte(x.String()), nil 75 | } 76 | 77 | // UnmarshalText implements the text unmarshaller method. 78 | func (x *ProjectStatus) UnmarshalText(text []byte) error { 79 | name := string(text) 80 | tmp, err := ParseProjectStatus(name) 81 | if err != nil { 82 | return err 83 | } 84 | *x = tmp 85 | return nil 86 | } 87 | 88 | var errProjectStatusNilPtr = errors.New("value pointer is nil") // one per type for package clashes 89 | 90 | // Scan implements the Scanner interface. 91 | func (x *ProjectStatus) Scan(value interface{}) (err error) { 92 | if value == nil { 93 | *x = ProjectStatus(0) 94 | return 95 | } 96 | 97 | // A wider range of scannable types. 98 | // driver.Value values at the top of the list for expediency 99 | switch v := value.(type) { 100 | case int64: 101 | *x = ProjectStatus(v) 102 | case string: 103 | *x, err = ParseProjectStatus(v) 104 | if err != nil { 105 | // try parsing the integer value as a string 106 | if val, verr := strconv.Atoi(v); verr == nil { 107 | *x, err = ProjectStatus(val), nil 108 | } 109 | } 110 | case []byte: 111 | *x, err = ParseProjectStatus(string(v)) 112 | if err != nil { 113 | // try parsing the integer value as a string 114 | if val, verr := strconv.Atoi(string(v)); verr == nil { 115 | *x, err = ProjectStatus(val), nil 116 | } 117 | } 118 | case ProjectStatus: 119 | *x = v 120 | case int: 121 | *x = ProjectStatus(v) 122 | case *ProjectStatus: 123 | if v == nil { 124 | return errProjectStatusNilPtr 125 | } 126 | *x = *v 127 | case uint: 128 | *x = ProjectStatus(v) 129 | case uint64: 130 | *x = ProjectStatus(v) 131 | case *int: 132 | if v == nil { 133 | return errProjectStatusNilPtr 134 | } 135 | *x = ProjectStatus(*v) 136 | case *int64: 137 | if v == nil { 138 | return errProjectStatusNilPtr 139 | } 140 | *x = ProjectStatus(*v) 141 | case float64: // json marshals everything as a float64 if it's a number 142 | *x = ProjectStatus(v) 143 | case *float64: // json marshals everything as a float64 if it's a number 144 | if v == nil { 145 | return errProjectStatusNilPtr 146 | } 147 | *x = ProjectStatus(*v) 148 | case *uint: 149 | if v == nil { 150 | return errProjectStatusNilPtr 151 | } 152 | *x = ProjectStatus(*v) 153 | case *uint64: 154 | if v == nil { 155 | return errProjectStatusNilPtr 156 | } 157 | *x = ProjectStatus(*v) 158 | case *string: 159 | if v == nil { 160 | return errProjectStatusNilPtr 161 | } 162 | *x, err = ParseProjectStatus(*v) 163 | if err != nil { 164 | // try parsing the integer value as a string 165 | if val, verr := strconv.Atoi(*v); verr == nil { 166 | *x, err = ProjectStatus(val), nil 167 | } 168 | } 169 | } 170 | 171 | return 172 | } 173 | 174 | // Value implements the driver Valuer interface. 175 | func (x ProjectStatus) Value() (driver.Value, error) { 176 | return x.String(), nil 177 | } 178 | 179 | type NullProjectStatus struct { 180 | ProjectStatus ProjectStatus 181 | Valid bool 182 | Set bool 183 | } 184 | 185 | func NewNullProjectStatus(val interface{}) (x NullProjectStatus) { 186 | x.Scan(val) // yes, we ignore this error, it will just be an invalid value. 187 | return 188 | } 189 | 190 | // Scan implements the Scanner interface. 191 | func (x *NullProjectStatus) Scan(value interface{}) (err error) { 192 | x.Set = true 193 | if value == nil { 194 | x.ProjectStatus, x.Valid = ProjectStatus(0), false 195 | return 196 | } 197 | 198 | err = x.ProjectStatus.Scan(value) 199 | x.Valid = (err == nil) 200 | return 201 | } 202 | 203 | // Value implements the driver Valuer interface. 204 | func (x NullProjectStatus) Value() (driver.Value, error) { 205 | if !x.Valid { 206 | return nil, nil 207 | } 208 | // driver.Value accepts int64 for int values. 209 | return int64(x.ProjectStatus), nil 210 | } 211 | 212 | // MarshalJSON correctly serializes a NullProjectStatus to JSON. 213 | func (n NullProjectStatus) MarshalJSON() ([]byte, error) { 214 | const nullStr = "null" 215 | if n.Valid { 216 | return json.Marshal(n.ProjectStatus) 217 | } 218 | return []byte(nullStr), nil 219 | } 220 | 221 | // UnmarshalJSON correctly deserializes a NullProjectStatus from JSON. 222 | func (n *NullProjectStatus) UnmarshalJSON(b []byte) error { 223 | n.Set = true 224 | var x interface{} 225 | err := json.Unmarshal(b, &x) 226 | if err != nil { 227 | return err 228 | } 229 | err = n.Scan(x) 230 | return err 231 | } 232 | 233 | type NullProjectStatusStr struct { 234 | NullProjectStatus 235 | } 236 | 237 | func NewNullProjectStatusStr(val interface{}) (x NullProjectStatusStr) { 238 | x.Scan(val) // yes, we ignore this error, it will just be an invalid value. 239 | return 240 | } 241 | 242 | // Value implements the driver Valuer interface. 243 | func (x NullProjectStatusStr) Value() (driver.Value, error) { 244 | if !x.Valid { 245 | return nil, nil 246 | } 247 | return x.ProjectStatus.String(), nil 248 | } 249 | 250 | // MarshalJSON correctly serializes a NullProjectStatus to JSON. 251 | func (n NullProjectStatusStr) MarshalJSON() ([]byte, error) { 252 | const nullStr = "null" 253 | if n.Valid { 254 | return json.Marshal(n.ProjectStatus) 255 | } 256 | return []byte(nullStr), nil 257 | } 258 | 259 | // UnmarshalJSON correctly deserializes a NullProjectStatus from JSON. 260 | func (n *NullProjectStatusStr) UnmarshalJSON(b []byte) error { 261 | n.Set = true 262 | var x interface{} 263 | err := json.Unmarshal(b, &x) 264 | if err != nil { 265 | return err 266 | } 267 | err = n.Scan(x) 268 | return err 269 | } 270 | -------------------------------------------------------------------------------- /example/sql_int.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --sqlnullint -b example 2 | 3 | package example 4 | 5 | // ENUM(jpeg, jpg, png, tiff, gif) 6 | type ImageType int 7 | -------------------------------------------------------------------------------- /example/sql_int_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "database/sql/driver" 14 | "errors" 15 | "fmt" 16 | "strconv" 17 | ) 18 | 19 | const ( 20 | // ImageTypeJpeg is a ImageType of type Jpeg. 21 | ImageTypeJpeg ImageType = iota 22 | // ImageTypeJpg is a ImageType of type Jpg. 23 | ImageTypeJpg 24 | // ImageTypePng is a ImageType of type Png. 25 | ImageTypePng 26 | // ImageTypeTiff is a ImageType of type Tiff. 27 | ImageTypeTiff 28 | // ImageTypeGif is a ImageType of type Gif. 29 | ImageTypeGif 30 | ) 31 | 32 | var ErrInvalidImageType = errors.New("not a valid ImageType") 33 | 34 | const _ImageTypeName = "jpegjpgpngtiffgif" 35 | 36 | var _ImageTypeMap = map[ImageType]string{ 37 | ImageTypeJpeg: _ImageTypeName[0:4], 38 | ImageTypeJpg: _ImageTypeName[4:7], 39 | ImageTypePng: _ImageTypeName[7:10], 40 | ImageTypeTiff: _ImageTypeName[10:14], 41 | ImageTypeGif: _ImageTypeName[14:17], 42 | } 43 | 44 | // String implements the Stringer interface. 45 | func (x ImageType) String() string { 46 | if str, ok := _ImageTypeMap[x]; ok { 47 | return str 48 | } 49 | return fmt.Sprintf("ImageType(%d)", x) 50 | } 51 | 52 | // IsValid provides a quick way to determine if the typed value is 53 | // part of the allowed enumerated values 54 | func (x ImageType) IsValid() bool { 55 | _, ok := _ImageTypeMap[x] 56 | return ok 57 | } 58 | 59 | var _ImageTypeValue = map[string]ImageType{ 60 | _ImageTypeName[0:4]: ImageTypeJpeg, 61 | _ImageTypeName[4:7]: ImageTypeJpg, 62 | _ImageTypeName[7:10]: ImageTypePng, 63 | _ImageTypeName[10:14]: ImageTypeTiff, 64 | _ImageTypeName[14:17]: ImageTypeGif, 65 | } 66 | 67 | // ParseImageType attempts to convert a string to a ImageType. 68 | func ParseImageType(name string) (ImageType, error) { 69 | if x, ok := _ImageTypeValue[name]; ok { 70 | return x, nil 71 | } 72 | return ImageType(0), fmt.Errorf("%s is %w", name, ErrInvalidImageType) 73 | } 74 | 75 | var errImageTypeNilPtr = errors.New("value pointer is nil") // one per type for package clashes 76 | 77 | // Scan implements the Scanner interface. 78 | func (x *ImageType) Scan(value interface{}) (err error) { 79 | if value == nil { 80 | *x = ImageType(0) 81 | return 82 | } 83 | 84 | // A wider range of scannable types. 85 | // driver.Value values at the top of the list for expediency 86 | switch v := value.(type) { 87 | case int64: 88 | *x = ImageType(v) 89 | case string: 90 | *x, err = ParseImageType(v) 91 | if err != nil { 92 | // try parsing the integer value as a string 93 | if val, verr := strconv.Atoi(v); verr == nil { 94 | *x, err = ImageType(val), nil 95 | } 96 | } 97 | case []byte: 98 | *x, err = ParseImageType(string(v)) 99 | if err != nil { 100 | // try parsing the integer value as a string 101 | if val, verr := strconv.Atoi(string(v)); verr == nil { 102 | *x, err = ImageType(val), nil 103 | } 104 | } 105 | case ImageType: 106 | *x = v 107 | case int: 108 | *x = ImageType(v) 109 | case *ImageType: 110 | if v == nil { 111 | return errImageTypeNilPtr 112 | } 113 | *x = *v 114 | case uint: 115 | *x = ImageType(v) 116 | case uint64: 117 | *x = ImageType(v) 118 | case *int: 119 | if v == nil { 120 | return errImageTypeNilPtr 121 | } 122 | *x = ImageType(*v) 123 | case *int64: 124 | if v == nil { 125 | return errImageTypeNilPtr 126 | } 127 | *x = ImageType(*v) 128 | case float64: // json marshals everything as a float64 if it's a number 129 | *x = ImageType(v) 130 | case *float64: // json marshals everything as a float64 if it's a number 131 | if v == nil { 132 | return errImageTypeNilPtr 133 | } 134 | *x = ImageType(*v) 135 | case *uint: 136 | if v == nil { 137 | return errImageTypeNilPtr 138 | } 139 | *x = ImageType(*v) 140 | case *uint64: 141 | if v == nil { 142 | return errImageTypeNilPtr 143 | } 144 | *x = ImageType(*v) 145 | case *string: 146 | if v == nil { 147 | return errImageTypeNilPtr 148 | } 149 | *x, err = ParseImageType(*v) 150 | if err != nil { 151 | // try parsing the integer value as a string 152 | if val, verr := strconv.Atoi(*v); verr == nil { 153 | *x, err = ImageType(val), nil 154 | } 155 | } 156 | } 157 | 158 | return 159 | } 160 | 161 | // Value implements the driver Valuer interface. 162 | func (x ImageType) Value() (driver.Value, error) { 163 | return int64(x), nil 164 | } 165 | 166 | type NullImageType struct { 167 | ImageType ImageType 168 | Valid bool 169 | } 170 | 171 | func NewNullImageType(val interface{}) (x NullImageType) { 172 | x.Scan(val) // yes, we ignore this error, it will just be an invalid value. 173 | return 174 | } 175 | 176 | // Scan implements the Scanner interface. 177 | func (x *NullImageType) Scan(value interface{}) (err error) { 178 | if value == nil { 179 | x.ImageType, x.Valid = ImageType(0), false 180 | return 181 | } 182 | 183 | err = x.ImageType.Scan(value) 184 | x.Valid = (err == nil) 185 | return 186 | } 187 | 188 | // Value implements the driver Valuer interface. 189 | func (x NullImageType) Value() (driver.Value, error) { 190 | if !x.Valid { 191 | return nil, nil 192 | } 193 | // driver.Value accepts int64 for int values. 194 | return int64(x.ImageType), nil 195 | } 196 | -------------------------------------------------------------------------------- /example/sql_mock_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: database/sql/driver (interfaces: Conn,Driver,Stmt,Result,Rows) 3 | 4 | // Package example is a generated GoMock package. 5 | package example 6 | 7 | import ( 8 | driver "database/sql/driver" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockConn is a mock of Conn interface. 15 | type MockConn struct { 16 | ctrl *gomock.Controller 17 | recorder *MockConnMockRecorder 18 | } 19 | 20 | // MockConnMockRecorder is the mock recorder for MockConn. 21 | type MockConnMockRecorder struct { 22 | mock *MockConn 23 | } 24 | 25 | // NewMockConn creates a new mock instance. 26 | func NewMockConn(ctrl *gomock.Controller) *MockConn { 27 | mock := &MockConn{ctrl: ctrl} 28 | mock.recorder = &MockConnMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockConn) EXPECT() *MockConnMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Begin mocks base method. 38 | func (m *MockConn) Begin() (driver.Tx, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Begin") 41 | ret0, _ := ret[0].(driver.Tx) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // Begin indicates an expected call of Begin. 47 | func (mr *MockConnMockRecorder) Begin() *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Begin", reflect.TypeOf((*MockConn)(nil).Begin)) 50 | } 51 | 52 | // Close mocks base method. 53 | func (m *MockConn) Close() error { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "Close") 56 | ret0, _ := ret[0].(error) 57 | return ret0 58 | } 59 | 60 | // Close indicates an expected call of Close. 61 | func (mr *MockConnMockRecorder) Close() *gomock.Call { 62 | mr.mock.ctrl.T.Helper() 63 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConn)(nil).Close)) 64 | } 65 | 66 | // Prepare mocks base method. 67 | func (m *MockConn) Prepare(arg0 string) (driver.Stmt, error) { 68 | m.ctrl.T.Helper() 69 | ret := m.ctrl.Call(m, "Prepare", arg0) 70 | ret0, _ := ret[0].(driver.Stmt) 71 | ret1, _ := ret[1].(error) 72 | return ret0, ret1 73 | } 74 | 75 | // Prepare indicates an expected call of Prepare. 76 | func (mr *MockConnMockRecorder) Prepare(arg0 interface{}) *gomock.Call { 77 | mr.mock.ctrl.T.Helper() 78 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockConn)(nil).Prepare), arg0) 79 | } 80 | 81 | // MockDriver is a mock of Driver interface. 82 | type MockDriver struct { 83 | ctrl *gomock.Controller 84 | recorder *MockDriverMockRecorder 85 | } 86 | 87 | // MockDriverMockRecorder is the mock recorder for MockDriver. 88 | type MockDriverMockRecorder struct { 89 | mock *MockDriver 90 | } 91 | 92 | // NewMockDriver creates a new mock instance. 93 | func NewMockDriver(ctrl *gomock.Controller) *MockDriver { 94 | mock := &MockDriver{ctrl: ctrl} 95 | mock.recorder = &MockDriverMockRecorder{mock} 96 | return mock 97 | } 98 | 99 | // EXPECT returns an object that allows the caller to indicate expected use. 100 | func (m *MockDriver) EXPECT() *MockDriverMockRecorder { 101 | return m.recorder 102 | } 103 | 104 | // Open mocks base method. 105 | func (m *MockDriver) Open(arg0 string) (driver.Conn, error) { 106 | m.ctrl.T.Helper() 107 | ret := m.ctrl.Call(m, "Open", arg0) 108 | ret0, _ := ret[0].(driver.Conn) 109 | ret1, _ := ret[1].(error) 110 | return ret0, ret1 111 | } 112 | 113 | // Open indicates an expected call of Open. 114 | func (mr *MockDriverMockRecorder) Open(arg0 interface{}) *gomock.Call { 115 | mr.mock.ctrl.T.Helper() 116 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockDriver)(nil).Open), arg0) 117 | } 118 | 119 | // MockStmt is a mock of Stmt interface. 120 | type MockStmt struct { 121 | ctrl *gomock.Controller 122 | recorder *MockStmtMockRecorder 123 | } 124 | 125 | // MockStmtMockRecorder is the mock recorder for MockStmt. 126 | type MockStmtMockRecorder struct { 127 | mock *MockStmt 128 | } 129 | 130 | // NewMockStmt creates a new mock instance. 131 | func NewMockStmt(ctrl *gomock.Controller) *MockStmt { 132 | mock := &MockStmt{ctrl: ctrl} 133 | mock.recorder = &MockStmtMockRecorder{mock} 134 | return mock 135 | } 136 | 137 | // EXPECT returns an object that allows the caller to indicate expected use. 138 | func (m *MockStmt) EXPECT() *MockStmtMockRecorder { 139 | return m.recorder 140 | } 141 | 142 | // Close mocks base method. 143 | func (m *MockStmt) Close() error { 144 | m.ctrl.T.Helper() 145 | ret := m.ctrl.Call(m, "Close") 146 | ret0, _ := ret[0].(error) 147 | return ret0 148 | } 149 | 150 | // Close indicates an expected call of Close. 151 | func (mr *MockStmtMockRecorder) Close() *gomock.Call { 152 | mr.mock.ctrl.T.Helper() 153 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStmt)(nil).Close)) 154 | } 155 | 156 | // Exec mocks base method. 157 | func (m *MockStmt) Exec(arg0 []driver.Value) (driver.Result, error) { 158 | m.ctrl.T.Helper() 159 | ret := m.ctrl.Call(m, "Exec", arg0) 160 | ret0, _ := ret[0].(driver.Result) 161 | ret1, _ := ret[1].(error) 162 | return ret0, ret1 163 | } 164 | 165 | // Exec indicates an expected call of Exec. 166 | func (mr *MockStmtMockRecorder) Exec(arg0 interface{}) *gomock.Call { 167 | mr.mock.ctrl.T.Helper() 168 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockStmt)(nil).Exec), arg0) 169 | } 170 | 171 | // NumInput mocks base method. 172 | func (m *MockStmt) NumInput() int { 173 | m.ctrl.T.Helper() 174 | ret := m.ctrl.Call(m, "NumInput") 175 | ret0, _ := ret[0].(int) 176 | return ret0 177 | } 178 | 179 | // NumInput indicates an expected call of NumInput. 180 | func (mr *MockStmtMockRecorder) NumInput() *gomock.Call { 181 | mr.mock.ctrl.T.Helper() 182 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NumInput", reflect.TypeOf((*MockStmt)(nil).NumInput)) 183 | } 184 | 185 | // Query mocks base method. 186 | func (m *MockStmt) Query(arg0 []driver.Value) (driver.Rows, error) { 187 | m.ctrl.T.Helper() 188 | ret := m.ctrl.Call(m, "Query", arg0) 189 | ret0, _ := ret[0].(driver.Rows) 190 | ret1, _ := ret[1].(error) 191 | return ret0, ret1 192 | } 193 | 194 | // Query indicates an expected call of Query. 195 | func (mr *MockStmtMockRecorder) Query(arg0 interface{}) *gomock.Call { 196 | mr.mock.ctrl.T.Helper() 197 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockStmt)(nil).Query), arg0) 198 | } 199 | 200 | // MockResult is a mock of Result interface. 201 | type MockResult struct { 202 | ctrl *gomock.Controller 203 | recorder *MockResultMockRecorder 204 | } 205 | 206 | // MockResultMockRecorder is the mock recorder for MockResult. 207 | type MockResultMockRecorder struct { 208 | mock *MockResult 209 | } 210 | 211 | // NewMockResult creates a new mock instance. 212 | func NewMockResult(ctrl *gomock.Controller) *MockResult { 213 | mock := &MockResult{ctrl: ctrl} 214 | mock.recorder = &MockResultMockRecorder{mock} 215 | return mock 216 | } 217 | 218 | // EXPECT returns an object that allows the caller to indicate expected use. 219 | func (m *MockResult) EXPECT() *MockResultMockRecorder { 220 | return m.recorder 221 | } 222 | 223 | // LastInsertId mocks base method. 224 | func (m *MockResult) LastInsertId() (int64, error) { 225 | m.ctrl.T.Helper() 226 | ret := m.ctrl.Call(m, "LastInsertId") 227 | ret0, _ := ret[0].(int64) 228 | ret1, _ := ret[1].(error) 229 | return ret0, ret1 230 | } 231 | 232 | // LastInsertId indicates an expected call of LastInsertId. 233 | func (mr *MockResultMockRecorder) LastInsertId() *gomock.Call { 234 | mr.mock.ctrl.T.Helper() 235 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastInsertId", reflect.TypeOf((*MockResult)(nil).LastInsertId)) 236 | } 237 | 238 | // RowsAffected mocks base method. 239 | func (m *MockResult) RowsAffected() (int64, error) { 240 | m.ctrl.T.Helper() 241 | ret := m.ctrl.Call(m, "RowsAffected") 242 | ret0, _ := ret[0].(int64) 243 | ret1, _ := ret[1].(error) 244 | return ret0, ret1 245 | } 246 | 247 | // RowsAffected indicates an expected call of RowsAffected. 248 | func (mr *MockResultMockRecorder) RowsAffected() *gomock.Call { 249 | mr.mock.ctrl.T.Helper() 250 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RowsAffected", reflect.TypeOf((*MockResult)(nil).RowsAffected)) 251 | } 252 | 253 | // MockRows is a mock of Rows interface. 254 | type MockRows struct { 255 | ctrl *gomock.Controller 256 | recorder *MockRowsMockRecorder 257 | } 258 | 259 | // MockRowsMockRecorder is the mock recorder for MockRows. 260 | type MockRowsMockRecorder struct { 261 | mock *MockRows 262 | } 263 | 264 | // NewMockRows creates a new mock instance. 265 | func NewMockRows(ctrl *gomock.Controller) *MockRows { 266 | mock := &MockRows{ctrl: ctrl} 267 | mock.recorder = &MockRowsMockRecorder{mock} 268 | return mock 269 | } 270 | 271 | // EXPECT returns an object that allows the caller to indicate expected use. 272 | func (m *MockRows) EXPECT() *MockRowsMockRecorder { 273 | return m.recorder 274 | } 275 | 276 | // Close mocks base method. 277 | func (m *MockRows) Close() error { 278 | m.ctrl.T.Helper() 279 | ret := m.ctrl.Call(m, "Close") 280 | ret0, _ := ret[0].(error) 281 | return ret0 282 | } 283 | 284 | // Close indicates an expected call of Close. 285 | func (mr *MockRowsMockRecorder) Close() *gomock.Call { 286 | mr.mock.ctrl.T.Helper() 287 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockRows)(nil).Close)) 288 | } 289 | 290 | // Columns mocks base method. 291 | func (m *MockRows) Columns() []string { 292 | m.ctrl.T.Helper() 293 | ret := m.ctrl.Call(m, "Columns") 294 | ret0, _ := ret[0].([]string) 295 | return ret0 296 | } 297 | 298 | // Columns indicates an expected call of Columns. 299 | func (mr *MockRowsMockRecorder) Columns() *gomock.Call { 300 | mr.mock.ctrl.T.Helper() 301 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Columns", reflect.TypeOf((*MockRows)(nil).Columns)) 302 | } 303 | 304 | // Next mocks base method. 305 | func (m *MockRows) Next(arg0 []driver.Value) error { 306 | m.ctrl.T.Helper() 307 | ret := m.ctrl.Call(m, "Next", arg0) 308 | ret0, _ := ret[0].(error) 309 | return ret0 310 | } 311 | 312 | // Next indicates an expected call of Next. 313 | func (mr *MockRowsMockRecorder) Next(arg0 interface{}) *gomock.Call { 314 | mr.mock.ctrl.T.Helper() 315 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockRows)(nil).Next), arg0) 316 | } 317 | -------------------------------------------------------------------------------- /example/sql_str.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --sql --sqlnullstr -b example 2 | 3 | package example 4 | 5 | // ENUM(pending, processing, completed, failed) 6 | type JobState int 7 | -------------------------------------------------------------------------------- /example/sql_str_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "database/sql/driver" 14 | "errors" 15 | "fmt" 16 | ) 17 | 18 | const ( 19 | // JobStatePending is a JobState of type Pending. 20 | JobStatePending JobState = iota 21 | // JobStateProcessing is a JobState of type Processing. 22 | JobStateProcessing 23 | // JobStateCompleted is a JobState of type Completed. 24 | JobStateCompleted 25 | // JobStateFailed is a JobState of type Failed. 26 | JobStateFailed 27 | ) 28 | 29 | var ErrInvalidJobState = errors.New("not a valid JobState") 30 | 31 | const _JobStateName = "pendingprocessingcompletedfailed" 32 | 33 | var _JobStateMap = map[JobState]string{ 34 | JobStatePending: _JobStateName[0:7], 35 | JobStateProcessing: _JobStateName[7:17], 36 | JobStateCompleted: _JobStateName[17:26], 37 | JobStateFailed: _JobStateName[26:32], 38 | } 39 | 40 | // String implements the Stringer interface. 41 | func (x JobState) String() string { 42 | if str, ok := _JobStateMap[x]; ok { 43 | return str 44 | } 45 | return fmt.Sprintf("JobState(%d)", x) 46 | } 47 | 48 | // IsValid provides a quick way to determine if the typed value is 49 | // part of the allowed enumerated values 50 | func (x JobState) IsValid() bool { 51 | _, ok := _JobStateMap[x] 52 | return ok 53 | } 54 | 55 | var _JobStateValue = map[string]JobState{ 56 | _JobStateName[0:7]: JobStatePending, 57 | _JobStateName[7:17]: JobStateProcessing, 58 | _JobStateName[17:26]: JobStateCompleted, 59 | _JobStateName[26:32]: JobStateFailed, 60 | } 61 | 62 | // ParseJobState attempts to convert a string to a JobState. 63 | func ParseJobState(name string) (JobState, error) { 64 | if x, ok := _JobStateValue[name]; ok { 65 | return x, nil 66 | } 67 | return JobState(0), fmt.Errorf("%s is %w", name, ErrInvalidJobState) 68 | } 69 | 70 | var errJobStateNilPtr = errors.New("value pointer is nil") // one per type for package clashes 71 | 72 | // Scan implements the Scanner interface. 73 | func (x *JobState) Scan(value interface{}) (err error) { 74 | if value == nil { 75 | *x = JobState(0) 76 | return 77 | } 78 | 79 | // A wider range of scannable types. 80 | // driver.Value values at the top of the list for expediency 81 | switch v := value.(type) { 82 | case int64: 83 | *x = JobState(v) 84 | case string: 85 | *x, err = ParseJobState(v) 86 | case []byte: 87 | *x, err = ParseJobState(string(v)) 88 | case JobState: 89 | *x = v 90 | case int: 91 | *x = JobState(v) 92 | case *JobState: 93 | if v == nil { 94 | return errJobStateNilPtr 95 | } 96 | *x = *v 97 | case uint: 98 | *x = JobState(v) 99 | case uint64: 100 | *x = JobState(v) 101 | case *int: 102 | if v == nil { 103 | return errJobStateNilPtr 104 | } 105 | *x = JobState(*v) 106 | case *int64: 107 | if v == nil { 108 | return errJobStateNilPtr 109 | } 110 | *x = JobState(*v) 111 | case float64: // json marshals everything as a float64 if it's a number 112 | *x = JobState(v) 113 | case *float64: // json marshals everything as a float64 if it's a number 114 | if v == nil { 115 | return errJobStateNilPtr 116 | } 117 | *x = JobState(*v) 118 | case *uint: 119 | if v == nil { 120 | return errJobStateNilPtr 121 | } 122 | *x = JobState(*v) 123 | case *uint64: 124 | if v == nil { 125 | return errJobStateNilPtr 126 | } 127 | *x = JobState(*v) 128 | case *string: 129 | if v == nil { 130 | return errJobStateNilPtr 131 | } 132 | *x, err = ParseJobState(*v) 133 | } 134 | 135 | return 136 | } 137 | 138 | // Value implements the driver Valuer interface. 139 | func (x JobState) Value() (driver.Value, error) { 140 | return x.String(), nil 141 | } 142 | 143 | type NullJobState struct { 144 | JobState JobState 145 | Valid bool 146 | } 147 | 148 | func NewNullJobState(val interface{}) (x NullJobState) { 149 | x.Scan(val) // yes, we ignore this error, it will just be an invalid value. 150 | return 151 | } 152 | 153 | // Scan implements the Scanner interface. 154 | func (x *NullJobState) Scan(value interface{}) (err error) { 155 | if value == nil { 156 | x.JobState, x.Valid = JobState(0), false 157 | return 158 | } 159 | 160 | err = x.JobState.Scan(value) 161 | x.Valid = (err == nil) 162 | return 163 | } 164 | 165 | // Value implements the driver Valuer interface. 166 | func (x NullJobState) Value() (driver.Value, error) { 167 | if !x.Valid { 168 | return nil, nil 169 | } 170 | return x.JobState.String(), nil 171 | } 172 | -------------------------------------------------------------------------------- /example/sql_str_int.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum -f=$GOFILE --sqlint --sqlnullint --names -b example 2 | 3 | package example 4 | 5 | // ENUM(_,zeus, apollo, athena=20, ares) 6 | type GreekGod string 7 | 8 | // ENUM(_,zeus, apollo, _=19, athena="20", ares) 9 | type GreekGodCustom string 10 | -------------------------------------------------------------------------------- /example/sql_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestSQLExtras(t *testing.T) { 15 | assert.Equal(t, "ProjectStatus(22)", ProjectStatus(22).String(), "String value is not correct") 16 | 17 | _, err := ParseProjectStatus(`NotAStatus`) 18 | assert.Error(t, err, "Should have had an error parsing a non status") 19 | 20 | var ( 21 | intVal int = 3 22 | strVal string = "completed" 23 | strIntVal string = "2" 24 | nullInt *int 25 | nullInt64 *int64 26 | nullFloat64 *float64 27 | nullUint *uint 28 | nullUint64 *uint64 29 | nullString *string 30 | nullProjectStatus *ProjectStatus 31 | ) 32 | 33 | tests := map[string]struct { 34 | input interface{} 35 | result NullProjectStatus 36 | }{ 37 | "nil": {}, 38 | "val": { 39 | input: ProjectStatusRejected, 40 | result: NullProjectStatus{ 41 | ProjectStatus: ProjectStatusRejected, 42 | Valid: true, 43 | }, 44 | }, 45 | "ptr": { 46 | input: ProjectStatusCompleted.Ptr(), 47 | result: NullProjectStatus{ 48 | ProjectStatus: ProjectStatusCompleted, 49 | Valid: true, 50 | }, 51 | }, 52 | "string": { 53 | input: strVal, 54 | result: NullProjectStatus{ 55 | ProjectStatus: ProjectStatusCompleted, 56 | Valid: true, 57 | }, 58 | }, 59 | "*string": { 60 | input: &strVal, 61 | result: NullProjectStatus{ 62 | ProjectStatus: ProjectStatusCompleted, 63 | Valid: true, 64 | }, 65 | }, 66 | "*string as int": { 67 | input: &strIntVal, 68 | result: NullProjectStatus{ 69 | ProjectStatus: ProjectStatusCompleted, 70 | Valid: true, 71 | }, 72 | }, 73 | "invalid string": { 74 | input: "random value", 75 | }, 76 | "[]byte": { 77 | input: []byte(ProjectStatusInWork.String()), 78 | result: NullProjectStatus{ 79 | ProjectStatus: ProjectStatusInWork, 80 | Valid: true, 81 | }, 82 | }, 83 | "int": { 84 | input: intVal, 85 | result: NullProjectStatus{ 86 | ProjectStatus: ProjectStatusRejected, 87 | Valid: true, 88 | }, 89 | }, 90 | "*int": { 91 | input: &intVal, 92 | result: NullProjectStatus{ 93 | ProjectStatus: ProjectStatusRejected, 94 | Valid: true, 95 | }, 96 | }, 97 | "nullInt": { 98 | input: nullInt, 99 | }, 100 | "nullInt64": { 101 | input: nullInt64, 102 | }, 103 | "nullUint": { 104 | input: nullUint, 105 | }, 106 | "nullUint64": { 107 | input: nullUint64, 108 | }, 109 | "nullFloat64": { 110 | input: nullFloat64, 111 | }, 112 | "nullString": { 113 | input: nullString, 114 | }, 115 | "nullProjectStatus": { 116 | input: nullProjectStatus, 117 | }, 118 | "int as []byte": { 119 | input: []byte("1"), 120 | result: NullProjectStatus{ 121 | ProjectStatus: ProjectStatusInWork, 122 | Valid: true, 123 | }, 124 | }, 125 | } 126 | 127 | for name, tc := range tests { 128 | t.Run(name, func(t *testing.T) { 129 | status := NewNullProjectStatus(tc.input) 130 | tc.result.Set = true 131 | assert.Equal(t, tc.result, status) 132 | }) 133 | } 134 | } 135 | 136 | type SQLMarshalType struct { 137 | Status NullProjectStatus `json:"status"` 138 | StatusStr NullProjectStatusStr `json:"status_str"` 139 | Status2 *ProjectStatus `json:"status2,omitempty"` 140 | } 141 | 142 | func TestSQLMarshal(t *testing.T) { 143 | var val SQLMarshalType 144 | var val2 SQLMarshalType 145 | 146 | result, err := json.Marshal(val) 147 | require.NoError(t, err) 148 | assert.Equal(t, `{"status":null,"status_str":null}`, string(result)) 149 | 150 | require.NoError(t, json.Unmarshal([]byte(`{}`), &val2)) 151 | assert.Equal(t, val, val2) 152 | 153 | require.NoError(t, json.Unmarshal(result, &val2)) 154 | val.Status.Set = true 155 | val.StatusStr.Set = true 156 | assert.Equal(t, val, val2) 157 | 158 | val.Status = NewNullProjectStatus(1) 159 | val.StatusStr = NewNullProjectStatusStr(2) 160 | result, err = json.Marshal(val) 161 | require.NoError(t, err) 162 | assert.Equal(t, `{"status":"inWork","status_str":"completed"}`, string(result)) 163 | 164 | require.NoError(t, json.Unmarshal(result, &val2)) 165 | assert.Equal(t, val, val2) 166 | 167 | require.NoError(t, json.Unmarshal([]byte(`{"status":"inWork"}`), &val2)) 168 | assert.Equal(t, val, val2) 169 | 170 | require.NoError(t, json.Unmarshal([]byte(`{"status":"2"}`), &val2)) 171 | val.Status = NewNullProjectStatus(2) 172 | assert.Equal(t, val, val2) 173 | 174 | require.NoError(t, json.Unmarshal([]byte(`{"status":3}`), &val2)) 175 | val.Status = NewNullProjectStatus(3) 176 | assert.Equal(t, val, val2) 177 | 178 | require.NoError(t, json.Unmarshal([]byte(`{"status":null}`), &val2)) 179 | val.Status = NullProjectStatus{Set: true} 180 | assert.Equal(t, val, val2) 181 | 182 | val2 = SQLMarshalType{} // reset it so that the `set` value is false. 183 | 184 | require.NoError(t, json.Unmarshal([]byte(`{"status2":"rejected"}`), &val2)) 185 | val.Status = NullProjectStatus{} 186 | val.StatusStr = NullProjectStatusStr{} 187 | val.Status2 = ProjectStatusRejected.Ptr() 188 | assert.Equal(t, val, val2) 189 | 190 | require.Error(t, json.Unmarshal([]byte(`{"status2":"xyz"}`), &val2)) 191 | val2 = SQLMarshalType{} // reset it so that the `set` value is false. 192 | 193 | require.NoError(t, json.Unmarshal([]byte(`{"status_str":"rejected"}`), &val2)) 194 | val.Status = NullProjectStatus{} 195 | val.StatusStr = NewNullProjectStatusStr(3) 196 | val.Status2 = nil 197 | assert.Equal(t, val, val2) 198 | 199 | require.Error(t, json.Unmarshal([]byte(`{"status2":"xyz"}`), &val2)) 200 | } 201 | -------------------------------------------------------------------------------- /example/strings_only.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum --ptr --marshal --flag --nocase --mustparse --sqlnullstr --sql --names --values --nocomments -b example 2 | 3 | package example 4 | 5 | // ENUM(pending, running, completed, failed=error) 6 | type StrState string 7 | -------------------------------------------------------------------------------- /example/strings_only_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "database/sql/driver" 14 | "encoding/json" 15 | "errors" 16 | "fmt" 17 | "strings" 18 | ) 19 | 20 | const ( 21 | StrStatePending StrState = "pending" 22 | StrStateRunning StrState = "running" 23 | StrStateCompleted StrState = "completed" 24 | StrStateFailed StrState = "error" 25 | ) 26 | 27 | var ErrInvalidStrState = fmt.Errorf("not a valid StrState, try [%s]", strings.Join(_StrStateNames, ", ")) 28 | 29 | var _StrStateNames = []string{ 30 | string(StrStatePending), 31 | string(StrStateRunning), 32 | string(StrStateCompleted), 33 | string(StrStateFailed), 34 | } 35 | 36 | // StrStateNames returns a list of possible string values of StrState. 37 | func StrStateNames() []string { 38 | tmp := make([]string, len(_StrStateNames)) 39 | copy(tmp, _StrStateNames) 40 | return tmp 41 | } 42 | 43 | // StrStateValues returns a list of the values for StrState 44 | func StrStateValues() []StrState { 45 | return []StrState{ 46 | StrStatePending, 47 | StrStateRunning, 48 | StrStateCompleted, 49 | StrStateFailed, 50 | } 51 | } 52 | 53 | // String implements the Stringer interface. 54 | func (x StrState) String() string { 55 | return string(x) 56 | } 57 | 58 | // IsValid provides a quick way to determine if the typed value is 59 | // part of the allowed enumerated values 60 | func (x StrState) IsValid() bool { 61 | _, err := ParseStrState(string(x)) 62 | return err == nil 63 | } 64 | 65 | var _StrStateValue = map[string]StrState{ 66 | "pending": StrStatePending, 67 | "running": StrStateRunning, 68 | "completed": StrStateCompleted, 69 | "error": StrStateFailed, 70 | } 71 | 72 | // ParseStrState attempts to convert a string to a StrState. 73 | func ParseStrState(name string) (StrState, error) { 74 | if x, ok := _StrStateValue[name]; ok { 75 | return x, nil 76 | } 77 | // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. 78 | if x, ok := _StrStateValue[strings.ToLower(name)]; ok { 79 | return x, nil 80 | } 81 | return StrState(""), fmt.Errorf("%s is %w", name, ErrInvalidStrState) 82 | } 83 | 84 | // MustParseStrState converts a string to a StrState, and panics if is not valid. 85 | func MustParseStrState(name string) StrState { 86 | val, err := ParseStrState(name) 87 | if err != nil { 88 | panic(err) 89 | } 90 | return val 91 | } 92 | 93 | func (x StrState) Ptr() *StrState { 94 | return &x 95 | } 96 | 97 | // MarshalText implements the text marshaller method. 98 | func (x StrState) MarshalText() ([]byte, error) { 99 | return []byte(string(x)), nil 100 | } 101 | 102 | // UnmarshalText implements the text unmarshaller method. 103 | func (x *StrState) UnmarshalText(text []byte) error { 104 | tmp, err := ParseStrState(string(text)) 105 | if err != nil { 106 | return err 107 | } 108 | *x = tmp 109 | return nil 110 | } 111 | 112 | var errStrStateNilPtr = errors.New("value pointer is nil") // one per type for package clashes 113 | 114 | // Scan implements the Scanner interface. 115 | func (x *StrState) Scan(value interface{}) (err error) { 116 | if value == nil { 117 | *x = StrState("") 118 | return 119 | } 120 | 121 | // A wider range of scannable types. 122 | // driver.Value values at the top of the list for expediency 123 | switch v := value.(type) { 124 | case string: 125 | *x, err = ParseStrState(v) 126 | case []byte: 127 | *x, err = ParseStrState(string(v)) 128 | case StrState: 129 | *x = v 130 | case *StrState: 131 | if v == nil { 132 | return errStrStateNilPtr 133 | } 134 | *x = *v 135 | case *string: 136 | if v == nil { 137 | return errStrStateNilPtr 138 | } 139 | *x, err = ParseStrState(*v) 140 | default: 141 | return errors.New("invalid type for StrState") 142 | } 143 | 144 | return 145 | } 146 | 147 | // Value implements the driver Valuer interface. 148 | func (x StrState) Value() (driver.Value, error) { 149 | return x.String(), nil 150 | } 151 | 152 | // Set implements the Golang flag.Value interface func. 153 | func (x *StrState) Set(val string) error { 154 | v, err := ParseStrState(val) 155 | *x = v 156 | return err 157 | } 158 | 159 | // Get implements the Golang flag.Getter interface func. 160 | func (x *StrState) Get() interface{} { 161 | return *x 162 | } 163 | 164 | // Type implements the github.com/spf13/pFlag Value interface. 165 | func (x *StrState) Type() string { 166 | return "StrState" 167 | } 168 | 169 | type NullStrState struct { 170 | StrState StrState 171 | Valid bool 172 | Set bool 173 | } 174 | 175 | func NewNullStrState(val interface{}) (x NullStrState) { 176 | err := x.Scan(val) // yes, we ignore this error, it will just be an invalid value. 177 | _ = err // make any errcheck linters happy 178 | return 179 | } 180 | 181 | // Scan implements the Scanner interface. 182 | func (x *NullStrState) Scan(value interface{}) (err error) { 183 | if value == nil { 184 | x.StrState, x.Valid = StrState(""), false 185 | return 186 | } 187 | 188 | err = x.StrState.Scan(value) 189 | x.Valid = (err == nil) 190 | return 191 | } 192 | 193 | // Value implements the driver Valuer interface. 194 | func (x NullStrState) Value() (driver.Value, error) { 195 | if !x.Valid { 196 | return nil, nil 197 | } 198 | return x.StrState.String(), nil 199 | } 200 | 201 | // MarshalJSON correctly serializes a NullStrState to JSON. 202 | func (n NullStrState) MarshalJSON() ([]byte, error) { 203 | const nullStr = "null" 204 | if n.Valid { 205 | return json.Marshal(n.StrState) 206 | } 207 | return []byte(nullStr), nil 208 | } 209 | 210 | // UnmarshalJSON correctly deserializes a NullStrState from JSON. 211 | func (n *NullStrState) UnmarshalJSON(b []byte) error { 212 | n.Set = true 213 | var x interface{} 214 | err := json.Unmarshal(b, &x) 215 | if err != nil { 216 | return err 217 | } 218 | err = n.Scan(x) 219 | return err 220 | } 221 | -------------------------------------------------------------------------------- /example/strings_only_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "encoding/json" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestStrState(t *testing.T) { 15 | x := StrState("") 16 | assert.Equal(t, "", x.String()) 17 | 18 | assert.Equal(t, StrState("pending"), StrStatePending) 19 | assert.Equal(t, StrState("running"), StrStateRunning) 20 | assert.Equal(t, &x, StrState("").Ptr()) 21 | } 22 | 23 | func TestStrStateMustParse(t *testing.T) { 24 | x := `avocado` 25 | 26 | assert.PanicsWithError(t, x+" is not a valid StrState, try [pending, running, completed, error]", func() { MustParseStrState(x) }) 27 | assert.NotPanics(t, func() { MustParseStrState(StrStateFailed.String()) }) 28 | } 29 | 30 | func TestStrStateUnmarshal(t *testing.T) { 31 | type testData struct { 32 | StrStateX StrState `json:"state"` 33 | } 34 | tests := []struct { 35 | name string 36 | input string 37 | output *testData 38 | errorExpected bool 39 | err error 40 | }{ 41 | { 42 | name: "pending", 43 | input: `{"state":"Pending"}`, 44 | output: &testData{StrStateX: StrStatePending}, 45 | errorExpected: false, 46 | err: nil, 47 | }, 48 | { 49 | name: "pendinglower", 50 | input: `{"state":"pending"}`, 51 | output: &testData{StrStateX: StrStatePending}, 52 | errorExpected: false, 53 | err: nil, 54 | }, 55 | { 56 | name: "running", 57 | input: `{"state":"RUNNING"}`, 58 | output: &testData{StrStateX: StrStateRunning}, 59 | errorExpected: false, 60 | err: nil, 61 | }, 62 | { 63 | name: "running", 64 | input: `{"state":"running"}`, 65 | output: &testData{StrStateX: StrStateRunning}, 66 | errorExpected: false, 67 | err: nil, 68 | }, 69 | { 70 | name: "completed", 71 | input: `{"state":"Completed"}`, 72 | output: &testData{StrStateX: StrStateCompleted}, 73 | errorExpected: false, 74 | err: nil, 75 | }, 76 | { 77 | name: "completedlower", 78 | input: `{"state":"completed"}`, 79 | output: &testData{StrStateX: StrStateCompleted}, 80 | errorExpected: false, 81 | err: nil, 82 | }, 83 | { 84 | name: "failed", 85 | input: `{"state":"Error"}`, 86 | output: &testData{StrStateX: StrStateFailed}, 87 | errorExpected: false, 88 | err: nil, 89 | }, 90 | { 91 | name: "failedlower", 92 | input: `{"state":"error"}`, 93 | output: &testData{StrStateX: StrStateFailed}, 94 | errorExpected: false, 95 | err: nil, 96 | }, 97 | } 98 | 99 | for _, test := range tests { 100 | t.Run(test.name, func(tt *testing.T) { 101 | x := &testData{} 102 | err := json.Unmarshal([]byte(test.input), x) 103 | if !test.errorExpected { 104 | require.NoError(tt, err, "failed unmarshalling the json.") 105 | assert.Equal(tt, test.output.StrStateX, x.StrStateX) 106 | } else { 107 | require.Error(tt, err) 108 | assert.EqualError(tt, err, test.err.Error()) 109 | } 110 | }) 111 | } 112 | } 113 | 114 | func TestStrStateMarshal(t *testing.T) { 115 | type testData struct { 116 | StrStateX StrState `json:"state"` 117 | } 118 | tests := []struct { 119 | name string 120 | input *testData 121 | output string 122 | errorExpected bool 123 | err error 124 | }{ 125 | { 126 | name: "black", 127 | output: `{"state":"pending"}`, 128 | input: &testData{StrStateX: StrStatePending}, 129 | errorExpected: false, 130 | err: nil, 131 | }, 132 | { 133 | name: "white", 134 | output: `{"state":"running"}`, 135 | input: &testData{StrStateX: StrStateRunning}, 136 | errorExpected: false, 137 | err: nil, 138 | }, 139 | { 140 | name: "red", 141 | output: `{"state":"completed"}`, 142 | input: &testData{StrStateX: StrStateCompleted}, 143 | errorExpected: false, 144 | err: nil, 145 | }, 146 | { 147 | name: "green", 148 | output: `{"state":"error"}`, 149 | input: &testData{StrStateX: StrStateFailed}, 150 | errorExpected: false, 151 | err: nil, 152 | }, 153 | } 154 | 155 | for _, test := range tests { 156 | t.Run(test.name, func(tt *testing.T) { 157 | raw, err := json.Marshal(test.input) 158 | require.NoError(tt, err, "failed marshalling to json") 159 | assert.JSONEq(tt, test.output, string(raw)) 160 | }) 161 | } 162 | } 163 | 164 | func TestStrStateSQLExtras(t *testing.T) { 165 | _, err := ParseStrState(`NotAState`) 166 | assert.Error(t, err, "Should have had an error parsing a non status") 167 | 168 | var ( 169 | intVal int = 3 170 | strVal string = "completed" 171 | enumVal StrState = StrStateCompleted 172 | nullInt *int 173 | nullInt64 *int64 174 | nullUint *uint 175 | nullUint64 *uint64 176 | nullString *string 177 | nullStrState *StrState 178 | ) 179 | 180 | tests := map[string]struct { 181 | input interface{} 182 | result NullStrState 183 | }{ 184 | "nil": {}, 185 | "val": { 186 | input: StrStatePending, 187 | result: NullStrState{ 188 | StrState: StrStatePending, 189 | Valid: true, 190 | }, 191 | }, 192 | "ptr": { 193 | input: &enumVal, 194 | result: NullStrState{ 195 | StrState: StrStateCompleted, 196 | Valid: true, 197 | }, 198 | }, 199 | "string": { 200 | input: strVal, 201 | result: NullStrState{ 202 | StrState: StrStateCompleted, 203 | Valid: true, 204 | }, 205 | }, 206 | "*string": { 207 | input: &strVal, 208 | result: NullStrState{ 209 | StrState: StrStateCompleted, 210 | Valid: true, 211 | }, 212 | }, 213 | "invalid string": { 214 | input: "random value", 215 | }, 216 | "[]byte": { 217 | input: []byte(StrStateRunning.String()), 218 | result: NullStrState{ 219 | StrState: StrStateRunning, 220 | Valid: true, 221 | }, 222 | }, 223 | "int": { 224 | input: intVal, 225 | }, 226 | "*int": { 227 | input: &intVal, 228 | }, 229 | "nullInt": { 230 | input: nullInt, 231 | }, 232 | "nullInt64": { 233 | input: nullInt64, 234 | }, 235 | "nullUint": { 236 | input: nullUint, 237 | }, 238 | "nullUint64": { 239 | input: nullUint64, 240 | }, 241 | "nullString": { 242 | input: nullString, 243 | }, 244 | "nullImageType": { 245 | input: nullStrState, 246 | }, 247 | "int as []byte": { // must have --sqlnullint flag to get this feature. 248 | input: []byte("3"), 249 | }, 250 | } 251 | 252 | for name, tc := range tests { 253 | t.Run(name, func(t *testing.T) { 254 | status := NewNullStrState(tc.input) 255 | assert.Equal(t, status, tc.result) 256 | }) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /example/suffix.enum.gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // SuffixGen is a Suffix of type gen. 19 | SuffixGen Suffix = "gen" 20 | ) 21 | 22 | var ErrInvalidSuffix = errors.New("not a valid Suffix") 23 | 24 | // String implements the Stringer interface. 25 | func (x Suffix) String() string { 26 | return string(x) 27 | } 28 | 29 | // IsValid provides a quick way to determine if the typed value is 30 | // part of the allowed enumerated values 31 | func (x Suffix) IsValid() bool { 32 | _, err := ParseSuffix(string(x)) 33 | return err == nil 34 | } 35 | 36 | var _SuffixValue = map[string]Suffix{ 37 | "gen": SuffixGen, 38 | } 39 | 40 | // ParseSuffix attempts to convert a string to a Suffix. 41 | func ParseSuffix(name string) (Suffix, error) { 42 | if x, ok := _SuffixValue[name]; ok { 43 | return x, nil 44 | } 45 | return Suffix(""), fmt.Errorf("%s is %w", name, ErrInvalidSuffix) 46 | } 47 | -------------------------------------------------------------------------------- /example/suffix.enum.gen_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // SuffixTestSomeItem is a SuffixTest of type some_item. 19 | SuffixTestSomeItem SuffixTest = "some_item" 20 | ) 21 | 22 | var ErrInvalidSuffixTest = errors.New("not a valid SuffixTest") 23 | 24 | // String implements the Stringer interface. 25 | func (x SuffixTest) String() string { 26 | return string(x) 27 | } 28 | 29 | // IsValid provides a quick way to determine if the typed value is 30 | // part of the allowed enumerated values 31 | func (x SuffixTest) IsValid() bool { 32 | _, err := ParseSuffixTest(string(x)) 33 | return err == nil 34 | } 35 | 36 | var _SuffixTestValue = map[string]SuffixTest{ 37 | "some_item": SuffixTestSomeItem, 38 | } 39 | 40 | // ParseSuffixTest attempts to convert a string to a SuffixTest. 41 | func ParseSuffixTest(name string) (SuffixTest, error) { 42 | if x, ok := _SuffixTestValue[name]; ok { 43 | return x, nil 44 | } 45 | return SuffixTest(""), fmt.Errorf("%s is %w", name, ErrInvalidSuffixTest) 46 | } 47 | -------------------------------------------------------------------------------- /example/suffix.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum -b example --output-suffix .enum.gen 2 | 3 | //go:build example 4 | // +build example 5 | 6 | package example 7 | 8 | // Suffix ENUM(gen) 9 | type Suffix string 10 | -------------------------------------------------------------------------------- /example/suffix_test.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum -b example --output-suffix .enum.gen 2 | 3 | //go:build example 4 | // +build example 5 | 6 | package example 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | // SuffixTest ENUM(some_item) 15 | type SuffixTest string 16 | 17 | func TestSuffix(t *testing.T) { 18 | x := Suffix("") 19 | assert.Equal(t, "", x.String()) 20 | 21 | assert.Equal(t, Suffix("gen"), SuffixGen) 22 | } 23 | 24 | func TestSuffixTest(t *testing.T) { 25 | x := SuffixTest("") 26 | assert.Equal(t, "", x.String()) 27 | 28 | assert.Equal(t, SuffixTest("some_item"), SuffixTestSomeItem) 29 | } 30 | -------------------------------------------------------------------------------- /example/user_globbed.tmpl: -------------------------------------------------------------------------------- 1 | func Parse{{.enum.Name}}GlobbedExample() bool { 2 | return true 3 | } 4 | -------------------------------------------------------------------------------- /example/user_globbed2.tmpl: -------------------------------------------------------------------------------- 1 | func Parse{{.enum.Name}}GlobbedExample2() bool { 2 | return true 3 | } 4 | -------------------------------------------------------------------------------- /example/user_template.go: -------------------------------------------------------------------------------- 1 | //go:generate ../bin/go-enum -t user_template.tmpl -t *user_glob*.tmpl -b example 2 | 3 | package example 4 | 5 | // OceanColor is an enumeration of ocean colors that are allowed. 6 | /* 7 | ENUM( 8 | Cerulean 9 | Blue 10 | Green 11 | ) 12 | */ 13 | type OceanColor int 14 | -------------------------------------------------------------------------------- /example/user_template.tmpl: -------------------------------------------------------------------------------- 1 | // Additional template 2 | func Parse{{.enum.Name}}Example() bool { 3 | return true 4 | } 5 | func Parse{{.enum.Name}}Description() string { 6 | return `{{.enum.Comment}}` 7 | } 8 | -------------------------------------------------------------------------------- /example/user_template_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-enum DO NOT EDIT. 2 | // Version: example 3 | // Revision: example 4 | // Build Date: example 5 | // Built By: example 6 | 7 | //go:build example 8 | // +build example 9 | 10 | package example 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | const ( 18 | // OceanColorCerulean is a OceanColor of type Cerulean. 19 | OceanColorCerulean OceanColor = iota 20 | // OceanColorBlue is a OceanColor of type Blue. 21 | OceanColorBlue 22 | // OceanColorGreen is a OceanColor of type Green. 23 | OceanColorGreen 24 | ) 25 | 26 | var ErrInvalidOceanColor = errors.New("not a valid OceanColor") 27 | 28 | const _OceanColorName = "CeruleanBlueGreen" 29 | 30 | var _OceanColorMap = map[OceanColor]string{ 31 | OceanColorCerulean: _OceanColorName[0:8], 32 | OceanColorBlue: _OceanColorName[8:12], 33 | OceanColorGreen: _OceanColorName[12:17], 34 | } 35 | 36 | // String implements the Stringer interface. 37 | func (x OceanColor) String() string { 38 | if str, ok := _OceanColorMap[x]; ok { 39 | return str 40 | } 41 | return fmt.Sprintf("OceanColor(%d)", x) 42 | } 43 | 44 | // IsValid provides a quick way to determine if the typed value is 45 | // part of the allowed enumerated values 46 | func (x OceanColor) IsValid() bool { 47 | _, ok := _OceanColorMap[x] 48 | return ok 49 | } 50 | 51 | var _OceanColorValue = map[string]OceanColor{ 52 | _OceanColorName[0:8]: OceanColorCerulean, 53 | _OceanColorName[8:12]: OceanColorBlue, 54 | _OceanColorName[12:17]: OceanColorGreen, 55 | } 56 | 57 | // ParseOceanColor attempts to convert a string to a OceanColor. 58 | func ParseOceanColor(name string) (OceanColor, error) { 59 | if x, ok := _OceanColorValue[name]; ok { 60 | return x, nil 61 | } 62 | return OceanColor(0), fmt.Errorf("%s is %w", name, ErrInvalidOceanColor) 63 | } 64 | 65 | func ParseOceanColorGlobbedExample() bool { 66 | return true 67 | } 68 | func ParseOceanColorGlobbedExample2() bool { 69 | return true 70 | } 71 | 72 | // Additional template 73 | func ParseOceanColorExample() bool { 74 | return true 75 | } 76 | func ParseOceanColorDescription() string { 77 | return `OceanColor is an enumeration of ocean colors that are allowed.` 78 | } 79 | -------------------------------------------------------------------------------- /example/user_template_test.go: -------------------------------------------------------------------------------- 1 | //go:build example 2 | // +build example 3 | 4 | package example 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestUserTemplateColor(t *testing.T) { 13 | assert.Equal(t, OceanColor(0), OceanColorCerulean) 14 | assert.Equal(t, "OceanColor is an enumeration of ocean colors that are allowed.", ParseOceanColorDescription()) 15 | assert.Equal(t, true, ParseOceanColorExample()) 16 | assert.Equal(t, true, ParseOceanColorGlobbedExample()) 17 | assert.Equal(t, true, ParseOceanColorGlobbedExample2()) 18 | 19 | val, err := ParseOceanColor("Cerulean") 20 | assert.NoError(t, err) 21 | assert.Equal(t, "Cerulean", val.String()) 22 | 23 | assert.Equal(t, "OceanColor(99)", OceanColor(99).String()) 24 | _, err = ParseOceanColor("-1") 25 | assert.Error(t, err) 26 | } 27 | -------------------------------------------------------------------------------- /generator/.snapshots/Test118ExampleFile-1.18: -------------------------------------------------------------------------------- 1 | ([]string) (len=172) { 2 | (string) (len=41) "// Code generated by go-enum DO NOT EDIT.", 3 | (string) (len=13) "// Version: -", 4 | (string) (len=14) "// Revision: -", 5 | (string) (len=16) "// Build Date: -", 6 | (string) (len=14) "// Built By: -", 7 | (string) "", 8 | (string) (len=17) "package generator", 9 | (string) "", 10 | (string) (len=8) "import (", 11 | (string) (len=22) "\t\"database/sql/driver\"", 12 | (string) (len=9) "\t\"errors\"", 13 | (string) (len=6) "\t\"fmt\"", 14 | (string) (len=10) "\t\"strings\"", 15 | (string) (len=1) ")", 16 | (string) "", 17 | (string) (len=7) "const (", 18 | (string) (len=52) "\t// ChangeTypeCreate is a ChangeType of type Create.", 19 | (string) (len=35) "\tChangeTypeCreate ChangeType = iota", 20 | (string) (len=52) "\t// ChangeTypeUpdate is a ChangeType of type Update.", 21 | (string) (len=17) "\tChangeTypeUpdate", 22 | (string) (len=52) "\t// ChangeTypeDelete is a ChangeType of type Delete.", 23 | (string) (len=17) "\tChangeTypeDelete", 24 | (string) (len=1) ")", 25 | (string) "", 26 | (string) (len=111) "var ErrInvalidChangeType = fmt.Errorf(\"not a valid ChangeType, try [%s]\", strings.Join(_ChangeTypeNames, \", \"))", 27 | (string) "", 28 | (string) (len=44) "const _ChangeTypeName = \"CreateUpdateDelete\"", 29 | (string) "", 30 | (string) (len=32) "var _ChangeTypeNames = []string{", 31 | (string) (len=22) "\t_ChangeTypeName[0:6],", 32 | (string) (len=23) "\t_ChangeTypeName[6:12],", 33 | (string) (len=24) "\t_ChangeTypeName[12:18],", 34 | (string) (len=1) "}", 35 | (string) "", 36 | (string) (len=74) "// ChangeTypeNames returns a list of possible string values of ChangeType.", 37 | (string) (len=33) "func ChangeTypeNames() []string {", 38 | (string) (len=45) "\ttmp := make([]string, len(_ChangeTypeNames))", 39 | (string) (len=28) "\tcopy(tmp, _ChangeTypeNames)", 40 | (string) (len=11) "\treturn tmp", 41 | (string) (len=1) "}", 42 | (string) "", 43 | (string) (len=43) "var _ChangeTypeMap = map[ChangeType]string{", 44 | (string) (len=40) "\tChangeTypeCreate: _ChangeTypeName[0:6],", 45 | (string) (len=41) "\tChangeTypeUpdate: _ChangeTypeName[6:12],", 46 | (string) (len=42) "\tChangeTypeDelete: _ChangeTypeName[12:18],", 47 | (string) (len=1) "}", 48 | (string) "", 49 | (string) (len=44) "// String implements the Stringer interface.", 50 | (string) (len=37) "func (x ChangeType) String() string {", 51 | (string) (len=38) "\tif str, ok := _ChangeTypeMap[x]; ok {", 52 | (string) (len=12) "\t\treturn str", 53 | (string) (len=2) "\t}", 54 | (string) (len=40) "\treturn fmt.Sprintf(\"ChangeType(%d)\", x)", 55 | (string) (len=1) "}", 56 | (string) "", 57 | (string) (len=66) "// IsValid provides a quick way to determine if the typed value is", 58 | (string) (len=40) "// part of the allowed enumerated values", 59 | (string) (len=36) "func (x ChangeType) IsValid() bool {", 60 | (string) (len=27) "\t_, ok := _ChangeTypeMap[x]", 61 | (string) (len=10) "\treturn ok", 62 | (string) (len=1) "}", 63 | (string) "", 64 | (string) (len=45) "var _ChangeTypeValue = map[string]ChangeType{", 65 | (string) (len=59) "\t_ChangeTypeName[0:6]: ChangeTypeCreate,", 66 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[0:6]): ChangeTypeCreate,", 67 | (string) (len=59) "\t_ChangeTypeName[6:12]: ChangeTypeUpdate,", 68 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[6:12]): ChangeTypeUpdate,", 69 | (string) (len=59) "\t_ChangeTypeName[12:18]: ChangeTypeDelete,", 70 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[12:18]): ChangeTypeDelete,", 71 | (string) (len=1) "}", 72 | (string) "", 73 | (string) (len=64) "// ParseChangeType attempts to convert a string to a ChangeType.", 74 | (string) (len=55) "func ParseChangeType(name string) (ChangeType, error) {", 75 | (string) (len=41) "\tif x, ok := _ChangeTypeValue[name]; ok {", 76 | (string) (len=15) "\t\treturn x, nil", 77 | (string) (len=2) "\t}", 78 | (string) (len=121) "\t// Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to.", 79 | (string) (len=58) "\tif x, ok := _ChangeTypeValue[strings.ToLower(name)]; ok {", 80 | (string) (len=15) "\t\treturn x, nil", 81 | (string) (len=2) "\t}", 82 | (string) (len=73) "\treturn ChangeType(0), fmt.Errorf(\"%s is %w\", name, ErrInvalidChangeType)", 83 | (string) (len=1) "}", 84 | (string) "", 85 | (string) (len=53) "// MarshalText implements the text marshaller method.", 86 | (string) (len=51) "func (x ChangeType) MarshalText() ([]byte, error) {", 87 | (string) (len=31) "\treturn []byte(x.String()), nil", 88 | (string) (len=1) "}", 89 | (string) "", 90 | (string) (len=57) "// UnmarshalText implements the text unmarshaller method.", 91 | (string) (len=55) "func (x *ChangeType) UnmarshalText(text []byte) error {", 92 | (string) (len=21) "\tname := string(text)", 93 | (string) (len=34) "\ttmp, err := ParseChangeType(name)", 94 | (string) (len=16) "\tif err != nil {", 95 | (string) (len=12) "\t\treturn err", 96 | (string) (len=2) "\t}", 97 | (string) (len=9) "\t*x = tmp", 98 | (string) (len=11) "\treturn nil", 99 | (string) (len=1) "}", 100 | (string) "", 101 | (string) (len=96) "var errChangeTypeNilPtr = errors.New(\"value pointer is nil\") // one per type for package clashes", 102 | (string) "", 103 | (string) (len=41) "// Scan implements the Scanner interface.", 104 | (string) (len=58) "func (x *ChangeType) Scan(value interface{}) (err error) {", 105 | (string) (len=18) "\tif value == nil {", 106 | (string) (len=20) "\t\t*x = ChangeType(0)", 107 | (string) (len=8) "\t\treturn", 108 | (string) (len=2) "\t}", 109 | (string) "", 110 | (string) (len=37) "\t// A wider range of scannable types.", 111 | (string) (len=61) "\t// driver.Value values at the top of the list for expediency", 112 | (string) (len=27) "\tswitch v := value.(type) {", 113 | (string) (len=12) "\tcase int64:", 114 | (string) (len=20) "\t\t*x = ChangeType(v)", 115 | (string) (len=13) "\tcase string:", 116 | (string) (len=30) "\t\t*x, err = ParseChangeType(v)", 117 | (string) (len=13) "\tcase []byte:", 118 | (string) (len=38) "\t\t*x, err = ParseChangeType(string(v))", 119 | (string) (len=17) "\tcase ChangeType:", 120 | (string) (len=8) "\t\t*x = v", 121 | (string) (len=10) "\tcase int:", 122 | (string) (len=20) "\t\t*x = ChangeType(v)", 123 | (string) (len=18) "\tcase *ChangeType:", 124 | (string) (len=15) "\t\tif v == nil {", 125 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 126 | (string) (len=3) "\t\t}", 127 | (string) (len=9) "\t\t*x = *v", 128 | (string) (len=11) "\tcase uint:", 129 | (string) (len=20) "\t\t*x = ChangeType(v)", 130 | (string) (len=13) "\tcase uint64:", 131 | (string) (len=20) "\t\t*x = ChangeType(v)", 132 | (string) (len=11) "\tcase *int:", 133 | (string) (len=15) "\t\tif v == nil {", 134 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 135 | (string) (len=3) "\t\t}", 136 | (string) (len=21) "\t\t*x = ChangeType(*v)", 137 | (string) (len=13) "\tcase *int64:", 138 | (string) (len=15) "\t\tif v == nil {", 139 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 140 | (string) (len=3) "\t\t}", 141 | (string) (len=21) "\t\t*x = ChangeType(*v)", 142 | (string) (len=72) "\tcase float64: // json marshals everything as a float64 if it's a number", 143 | (string) (len=20) "\t\t*x = ChangeType(v)", 144 | (string) (len=73) "\tcase *float64: // json marshals everything as a float64 if it's a number", 145 | (string) (len=15) "\t\tif v == nil {", 146 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 147 | (string) (len=3) "\t\t}", 148 | (string) (len=21) "\t\t*x = ChangeType(*v)", 149 | (string) (len=12) "\tcase *uint:", 150 | (string) (len=15) "\t\tif v == nil {", 151 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 152 | (string) (len=3) "\t\t}", 153 | (string) (len=21) "\t\t*x = ChangeType(*v)", 154 | (string) (len=14) "\tcase *uint64:", 155 | (string) (len=15) "\t\tif v == nil {", 156 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 157 | (string) (len=3) "\t\t}", 158 | (string) (len=21) "\t\t*x = ChangeType(*v)", 159 | (string) (len=14) "\tcase *string:", 160 | (string) (len=15) "\t\tif v == nil {", 161 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 162 | (string) (len=3) "\t\t}", 163 | (string) (len=31) "\t\t*x, err = ParseChangeType(*v)", 164 | (string) (len=2) "\t}", 165 | (string) "", 166 | (string) (len=7) "\treturn", 167 | (string) (len=1) "}", 168 | (string) "", 169 | (string) (len=48) "// Value implements the driver Valuer interface.", 170 | (string) (len=51) "func (x ChangeType) Value() (driver.Value, error) {", 171 | (string) (len=23) "\treturn x.String(), nil", 172 | (string) (len=1) "}", 173 | (string) "" 174 | } 175 | -------------------------------------------------------------------------------- /generator/.snapshots/Test118ExampleFileMoreOptions-1.18: -------------------------------------------------------------------------------- 1 | ([]string) (len=189) { 2 | (string) (len=41) "// Code generated by go-enum DO NOT EDIT.", 3 | (string) (len=13) "// Version: -", 4 | (string) (len=14) "// Revision: -", 5 | (string) (len=16) "// Build Date: -", 6 | (string) (len=14) "// Built By: -", 7 | (string) "", 8 | (string) (len=17) "package generator", 9 | (string) "", 10 | (string) (len=8) "import (", 11 | (string) (len=22) "\t\"database/sql/driver\"", 12 | (string) (len=9) "\t\"errors\"", 13 | (string) (len=6) "\t\"fmt\"", 14 | (string) (len=10) "\t\"strings\"", 15 | (string) (len=1) ")", 16 | (string) "", 17 | (string) (len=7) "const (", 18 | (string) (len=52) "\t// ChangeTypeCreate is a ChangeType of type Create.", 19 | (string) (len=35) "\tChangeTypeCreate ChangeType = iota", 20 | (string) (len=52) "\t// ChangeTypeUpdate is a ChangeType of type Update.", 21 | (string) (len=17) "\tChangeTypeUpdate", 22 | (string) (len=52) "\t// ChangeTypeDelete is a ChangeType of type Delete.", 23 | (string) (len=17) "\tChangeTypeDelete", 24 | (string) (len=1) ")", 25 | (string) "", 26 | (string) (len=111) "var ErrInvalidChangeType = fmt.Errorf(\"not a valid ChangeType, try [%s]\", strings.Join(_ChangeTypeNames, \", \"))", 27 | (string) "", 28 | (string) (len=44) "const _ChangeTypeName = \"createupdatedelete\"", 29 | (string) "", 30 | (string) (len=32) "var _ChangeTypeNames = []string{", 31 | (string) (len=22) "\t_ChangeTypeName[0:6],", 32 | (string) (len=23) "\t_ChangeTypeName[6:12],", 33 | (string) (len=24) "\t_ChangeTypeName[12:18],", 34 | (string) (len=1) "}", 35 | (string) "", 36 | (string) (len=74) "// ChangeTypeNames returns a list of possible string values of ChangeType.", 37 | (string) (len=33) "func ChangeTypeNames() []string {", 38 | (string) (len=45) "\ttmp := make([]string, len(_ChangeTypeNames))", 39 | (string) (len=28) "\tcopy(tmp, _ChangeTypeNames)", 40 | (string) (len=11) "\treturn tmp", 41 | (string) (len=1) "}", 42 | (string) "", 43 | (string) (len=43) "var _ChangeTypeMap = map[ChangeType]string{", 44 | (string) (len=40) "\tChangeTypeCreate: _ChangeTypeName[0:6],", 45 | (string) (len=41) "\tChangeTypeUpdate: _ChangeTypeName[6:12],", 46 | (string) (len=42) "\tChangeTypeDelete: _ChangeTypeName[12:18],", 47 | (string) (len=1) "}", 48 | (string) "", 49 | (string) (len=44) "// String implements the Stringer interface.", 50 | (string) (len=37) "func (x ChangeType) String() string {", 51 | (string) (len=38) "\tif str, ok := _ChangeTypeMap[x]; ok {", 52 | (string) (len=12) "\t\treturn str", 53 | (string) (len=2) "\t}", 54 | (string) (len=40) "\treturn fmt.Sprintf(\"ChangeType(%d)\", x)", 55 | (string) (len=1) "}", 56 | (string) "", 57 | (string) (len=66) "// IsValid provides a quick way to determine if the typed value is", 58 | (string) (len=40) "// part of the allowed enumerated values", 59 | (string) (len=36) "func (x ChangeType) IsValid() bool {", 60 | (string) (len=27) "\t_, ok := _ChangeTypeMap[x]", 61 | (string) (len=10) "\treturn ok", 62 | (string) (len=1) "}", 63 | (string) "", 64 | (string) (len=45) "var _ChangeTypeValue = map[string]ChangeType{", 65 | (string) (len=59) "\t_ChangeTypeName[0:6]: ChangeTypeCreate,", 66 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[0:6]): ChangeTypeCreate,", 67 | (string) (len=59) "\t_ChangeTypeName[6:12]: ChangeTypeUpdate,", 68 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[6:12]): ChangeTypeUpdate,", 69 | (string) (len=59) "\t_ChangeTypeName[12:18]: ChangeTypeDelete,", 70 | (string) (len=59) "\tstrings.ToLower(_ChangeTypeName[12:18]): ChangeTypeDelete,", 71 | (string) (len=1) "}", 72 | (string) "", 73 | (string) (len=64) "// ParseChangeType attempts to convert a string to a ChangeType.", 74 | (string) (len=55) "func ParseChangeType(name string) (ChangeType, error) {", 75 | (string) (len=41) "\tif x, ok := _ChangeTypeValue[name]; ok {", 76 | (string) (len=15) "\t\treturn x, nil", 77 | (string) (len=2) "\t}", 78 | (string) (len=121) "\t// Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to.", 79 | (string) (len=58) "\tif x, ok := _ChangeTypeValue[strings.ToLower(name)]; ok {", 80 | (string) (len=15) "\t\treturn x, nil", 81 | (string) (len=2) "\t}", 82 | (string) (len=73) "\treturn ChangeType(0), fmt.Errorf(\"%s is %w\", name, ErrInvalidChangeType)", 83 | (string) (len=1) "}", 84 | (string) "", 85 | (string) (len=85) "// MustParseChangeType converts a string to a ChangeType, and panics if is not valid.", 86 | (string) (len=50) "func MustParseChangeType(name string) ChangeType {", 87 | (string) (len=34) "\tval, err := ParseChangeType(name)", 88 | (string) (len=16) "\tif err != nil {", 89 | (string) (len=12) "\t\tpanic(err)", 90 | (string) (len=2) "\t}", 91 | (string) (len=11) "\treturn val", 92 | (string) (len=1) "}", 93 | (string) "", 94 | (string) (len=53) "// MarshalText implements the text marshaller method.", 95 | (string) (len=51) "func (x ChangeType) MarshalText() ([]byte, error) {", 96 | (string) (len=31) "\treturn []byte(x.String()), nil", 97 | (string) (len=1) "}", 98 | (string) "", 99 | (string) (len=57) "// UnmarshalText implements the text unmarshaller method.", 100 | (string) (len=55) "func (x *ChangeType) UnmarshalText(text []byte) error {", 101 | (string) (len=21) "\tname := string(text)", 102 | (string) (len=34) "\ttmp, err := ParseChangeType(name)", 103 | (string) (len=16) "\tif err != nil {", 104 | (string) (len=12) "\t\treturn err", 105 | (string) (len=2) "\t}", 106 | (string) (len=9) "\t*x = tmp", 107 | (string) (len=11) "\treturn nil", 108 | (string) (len=1) "}", 109 | (string) "", 110 | (string) (len=96) "var errChangeTypeNilPtr = errors.New(\"value pointer is nil\") // one per type for package clashes", 111 | (string) "", 112 | (string) (len=41) "// Scan implements the Scanner interface.", 113 | (string) (len=58) "func (x *ChangeType) Scan(value interface{}) (err error) {", 114 | (string) (len=18) "\tif value == nil {", 115 | (string) (len=20) "\t\t*x = ChangeType(0)", 116 | (string) (len=8) "\t\treturn", 117 | (string) (len=2) "\t}", 118 | (string) "", 119 | (string) (len=37) "\t// A wider range of scannable types.", 120 | (string) (len=61) "\t// driver.Value values at the top of the list for expediency", 121 | (string) (len=27) "\tswitch v := value.(type) {", 122 | (string) (len=12) "\tcase int64:", 123 | (string) (len=20) "\t\t*x = ChangeType(v)", 124 | (string) (len=13) "\tcase string:", 125 | (string) (len=30) "\t\t*x, err = ParseChangeType(v)", 126 | (string) (len=13) "\tcase []byte:", 127 | (string) (len=38) "\t\t*x, err = ParseChangeType(string(v))", 128 | (string) (len=17) "\tcase ChangeType:", 129 | (string) (len=8) "\t\t*x = v", 130 | (string) (len=10) "\tcase int:", 131 | (string) (len=20) "\t\t*x = ChangeType(v)", 132 | (string) (len=18) "\tcase *ChangeType:", 133 | (string) (len=15) "\t\tif v == nil {", 134 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 135 | (string) (len=3) "\t\t}", 136 | (string) (len=9) "\t\t*x = *v", 137 | (string) (len=11) "\tcase uint:", 138 | (string) (len=20) "\t\t*x = ChangeType(v)", 139 | (string) (len=13) "\tcase uint64:", 140 | (string) (len=20) "\t\t*x = ChangeType(v)", 141 | (string) (len=11) "\tcase *int:", 142 | (string) (len=15) "\t\tif v == nil {", 143 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 144 | (string) (len=3) "\t\t}", 145 | (string) (len=21) "\t\t*x = ChangeType(*v)", 146 | (string) (len=13) "\tcase *int64:", 147 | (string) (len=15) "\t\tif v == nil {", 148 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 149 | (string) (len=3) "\t\t}", 150 | (string) (len=21) "\t\t*x = ChangeType(*v)", 151 | (string) (len=72) "\tcase float64: // json marshals everything as a float64 if it's a number", 152 | (string) (len=20) "\t\t*x = ChangeType(v)", 153 | (string) (len=73) "\tcase *float64: // json marshals everything as a float64 if it's a number", 154 | (string) (len=15) "\t\tif v == nil {", 155 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 156 | (string) (len=3) "\t\t}", 157 | (string) (len=21) "\t\t*x = ChangeType(*v)", 158 | (string) (len=12) "\tcase *uint:", 159 | (string) (len=15) "\t\tif v == nil {", 160 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 161 | (string) (len=3) "\t\t}", 162 | (string) (len=21) "\t\t*x = ChangeType(*v)", 163 | (string) (len=14) "\tcase *uint64:", 164 | (string) (len=15) "\t\tif v == nil {", 165 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 166 | (string) (len=3) "\t\t}", 167 | (string) (len=21) "\t\t*x = ChangeType(*v)", 168 | (string) (len=14) "\tcase *string:", 169 | (string) (len=15) "\t\tif v == nil {", 170 | (string) (len=29) "\t\t\treturn errChangeTypeNilPtr", 171 | (string) (len=3) "\t\t}", 172 | (string) (len=31) "\t\t*x, err = ParseChangeType(*v)", 173 | (string) (len=2) "\t}", 174 | (string) "", 175 | (string) (len=7) "\treturn", 176 | (string) (len=1) "}", 177 | (string) "", 178 | (string) (len=48) "// Value implements the driver Valuer interface.", 179 | (string) (len=51) "func (x ChangeType) Value() (driver.Value, error) {", 180 | (string) (len=23) "\treturn x.String(), nil", 181 | (string) (len=1) "}", 182 | (string) "", 183 | (string) (len=22) "// Additional template", 184 | (string) (len=36) "func ParseChangeTypeExample() bool {", 185 | (string) (len=12) "\treturn true", 186 | (string) (len=1) "}", 187 | (string) (len=42) "func ParseChangeTypeDescription() string {", 188 | (string) (len=50) "\treturn `ChangeType is a type of change detected.`", 189 | (string) (len=1) "}", 190 | (string) "" 191 | } 192 | -------------------------------------------------------------------------------- /generator/.snapshots/Test118NoPrefixExampleFile-1.18: -------------------------------------------------------------------------------- 1 | ([]string) (len=98) { 2 | (string) (len=41) "// Code generated by go-enum DO NOT EDIT.", 3 | (string) (len=13) "// Version: -", 4 | (string) (len=14) "// Revision: -", 5 | (string) (len=16) "// Build Date: -", 6 | (string) (len=14) "// Built By: -", 7 | (string) "", 8 | (string) (len=17) "package generator", 9 | (string) "", 10 | (string) (len=8) "import (", 11 | (string) (len=9) "\t\"errors\"", 12 | (string) (len=6) "\t\"fmt\"", 13 | (string) (len=10) "\t\"strings\"", 14 | (string) (len=1) ")", 15 | (string) "", 16 | (string) (len=7) "const (", 17 | (string) (len=42) "\t// Create is a ChangeType of type Create.", 18 | (string) (len=25) "\tCreate ChangeType = iota", 19 | (string) (len=42) "\t// Update is a ChangeType of type Update.", 20 | (string) (len=7) "\tUpdate", 21 | (string) (len=42) "\t// Delete is a ChangeType of type Delete.", 22 | (string) (len=7) "\tDelete", 23 | (string) (len=1) ")", 24 | (string) "", 25 | (string) (len=63) "var ErrInvalidChangeType = errors.New(\"not a valid ChangeType\")", 26 | (string) "", 27 | (string) (len=44) "const _ChangeTypeName = \"CreateUpdateDelete\"", 28 | (string) "", 29 | (string) (len=43) "var _ChangeTypeMap = map[ChangeType]string{", 30 | (string) (len=30) "\tCreate: _ChangeTypeName[0:6],", 31 | (string) (len=31) "\tUpdate: _ChangeTypeName[6:12],", 32 | (string) (len=32) "\tDelete: _ChangeTypeName[12:18],", 33 | (string) (len=1) "}", 34 | (string) "", 35 | (string) (len=44) "// String implements the Stringer interface.", 36 | (string) (len=37) "func (x ChangeType) String() string {", 37 | (string) (len=38) "\tif str, ok := _ChangeTypeMap[x]; ok {", 38 | (string) (len=12) "\t\treturn str", 39 | (string) (len=2) "\t}", 40 | (string) (len=40) "\treturn fmt.Sprintf(\"ChangeType(%d)\", x)", 41 | (string) (len=1) "}", 42 | (string) "", 43 | (string) (len=66) "// IsValid provides a quick way to determine if the typed value is", 44 | (string) (len=40) "// part of the allowed enumerated values", 45 | (string) (len=36) "func (x ChangeType) IsValid() bool {", 46 | (string) (len=27) "\t_, ok := _ChangeTypeMap[x]", 47 | (string) (len=10) "\treturn ok", 48 | (string) (len=1) "}", 49 | (string) "", 50 | (string) (len=45) "var _ChangeTypeValue = map[string]ChangeType{", 51 | (string) (len=49) "\t_ChangeTypeName[0:6]: Create,", 52 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[0:6]): Create,", 53 | (string) (len=49) "\t_ChangeTypeName[6:12]: Update,", 54 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[6:12]): Update,", 55 | (string) (len=49) "\t_ChangeTypeName[12:18]: Delete,", 56 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[12:18]): Delete,", 57 | (string) (len=1) "}", 58 | (string) "", 59 | (string) (len=64) "// ParseChangeType attempts to convert a string to a ChangeType.", 60 | (string) (len=55) "func ParseChangeType(name string) (ChangeType, error) {", 61 | (string) (len=41) "\tif x, ok := _ChangeTypeValue[name]; ok {", 62 | (string) (len=15) "\t\treturn x, nil", 63 | (string) (len=2) "\t}", 64 | (string) (len=73) "\treturn ChangeType(0), fmt.Errorf(\"%s is %w\", name, ErrInvalidChangeType)", 65 | (string) (len=1) "}", 66 | (string) "", 67 | (string) (len=53) "// MarshalText implements the text marshaller method.", 68 | (string) (len=51) "func (x ChangeType) MarshalText() ([]byte, error) {", 69 | (string) (len=31) "\treturn []byte(x.String()), nil", 70 | (string) (len=1) "}", 71 | (string) "", 72 | (string) (len=57) "// UnmarshalText implements the text unmarshaller method.", 73 | (string) (len=55) "func (x *ChangeType) UnmarshalText(text []byte) error {", 74 | (string) (len=21) "\tname := string(text)", 75 | (string) (len=34) "\ttmp, err := ParseChangeType(name)", 76 | (string) (len=16) "\tif err != nil {", 77 | (string) (len=12) "\t\treturn err", 78 | (string) (len=2) "\t}", 79 | (string) (len=9) "\t*x = tmp", 80 | (string) (len=11) "\treturn nil", 81 | (string) (len=1) "}", 82 | (string) "", 83 | (string) (len=55) "// Set implements the Golang flag.Value interface func.", 84 | (string) (len=44) "func (x *ChangeType) Set(val string) error {", 85 | (string) (len=31) "\tv, err := ParseChangeType(val)", 86 | (string) (len=7) "\t*x = v", 87 | (string) (len=11) "\treturn err", 88 | (string) (len=1) "}", 89 | (string) "", 90 | (string) (len=56) "// Get implements the Golang flag.Getter interface func.", 91 | (string) (len=40) "func (x *ChangeType) Get() interface{} {", 92 | (string) (len=10) "\treturn *x", 93 | (string) (len=1) "}", 94 | (string) "", 95 | (string) (len=62) "// Type implements the github.com/spf13/pFlag Value interface.", 96 | (string) (len=36) "func (x *ChangeType) Type() string {", 97 | (string) (len=20) "\treturn \"ChangeType\"", 98 | (string) (len=1) "}", 99 | (string) "" 100 | } 101 | -------------------------------------------------------------------------------- /generator/.snapshots/Test118NoPrefixExampleFileWithSnakeToCamel-1.18: -------------------------------------------------------------------------------- 1 | ([]string) (len=98) { 2 | (string) (len=41) "// Code generated by go-enum DO NOT EDIT.", 3 | (string) (len=13) "// Version: -", 4 | (string) (len=14) "// Revision: -", 5 | (string) (len=16) "// Build Date: -", 6 | (string) (len=14) "// Built By: -", 7 | (string) "", 8 | (string) (len=17) "package generator", 9 | (string) "", 10 | (string) (len=8) "import (", 11 | (string) (len=9) "\t\"errors\"", 12 | (string) (len=6) "\t\"fmt\"", 13 | (string) (len=10) "\t\"strings\"", 14 | (string) (len=1) ")", 15 | (string) "", 16 | (string) (len=7) "const (", 17 | (string) (len=42) "\t// Create is a ChangeType of type Create.", 18 | (string) (len=25) "\tCreate ChangeType = iota", 19 | (string) (len=42) "\t// Update is a ChangeType of type Update.", 20 | (string) (len=7) "\tUpdate", 21 | (string) (len=42) "\t// Delete is a ChangeType of type Delete.", 22 | (string) (len=7) "\tDelete", 23 | (string) (len=1) ")", 24 | (string) "", 25 | (string) (len=63) "var ErrInvalidChangeType = errors.New(\"not a valid ChangeType\")", 26 | (string) "", 27 | (string) (len=44) "const _ChangeTypeName = \"CreateUpdateDelete\"", 28 | (string) "", 29 | (string) (len=43) "var _ChangeTypeMap = map[ChangeType]string{", 30 | (string) (len=30) "\tCreate: _ChangeTypeName[0:6],", 31 | (string) (len=31) "\tUpdate: _ChangeTypeName[6:12],", 32 | (string) (len=32) "\tDelete: _ChangeTypeName[12:18],", 33 | (string) (len=1) "}", 34 | (string) "", 35 | (string) (len=44) "// String implements the Stringer interface.", 36 | (string) (len=37) "func (x ChangeType) String() string {", 37 | (string) (len=38) "\tif str, ok := _ChangeTypeMap[x]; ok {", 38 | (string) (len=12) "\t\treturn str", 39 | (string) (len=2) "\t}", 40 | (string) (len=40) "\treturn fmt.Sprintf(\"ChangeType(%d)\", x)", 41 | (string) (len=1) "}", 42 | (string) "", 43 | (string) (len=66) "// IsValid provides a quick way to determine if the typed value is", 44 | (string) (len=40) "// part of the allowed enumerated values", 45 | (string) (len=36) "func (x ChangeType) IsValid() bool {", 46 | (string) (len=27) "\t_, ok := _ChangeTypeMap[x]", 47 | (string) (len=10) "\treturn ok", 48 | (string) (len=1) "}", 49 | (string) "", 50 | (string) (len=45) "var _ChangeTypeValue = map[string]ChangeType{", 51 | (string) (len=49) "\t_ChangeTypeName[0:6]: Create,", 52 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[0:6]): Create,", 53 | (string) (len=49) "\t_ChangeTypeName[6:12]: Update,", 54 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[6:12]): Update,", 55 | (string) (len=49) "\t_ChangeTypeName[12:18]: Delete,", 56 | (string) (len=49) "\tstrings.ToLower(_ChangeTypeName[12:18]): Delete,", 57 | (string) (len=1) "}", 58 | (string) "", 59 | (string) (len=64) "// ParseChangeType attempts to convert a string to a ChangeType.", 60 | (string) (len=55) "func ParseChangeType(name string) (ChangeType, error) {", 61 | (string) (len=41) "\tif x, ok := _ChangeTypeValue[name]; ok {", 62 | (string) (len=15) "\t\treturn x, nil", 63 | (string) (len=2) "\t}", 64 | (string) (len=73) "\treturn ChangeType(0), fmt.Errorf(\"%s is %w\", name, ErrInvalidChangeType)", 65 | (string) (len=1) "}", 66 | (string) "", 67 | (string) (len=53) "// MarshalText implements the text marshaller method.", 68 | (string) (len=51) "func (x ChangeType) MarshalText() ([]byte, error) {", 69 | (string) (len=31) "\treturn []byte(x.String()), nil", 70 | (string) (len=1) "}", 71 | (string) "", 72 | (string) (len=57) "// UnmarshalText implements the text unmarshaller method.", 73 | (string) (len=55) "func (x *ChangeType) UnmarshalText(text []byte) error {", 74 | (string) (len=21) "\tname := string(text)", 75 | (string) (len=34) "\ttmp, err := ParseChangeType(name)", 76 | (string) (len=16) "\tif err != nil {", 77 | (string) (len=12) "\t\treturn err", 78 | (string) (len=2) "\t}", 79 | (string) (len=9) "\t*x = tmp", 80 | (string) (len=11) "\treturn nil", 81 | (string) (len=1) "}", 82 | (string) "", 83 | (string) (len=55) "// Set implements the Golang flag.Value interface func.", 84 | (string) (len=44) "func (x *ChangeType) Set(val string) error {", 85 | (string) (len=31) "\tv, err := ParseChangeType(val)", 86 | (string) (len=7) "\t*x = v", 87 | (string) (len=11) "\treturn err", 88 | (string) (len=1) "}", 89 | (string) "", 90 | (string) (len=56) "// Get implements the Golang flag.Getter interface func.", 91 | (string) (len=40) "func (x *ChangeType) Get() interface{} {", 92 | (string) (len=10) "\treturn *x", 93 | (string) (len=1) "}", 94 | (string) "", 95 | (string) (len=62) "// Type implements the github.com/spf13/pFlag Value interface.", 96 | (string) (len=36) "func (x *ChangeType) Type() string {", 97 | (string) (len=20) "\treturn \"ChangeType\"", 98 | (string) (len=1) "}", 99 | (string) "" 100 | } 101 | -------------------------------------------------------------------------------- /generator/embedded_1.16.go: -------------------------------------------------------------------------------- 1 | //go:build go1.16 2 | // +build go1.16 3 | 4 | package generator 5 | 6 | import ( 7 | "embed" 8 | "text/template" 9 | ) 10 | 11 | //go:embed enum.tmpl enum_string.tmpl 12 | var content embed.FS 13 | 14 | func (g *Generator) addEmbeddedTemplates() { 15 | g.t = template.Must(g.t.ParseFS(content, "*.tmpl")) 16 | } 17 | -------------------------------------------------------------------------------- /generator/example_1.18_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package generator 5 | 6 | // SumIntsOrFloats sums the values of map m. It supports both int64 and float64 7 | // as types for map values. 8 | func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { 9 | var s V 10 | for _, v := range m { 11 | s += v 12 | } 13 | return s 14 | } 15 | 16 | // ChangeType is a type of change detected. 17 | /* ENUM( 18 | Create 19 | Update 20 | Delete 21 | ) */ 22 | type ChangeType int 23 | -------------------------------------------------------------------------------- /generator/example_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | // X is doc'ed 4 | type X struct { 5 | } 6 | 7 | // Color is an enumeration of colors that are allowed. 8 | // ENUM( 9 | // Black, White, Red 10 | // Green 11 | // Blue=33 12 | // grey= 13 | // yellow 14 | // ). 15 | type Color int 16 | 17 | // Animal x ENUM( 18 | // Cat, 19 | // Dog, 20 | // Fish 21 | // ) Some other line of info 22 | type Animal int32 23 | 24 | // Model x ENUM(Toyota,_,Chevy,_,Ford). 25 | type Model int32 26 | 27 | /* 28 | ENUM( 29 | Coke 30 | Pepsi 31 | MtnDew 32 | 33 | ). 34 | */ 35 | type Soda int64 36 | 37 | /* 38 | ENUM( 39 | test_lower 40 | Test_capital 41 | anotherLowerCaseStart 42 | 43 | ) 44 | */ 45 | type Cases int64 46 | 47 | /* 48 | ENUM( 49 | test-Hyphen 50 | -hyphenStart 51 | _underscoreFirst 52 | 0numberFirst 53 | 123456789a 54 | 123123-asdf 55 | ending-hyphen- 56 | 57 | ) 58 | */ 59 | type Sanitizing int64 60 | 61 | /* 62 | ENUM( 63 | startWithNum=23 64 | nextNum 65 | 66 | ) 67 | */ 68 | type StartNotZero int64 69 | 70 | // ENUM( 71 | // Black, White, Red 72 | // Green 73 | // Blue=33 // Blue starts with 33. 74 | // grey= 75 | // yellow 76 | // ) 77 | type ColorWithComment int 78 | 79 | /* 80 | ENUM( 81 | Black, White, Red 82 | Green 83 | Blue=33 // Blue starts with 33 84 | grey= 85 | yellow 86 | ) 87 | */ 88 | type ColorWithComment2 int 89 | 90 | /* ENUM( 91 | Black, White, Red 92 | Green = 33 // Green starts with 33 93 | */ 94 | // Blue 95 | // grey= 96 | // yellow 97 | // blue-green // blue-green comment 98 | // red-orange 99 | // red-orange-blue 100 | // ) 101 | type ColorWithComment3 int 102 | 103 | /* ENUM( 104 | _, // Placeholder 105 | Black, White, Red 106 | Green = 33 // Green starts with 33 107 | */ 108 | // Blue 109 | // grey= 110 | // yellow // Where did all the (somewhat) bad fish go? (something else that goes in parentheses at the end of the line) 111 | // blue-green // blue-green comment 112 | // red-orange // has a , in it!?! 113 | // ) 114 | type ColorWithComment4 int 115 | 116 | /* 117 | ENUM( 118 | 119 | Unknown= 0 120 | E2P15 = 32768 121 | E2P16 = 65536 122 | E2P17 = 131072 123 | E2P18 = 262144 124 | E2P19 = 524288 125 | E2P20 = 1048576 126 | E2P21 = 2097152 127 | E2P22 = 33554432 128 | E2P23 = 67108864 129 | E2P28 = 536870912 130 | E2P30 = 1073741824 131 | E2P31 = 2147483648 132 | E2P32 = 4294967296 133 | E2P33 = 8454967296 134 | ) 135 | */ 136 | type Enum64bit uint64 137 | 138 | // NonASCII 139 | // ENUM( 140 | // Продам = 1114 141 | // 車庫 = 300 142 | // էժան = 1 143 | // ) 144 | type NonASCII int 145 | 146 | // StringEnum. 147 | // ENUM( 148 | // random = 1114 149 | // values = 300 150 | // here = 1 151 | // ) 152 | type StringEnum string 153 | -------------------------------------------------------------------------------- /generator/generator_1.18_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package generator 5 | 6 | import ( 7 | "fmt" 8 | "go/parser" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/bradleyjkemp/cupaloy/v2" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | var testExampleFiles = map[string]string{ 18 | "og": `example_test.go`, 19 | "1.18": `example_1.18_test.go`, 20 | } 21 | 22 | // TestNoStructInputFile 23 | func Test118NoStructFile(t *testing.T) { 24 | input := `package test 25 | // Behavior 26 | type SomeInterface interface{ 27 | 28 | } 29 | ` 30 | g := NewGenerator(). 31 | WithoutSnakeToCamel() 32 | f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) 33 | assert.Nil(t, err, "Error parsing no struct input") 34 | 35 | output, err := g.Generate(f) 36 | assert.Nil(t, err, "Error generating formatted code") 37 | if false { // Debugging statement 38 | fmt.Println(string(output)) 39 | } 40 | } 41 | 42 | // TestNoFile 43 | func Test118NoFile(t *testing.T) { 44 | g := NewGenerator(). 45 | WithoutSnakeToCamel() 46 | // Parse the file given in arguments 47 | _, err := g.GenerateFromFile("") 48 | assert.NotNil(t, err, "Error generating formatted code") 49 | } 50 | 51 | // TestExampleFile 52 | func Test118ExampleFile(t *testing.T) { 53 | g := NewGenerator(). 54 | WithMarshal(). 55 | WithSQLDriver(). 56 | WithCaseInsensitiveParse(). 57 | WithNames(). 58 | WithoutSnakeToCamel() 59 | 60 | for name, testExample := range testExampleFiles { 61 | t.Run(name, func(t *testing.T) { 62 | // Parse the file given in arguments 63 | imported, err := g.GenerateFromFile(testExample) 64 | require.Nil(t, err, "Error generating formatted code") 65 | 66 | outputLines := strings.Split(string(imported), "\n") 67 | cupaloy.SnapshotT(t, outputLines) 68 | 69 | if false { 70 | fmt.Println(string(imported)) 71 | } 72 | }) 73 | } 74 | } 75 | 76 | // TestExampleFileMoreOptions 77 | func Test118ExampleFileMoreOptions(t *testing.T) { 78 | g := NewGenerator(). 79 | WithMarshal(). 80 | WithSQLDriver(). 81 | WithCaseInsensitiveParse(). 82 | WithNames(). 83 | WithoutSnakeToCamel(). 84 | WithMustParse(). 85 | WithForceLower(). 86 | WithTemplates(`../example/user_template.tmpl`) 87 | for name, testExample := range testExampleFiles { 88 | t.Run(name, func(t *testing.T) { 89 | // Parse the file given in arguments 90 | imported, err := g.GenerateFromFile(testExample) 91 | require.Nil(t, err, "Error generating formatted code") 92 | 93 | outputLines := strings.Split(string(imported), "\n") 94 | cupaloy.SnapshotT(t, outputLines) 95 | 96 | if false { 97 | fmt.Println(string(imported)) 98 | } 99 | }) 100 | } 101 | } 102 | 103 | // TestExampleFile 104 | func Test118NoPrefixExampleFile(t *testing.T) { 105 | g := NewGenerator(). 106 | WithMarshal(). 107 | WithLowercaseVariant(). 108 | WithNoPrefix(). 109 | WithFlag(). 110 | WithoutSnakeToCamel() 111 | for name, testExample := range testExampleFiles { 112 | t.Run(name, func(t *testing.T) { 113 | // Parse the file given in arguments 114 | imported, err := g.GenerateFromFile(testExample) 115 | require.Nil(t, err, "Error generating formatted code") 116 | 117 | outputLines := strings.Split(string(imported), "\n") 118 | cupaloy.SnapshotT(t, outputLines) 119 | 120 | if false { 121 | fmt.Println(string(imported)) 122 | } 123 | }) 124 | } 125 | } 126 | 127 | // TestExampleFile 128 | func Test118NoPrefixExampleFileWithSnakeToCamel(t *testing.T) { 129 | g := NewGenerator(). 130 | WithMarshal(). 131 | WithLowercaseVariant(). 132 | WithNoPrefix(). 133 | WithFlag() 134 | 135 | for name, testExample := range testExampleFiles { 136 | t.Run(name, func(t *testing.T) { 137 | // Parse the file given in arguments 138 | imported, err := g.GenerateFromFile(testExample) 139 | require.Nil(t, err, "Error generating formatted code") 140 | 141 | outputLines := strings.Split(string(imported), "\n") 142 | cupaloy.SnapshotT(t, outputLines) 143 | 144 | if false { 145 | fmt.Println(string(imported)) 146 | } 147 | }) 148 | } 149 | } 150 | 151 | // TestCustomPrefixExampleFile 152 | func Test118CustomPrefixExampleFile(t *testing.T) { 153 | g := NewGenerator(). 154 | WithMarshal(). 155 | WithLowercaseVariant(). 156 | WithNoPrefix(). 157 | WithFlag(). 158 | WithoutSnakeToCamel(). 159 | WithPtr(). 160 | WithSQLNullInt(). 161 | WithSQLNullStr(). 162 | WithPrefix("Custom_prefix_") 163 | for name, testExample := range testExampleFiles { 164 | t.Run(name, func(t *testing.T) { 165 | // Parse the file given in arguments 166 | imported, err := g.GenerateFromFile(testExample) 167 | require.Nil(t, err, "Error generating formatted code") 168 | 169 | outputLines := strings.Split(string(imported), "\n") 170 | cupaloy.SnapshotT(t, outputLines) 171 | 172 | if false { 173 | fmt.Println(string(imported)) 174 | } 175 | }) 176 | } 177 | } 178 | 179 | func Test118AliasParsing(t *testing.T) { 180 | tests := map[string]struct { 181 | input []string 182 | resultingMap map[string]string 183 | err error 184 | }{ 185 | "no aliases": { 186 | resultingMap: map[string]string{}, 187 | }, 188 | "multiple arrays": { 189 | input: []string{ 190 | `!:Bang,a:a`, 191 | `@:AT`, 192 | `&:AND,|:OR`, 193 | }, 194 | resultingMap: map[string]string{ 195 | "a": "a", 196 | "!": "Bang", 197 | "@": "AT", 198 | "&": "AND", 199 | "|": "OR", 200 | }, 201 | }, 202 | "more types": { 203 | input: []string{ 204 | `*:star,+:PLUS`, 205 | `-:less`, 206 | `#:HASH,!:Bang`, 207 | }, 208 | resultingMap: map[string]string{ 209 | "*": "star", 210 | "+": "PLUS", 211 | "-": "less", 212 | "#": "HASH", 213 | "!": "Bang", 214 | }, 215 | }, 216 | } 217 | 218 | for name, tc := range tests { 219 | t.Run(name, func(t *testing.T) { 220 | replacementNames, err := ParseAliases(tc.input) 221 | if tc.err != nil { 222 | require.Error(t, err) 223 | require.EqualError(t, err, tc.err.Error()) 224 | } else { 225 | require.NoError(t, err) 226 | require.Equal(t, tc.resultingMap, replacementNames) 227 | } 228 | }) 229 | } 230 | } 231 | 232 | // TestEnumParseFailure 233 | func Test118EnumParseFailure(t *testing.T) { 234 | input := `package test 235 | // Behavior 236 | type SomeInterface interface{ 237 | 238 | } 239 | 240 | // ENUM( 241 | // a, 242 | //} 243 | type Animal int 244 | ` 245 | g := NewGenerator(). 246 | WithoutSnakeToCamel() 247 | f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) 248 | assert.Nil(t, err, "Error parsing no struct input") 249 | 250 | output, err := g.Generate(f) 251 | assert.Nil(t, err, "Error generating formatted code") 252 | assert.Empty(t, string(output)) 253 | if false { // Debugging statement 254 | fmt.Println(string(output)) 255 | } 256 | } 257 | 258 | // TestUintInvalidParsing 259 | func Test118UintInvalidParsing(t *testing.T) { 260 | input := `package test 261 | // ENUM( 262 | // a=-1, 263 | //) 264 | type Animal uint 265 | ` 266 | g := NewGenerator(). 267 | WithoutSnakeToCamel() 268 | f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) 269 | assert.Nil(t, err, "Error parsing no struct input") 270 | 271 | output, err := g.Generate(f) 272 | assert.Nil(t, err, "Error generating formatted code") 273 | assert.Empty(t, string(output)) 274 | if false { // Debugging statement 275 | fmt.Println(string(output)) 276 | } 277 | } 278 | 279 | // TestIntInvalidParsing 280 | func Test118IntInvalidParsing(t *testing.T) { 281 | input := `package test 282 | // ENUM( 283 | // a=c, 284 | //) 285 | type Animal int 286 | ` 287 | g := NewGenerator(). 288 | WithoutSnakeToCamel() 289 | f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) 290 | assert.Nil(t, err, "Error parsing no struct input") 291 | 292 | output, err := g.Generate(f) 293 | assert.Nil(t, err, "Error generating formatted code") 294 | assert.Empty(t, string(output)) 295 | if false { // Debugging statement 296 | fmt.Println(string(output)) 297 | } 298 | } 299 | 300 | // TestAliasing 301 | func Test118Aliasing(t *testing.T) { 302 | input := `package test 303 | // ENUM(a,b,CDEF) with some extra text 304 | type Animal int 305 | ` 306 | aliases, err := ParseAliases([]string{"CDEF:C"}) 307 | require.NoError(t, err) 308 | g := NewGenerator(). 309 | WithoutSnakeToCamel(). 310 | WithAliases(aliases) 311 | f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) 312 | assert.Nil(t, err, "Error parsing no struct input") 313 | 314 | output, err := g.Generate(f) 315 | assert.Nil(t, err, "Error generating formatted code") 316 | assert.Contains(t, string(output), "// AnimalC is a Animal of type CDEF.") 317 | if false { // Debugging statement 318 | fmt.Println(string(output)) 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /generator/template_funcs.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Stringify returns a string that is all of the enum value names concatenated without a separator 10 | func Stringify(e Enum, forceLower, forceUpper bool) (ret string, err error) { 11 | for _, val := range e.Values { 12 | if val.Name != skipHolder { 13 | next := val.RawName 14 | if forceLower { 15 | next = strings.ToLower(next) 16 | } 17 | if forceUpper { 18 | next = strings.ToUpper(next) 19 | } 20 | ret = ret + next 21 | } 22 | } 23 | return 24 | } 25 | 26 | // Mapify returns a map that is all of the indexes for a string value lookup 27 | func Mapify(e Enum) (ret string, err error) { 28 | strName := fmt.Sprintf(`_%sName`, e.Name) 29 | ret = fmt.Sprintf("map[%s]string{\n", e.Name) 30 | index := 0 31 | for _, val := range e.Values { 32 | if val.Name != skipHolder { 33 | nextIndex := index + len(val.Name) 34 | ret = fmt.Sprintf("%s%s: %s[%d:%d],\n", ret, val.PrefixedName, strName, index, nextIndex) 35 | index = nextIndex 36 | } 37 | } 38 | ret = ret + `}` 39 | return 40 | } 41 | 42 | // Unmapify returns a map that is all of the indexes for a string value lookup 43 | func Unmapify(e Enum, lowercase bool) (ret string, err error) { 44 | if e.Type == "string" { 45 | return UnmapifyStringEnum(e, lowercase) 46 | } 47 | strName := fmt.Sprintf(`_%sName`, e.Name) 48 | ret = fmt.Sprintf("map[string]%s{\n", e.Name) 49 | index := 0 50 | for _, val := range e.Values { 51 | if val.Name != skipHolder { 52 | nextIndex := index + len(val.Name) 53 | ret = fmt.Sprintf("%s%s[%d:%d]: %s,\n", ret, strName, index, nextIndex, val.PrefixedName) 54 | if lowercase { 55 | ret = fmt.Sprintf("%sstrings.ToLower(%s[%d:%d]): %s,\n", ret, strName, index, nextIndex, val.PrefixedName) 56 | } 57 | index = nextIndex 58 | } 59 | } 60 | ret = ret + `}` 61 | return 62 | } 63 | 64 | // Unmapify returns a map that is all of the indexes for a string value lookup 65 | func UnmapifyStringEnum(e Enum, lowercase bool) (ret string, err error) { 66 | var builder strings.Builder 67 | _, err = builder.WriteString("map[string]" + e.Name + "{\n") 68 | if err != nil { 69 | return 70 | } 71 | for _, val := range e.Values { 72 | if val.Name != skipHolder { 73 | _, err = builder.WriteString(fmt.Sprintf("%q:%s,\n", val.ValueStr, val.PrefixedName)) 74 | if err != nil { 75 | return 76 | } 77 | if lowercase && strings.ToLower(val.ValueStr) != val.ValueStr { 78 | _, err = builder.WriteString(fmt.Sprintf("%q:%s,\n", strings.ToLower(val.ValueStr), val.PrefixedName)) 79 | if err != nil { 80 | return 81 | } 82 | } 83 | } 84 | } 85 | builder.WriteByte('}') 86 | ret = builder.String() 87 | return 88 | } 89 | 90 | // Namify returns a slice that is all of the possible names for an enum in a slice 91 | func Namify(e Enum) (ret string, err error) { 92 | if e.Type == "string" { 93 | return namifyStringEnum(e) 94 | } 95 | strName := fmt.Sprintf(`_%sName`, e.Name) 96 | ret = "[]string{\n" 97 | index := 0 98 | for _, val := range e.Values { 99 | if val.Name != skipHolder { 100 | nextIndex := index + len(val.Name) 101 | ret = fmt.Sprintf("%s%s[%d:%d],\n", ret, strName, index, nextIndex) 102 | index = nextIndex 103 | } 104 | } 105 | ret = ret + "}" 106 | return 107 | } 108 | 109 | // Namify returns a slice that is all of the possible names for an enum in a slice 110 | func namifyStringEnum(e Enum) (ret string, err error) { 111 | ret = "[]string{\n" 112 | for _, val := range e.Values { 113 | if val.Name != skipHolder { 114 | ret = fmt.Sprintf("%sstring(%s),\n", ret, val.PrefixedName) 115 | } 116 | } 117 | ret = ret + "}" 118 | return 119 | } 120 | 121 | func Offset(index int, enumType string, val EnumValue) (strResult string) { 122 | if strings.HasPrefix(enumType, "u") { 123 | // Unsigned 124 | return strconv.FormatUint(val.ValueInt.(uint64)-uint64(index), 10) 125 | } else { 126 | // Signed 127 | return strconv.FormatInt(val.ValueInt.(int64)-int64(index), 10) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/abice/go-enum 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/Masterminds/sprig/v3 v3.2.3 7 | github.com/bradleyjkemp/cupaloy/v2 v2.8.0 8 | github.com/golang/mock v1.6.0 9 | github.com/labstack/gommon v0.4.2 10 | github.com/mattn/goveralls v0.0.12 11 | github.com/spf13/pflag v1.0.5 12 | github.com/stretchr/testify v1.9.0 13 | github.com/urfave/cli/v2 v2.27.2 14 | golang.org/x/text v0.16.0 15 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d 16 | golang.org/x/tools/cmd/cover v0.1.0-deprecated 17 | ) 18 | 19 | require ( 20 | github.com/Masterminds/goutils v1.1.1 // indirect 21 | github.com/Masterminds/semver/v3 v3.2.0 // indirect 22 | github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect 23 | github.com/davecgh/go-spew v1.1.1 // indirect 24 | github.com/google/uuid v1.3.0 // indirect 25 | github.com/huandu/xstrings v1.3.3 // indirect 26 | github.com/imdario/mergo v0.3.13 // indirect 27 | github.com/kr/pretty v0.3.1 // indirect 28 | github.com/mattn/go-colorable v0.1.13 // indirect 29 | github.com/mattn/go-isatty v0.0.20 // indirect 30 | github.com/mitchellh/copystructure v1.2.0 // indirect 31 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 32 | github.com/pmezard/go-difflib v1.0.0 // indirect 33 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 34 | github.com/shopspring/decimal v1.2.0 // indirect 35 | github.com/spf13/cast v1.3.1 // indirect 36 | github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect 37 | golang.org/x/crypto v0.17.0 // indirect 38 | golang.org/x/mod v0.17.0 // indirect 39 | golang.org/x/sys v0.20.0 // indirect 40 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 41 | gopkg.in/yaml.v3 v3.0.1 // indirect 42 | ) 43 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/abice/go-enum/generator" 11 | "github.com/labstack/gommon/color" 12 | "github.com/urfave/cli/v2" 13 | ) 14 | 15 | var ( 16 | version string 17 | commit string 18 | date string 19 | builtBy string 20 | ) 21 | 22 | type rootT struct { 23 | FileNames cli.StringSlice 24 | NoPrefix bool 25 | Lowercase bool 26 | NoCase bool 27 | Marshal bool 28 | SQL bool 29 | SQLInt bool 30 | Flag bool 31 | Prefix string 32 | Names bool 33 | Values bool 34 | LeaveSnakeCase bool 35 | SQLNullStr bool 36 | SQLNullInt bool 37 | Ptr bool 38 | TemplateFileNames cli.StringSlice 39 | Aliases cli.StringSlice 40 | BuildTags cli.StringSlice 41 | MustParse bool 42 | ForceLower bool 43 | ForceUpper bool 44 | NoComments bool 45 | OutputSuffix string 46 | } 47 | 48 | func main() { 49 | var argv rootT 50 | 51 | clr := color.New() 52 | out := func(format string, args ...interface{}) { 53 | _, _ = fmt.Fprintf(clr.Output(), format, args...) 54 | } 55 | 56 | app := &cli.App{ 57 | Name: "go-enum", 58 | Usage: "An enum generator for go", 59 | HideHelpCommand: true, 60 | Version: version, 61 | Flags: []cli.Flag{ 62 | &cli.StringSliceFlag{ 63 | Name: "file", 64 | Aliases: []string{"f"}, 65 | EnvVars: []string{"GOFILE"}, 66 | Usage: "The file(s) to generate enums. Use more than one flag for more files.", 67 | Required: true, 68 | Destination: &argv.FileNames, 69 | }, 70 | &cli.BoolFlag{ 71 | Name: "noprefix", 72 | Usage: "Prevents the constants generated from having the Enum as a prefix.", 73 | Destination: &argv.NoPrefix, 74 | }, 75 | &cli.BoolFlag{ 76 | Name: "lower", 77 | Usage: "Adds lowercase variants of the enum strings for lookup.", 78 | Destination: &argv.Lowercase, 79 | }, 80 | &cli.BoolFlag{ 81 | Name: "nocase", 82 | Usage: "Adds case insensitive parsing to the enumeration (forces lower flag).", 83 | Destination: &argv.NoCase, 84 | }, 85 | &cli.BoolFlag{ 86 | Name: "marshal", 87 | Usage: "Adds text (and inherently json) marshalling functions.", 88 | Destination: &argv.Marshal, 89 | }, 90 | &cli.BoolFlag{ 91 | Name: "sql", 92 | Usage: "Adds SQL database scan and value functions.", 93 | Destination: &argv.SQL, 94 | }, 95 | &cli.BoolFlag{ 96 | Name: "sqlint", 97 | Usage: "Tells the generator that a string typed enum should be stored in sql as an integer value.", 98 | Destination: &argv.SQLInt, 99 | }, 100 | &cli.BoolFlag{ 101 | Name: "flag", 102 | Usage: "Adds golang flag functions.", 103 | Destination: &argv.Flag, 104 | }, 105 | &cli.StringFlag{ 106 | Name: "prefix", 107 | Usage: "Adds a prefix with a user one. If you would like to replace the prefix, then combine this option with --noprefix.", 108 | Destination: &argv.Prefix, 109 | }, 110 | &cli.BoolFlag{ 111 | Name: "names", 112 | Usage: "Generates a 'Names() []string' function, and adds the possible enum values in the error response during parsing", 113 | Destination: &argv.Names, 114 | }, 115 | &cli.BoolFlag{ 116 | Name: "values", 117 | Usage: "Generates a 'Values() []{{ENUM}}' function.", 118 | Destination: &argv.Values, 119 | }, 120 | &cli.BoolFlag{ 121 | Name: "nocamel", 122 | Usage: "Removes the snake_case to CamelCase name changing", 123 | Destination: &argv.LeaveSnakeCase, 124 | }, 125 | &cli.BoolFlag{ 126 | Name: "ptr", 127 | Usage: "Adds a pointer method to get a pointer from const values", 128 | Destination: &argv.Ptr, 129 | }, 130 | &cli.BoolFlag{ 131 | Name: "sqlnullint", 132 | Usage: "Adds a Null{{ENUM}} type for marshalling a nullable int value to sql", 133 | Destination: &argv.SQLNullInt, 134 | }, 135 | &cli.BoolFlag{ 136 | Name: "sqlnullstr", 137 | Usage: "Adds a Null{{ENUM}} type for marshalling a nullable string value to sql. If sqlnullint is specified too, it will be Null{{ENUM}}Str", 138 | Destination: &argv.SQLNullStr, 139 | }, 140 | &cli.StringSliceFlag{ 141 | Name: "template", 142 | Aliases: []string{"t"}, 143 | Usage: "Additional template file(s) to generate enums. Use more than one flag for more files. Templates will be executed in alphabetical order.", 144 | Destination: &argv.TemplateFileNames, 145 | }, 146 | &cli.StringSliceFlag{ 147 | Name: "alias", 148 | Aliases: []string{"a"}, 149 | Usage: "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!]", 150 | Destination: &argv.Aliases, 151 | }, 152 | &cli.BoolFlag{ 153 | Name: "mustparse", 154 | Usage: "Adds a Must version of the Parse that will panic on failure.", 155 | Destination: &argv.MustParse, 156 | }, 157 | &cli.BoolFlag{ 158 | Name: "forcelower", 159 | Usage: "Forces a camel cased comment to generate lowercased names.", 160 | Destination: &argv.ForceLower, 161 | }, 162 | &cli.BoolFlag{ 163 | Name: "forceupper", 164 | Usage: "Forces a camel cased comment to generate uppercased names.", 165 | Destination: &argv.ForceUpper, 166 | }, 167 | &cli.BoolFlag{ 168 | Name: "nocomments", 169 | Usage: "Removes auto generated comments. If you add your own comments, these will still be created.", 170 | Destination: &argv.NoComments, 171 | }, 172 | &cli.StringSliceFlag{ 173 | Name: "buildtag", 174 | Aliases: []string{"b"}, 175 | Usage: "Adds build tags to a generated enum file.", 176 | Destination: &argv.BuildTags, 177 | }, 178 | &cli.StringFlag{ 179 | Name: "output-suffix", 180 | Usage: "Changes the default filename suffix of _enum to something else. `.go` will be appended to the end of the string no matter what, so that `_test.go` cases can be accommodated ", 181 | Destination: &argv.OutputSuffix, 182 | }, 183 | }, 184 | Action: func(ctx *cli.Context) error { 185 | aliases, err := generator.ParseAliases(argv.Aliases.Value()) 186 | if err != nil { 187 | return err 188 | } 189 | for _, fileOption := range argv.FileNames.Value() { 190 | 191 | g := generator.NewGenerator() 192 | g.Version = version 193 | g.Revision = commit 194 | g.BuildDate = date 195 | g.BuiltBy = builtBy 196 | 197 | g.WithBuildTags(argv.BuildTags.Value()...) 198 | g.WithAliases(aliases) 199 | 200 | if argv.NoPrefix { 201 | g.WithNoPrefix() 202 | } 203 | if argv.Lowercase { 204 | g.WithLowercaseVariant() 205 | } 206 | if argv.NoCase { 207 | g.WithCaseInsensitiveParse() 208 | } 209 | if argv.Marshal { 210 | g.WithMarshal() 211 | } 212 | if argv.SQL { 213 | g.WithSQLDriver() 214 | } 215 | if argv.SQLInt { 216 | g.WithSQLInt() 217 | } 218 | if argv.Flag { 219 | g.WithFlag() 220 | } 221 | if argv.Names { 222 | g.WithNames() 223 | } 224 | if argv.Values { 225 | g.WithValues() 226 | } 227 | if argv.LeaveSnakeCase { 228 | g.WithoutSnakeToCamel() 229 | } 230 | if argv.Prefix != "" { 231 | g.WithPrefix(argv.Prefix) 232 | } 233 | if argv.Ptr { 234 | g.WithPtr() 235 | } 236 | if argv.SQLNullInt { 237 | g.WithSQLNullInt() 238 | } 239 | if argv.SQLNullStr { 240 | g.WithSQLNullStr() 241 | } 242 | if argv.MustParse { 243 | g.WithMustParse() 244 | } 245 | if argv.ForceLower { 246 | g.WithForceLower() 247 | } 248 | if argv.ForceUpper { 249 | g.WithForceUpper() 250 | } 251 | if argv.NoComments { 252 | g.WithNoComments() 253 | } 254 | if templates := []string(argv.TemplateFileNames.Value()); len(templates) > 0 { 255 | for _, t := range templates { 256 | if fn, err := globFilenames(t); err != nil { 257 | return err 258 | } else { 259 | g.WithTemplates(fn...) 260 | } 261 | } 262 | } 263 | 264 | var filenames []string 265 | if fn, err := globFilenames(fileOption); err != nil { 266 | return err 267 | } else { 268 | filenames = fn 269 | } 270 | 271 | outputSuffix := `_enum` 272 | if argv.OutputSuffix != "" { 273 | outputSuffix = argv.OutputSuffix 274 | } 275 | 276 | for _, fileName := range filenames { 277 | originalName := fileName 278 | 279 | out("go-enum started. file: %s\n", color.Cyan(originalName)) 280 | fileName, _ = filepath.Abs(fileName) 281 | 282 | outFilePath := fmt.Sprintf("%s%s.go", strings.TrimSuffix(fileName, filepath.Ext(fileName)), outputSuffix) 283 | if strings.HasSuffix(fileName, "_test.go") { 284 | outFilePath = strings.Replace(outFilePath, "_test"+outputSuffix+".go", outputSuffix+"_test.go", 1) 285 | } 286 | 287 | // Parse the file given in arguments 288 | raw, err := g.GenerateFromFile(fileName) 289 | if err != nil { 290 | return fmt.Errorf("failed generating enums\nInputFile=%s\nError=%s", color.Cyan(fileName), color.RedBg(err)) 291 | } 292 | 293 | // Nothing was generated, ignore the output and don't create a file. 294 | if len(raw) < 1 { 295 | out(color.Yellow("go-enum ignored. file: %s\n"), color.Cyan(originalName)) 296 | continue 297 | } 298 | 299 | mode := int(0o644) 300 | err = os.WriteFile(outFilePath, raw, os.FileMode(mode)) 301 | if err != nil { 302 | return fmt.Errorf("failed writing to file %s: %s", color.Cyan(outFilePath), color.Red(err)) 303 | } 304 | out("go-enum finished. file: %s\n", color.Cyan(originalName)) 305 | } 306 | } 307 | 308 | return nil 309 | }, 310 | } 311 | 312 | if err := app.Run(os.Args); err != nil { 313 | log.Fatal(err) 314 | } 315 | } 316 | 317 | // globFilenames gets a list of filenames matching the provided filename. 318 | // In order to maintain existing capabilities, only glob when a * is in the path. 319 | // Leave execution on par with old method in case there are bad patterns in use that somehow 320 | // work without the Glob method. 321 | func globFilenames(filename string) ([]string, error) { 322 | if strings.Contains(filename, "*") { 323 | matches, err := filepath.Glob(filename) 324 | if err != nil { 325 | return []string{}, fmt.Errorf("failed parsing glob filepath\nInputFile=%s\nError=%s", color.Cyan(filename), color.RedBg(err)) 326 | } 327 | return matches, nil 328 | } else { 329 | return []string{filename}, nil 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package main 5 | 6 | import ( 7 | _ "github.com/golang/mock/mockgen" 8 | _ "github.com/golang/mock/mockgen/model" 9 | _ "github.com/mattn/goveralls" 10 | _ "golang.org/x/tools/cmd/cover" 11 | _ "golang.org/x/tools/cmd/goimports" 12 | ) 13 | -------------------------------------------------------------------------------- /update-snapshots.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | UPDATE_SNAPSHOTS=true make test --------------------------------------------------------------------------------