feat: add gen enum command
This commit is contained in:
@@ -11,6 +11,7 @@ func CommandGen(root *cobra.Command) {
|
|||||||
|
|
||||||
cmds := []func(*cobra.Command){
|
cmds := []func(*cobra.Command){
|
||||||
CommandGenModel,
|
CommandGenModel,
|
||||||
|
CommandGenEnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cmds {
|
for _, c := range cmds {
|
||||||
|
|||||||
110
cmd/gen_enum.go
Normal file
110
cmd/gen_enum.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.ipao.vip/rogeecn/atomctl/pkg/utils"
|
||||||
|
"git.ipao.vip/rogeecn/atomctl/pkg/utils/generator"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CommandGenEnum(root *cobra.Command) {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "enum",
|
||||||
|
Short: "Generate enums",
|
||||||
|
RunE: commandGenEnumE,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().BoolP("flag", "f", true, "Flag enum values")
|
||||||
|
cmd.Flags().BoolP("marshal", "m", false, "Marshal enum values")
|
||||||
|
cmd.Flags().BoolP("sql", "s", true, "SQL driver enum values")
|
||||||
|
|
||||||
|
root.AddCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandGenEnumE(cmd *cobra.Command, args []string) error {
|
||||||
|
var filenames []string
|
||||||
|
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = filepath.Walk(pwd, func(path string, info fs.FileInfo, err error) error {
|
||||||
|
if utils.IsDir(path) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(path, ".go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(string(content), "ENUM(") && strings.Contains(string(content), "swagger:enum") {
|
||||||
|
filenames = append(filenames, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filenames) == 0 {
|
||||||
|
return fmt.Errorf("no enum files found in %s", pwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
g := generator.NewGenerator()
|
||||||
|
|
||||||
|
if marshal, _ := cmd.Flags().GetBool("marshal"); marshal {
|
||||||
|
g.WithMarshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag, _ := cmd.Flags().GetBool("flag"); flag {
|
||||||
|
g.WithFlag()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sql, _ := cmd.Flags().GetBool("sql"); sql {
|
||||||
|
g.WithSQLDriver()
|
||||||
|
g.WithSQLInt()
|
||||||
|
g.WithSQLNullInt()
|
||||||
|
g.WithSQLNullStr()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.WithNames()
|
||||||
|
g.WithValues()
|
||||||
|
|
||||||
|
for _, fileName := range filenames {
|
||||||
|
log.Printf("Generating enums for %s", fileName)
|
||||||
|
|
||||||
|
fileName, _ = filepath.Abs(fileName)
|
||||||
|
outFilePath := fmt.Sprintf("%s.gen.go", strings.TrimSuffix(fileName, filepath.Ext(fileName)))
|
||||||
|
|
||||||
|
// Parse the file given in arguments
|
||||||
|
raw, err := g.GenerateFromFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed generating enums\nInputFile=%s\nError=%s", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing was generated, ignore the output and don't create a file.
|
||||||
|
if len(raw) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := int(0o644)
|
||||||
|
err = os.WriteFile(outFilePath, raw, os.FileMode(mode))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed writing to file %s: %s", outFilePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
go.mod
17
go.mod
@@ -3,6 +3,8 @@ module git.ipao.vip/rogeecn/atomctl
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0
|
||||||
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
|
||||||
github.com/go-jet/jet/v2 v2.12.0
|
github.com/go-jet/jet/v2 v2.12.0
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
@@ -11,13 +13,20 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
|
golang.org/x/text v0.21.0
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
dario.cat/mergo v1.0.1 // indirect
|
||||||
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
github.com/jackc/pgconn v1.14.3 // indirect
|
github.com/jackc/pgconn v1.14.3 // indirect
|
||||||
@@ -28,24 +37,26 @@ require (
|
|||||||
github.com/jackc/pgtype v1.14.4 // indirect
|
github.com/jackc/pgtype v1.14.4 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||||
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
27
go.sum
27
go.sum
@@ -1,5 +1,15 @@
|
|||||||
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||||
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||||
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
@@ -31,6 +41,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
|
|||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
|
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
@@ -112,8 +124,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
@@ -146,6 +162,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah
|
|||||||
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
@@ -154,8 +172,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
|||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@@ -172,6 +190,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
@@ -217,6 +236,8 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
|
|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -279,6 +300,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
23
pkg/utils/fs.go
Normal file
23
pkg/utils/fs.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsDir(dir string) bool {
|
||||||
|
f, err := os.Stat(dir)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFile(path string) bool {
|
||||||
|
f, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Mode().IsRegular()
|
||||||
|
}
|
||||||
1
pkg/utils/generate.go
Normal file
1
pkg/utils/generate.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package utils
|
||||||
16
pkg/utils/generator/embedded_1.16.go
Normal file
16
pkg/utils/generator/embedded_1.16.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed enum.tmpl enum_string.tmpl
|
||||||
|
var content embed.FS
|
||||||
|
|
||||||
|
func (g *Generator) addEmbeddedTemplates() {
|
||||||
|
g.t = template.Must(g.t.ParseFS(content, "*.tmpl"))
|
||||||
|
}
|
||||||
367
pkg/utils/generator/enum.tmpl
Normal file
367
pkg/utils/generator/enum.tmpl
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
{{- define "header"}}
|
||||||
|
// Code generated by go-enum DO NOT EDIT.
|
||||||
|
// Version: {{ .version }}
|
||||||
|
// Revision: {{ .revision }}
|
||||||
|
// Build Date: {{ .buildDate }}
|
||||||
|
// Built By: {{ .builtBy }}
|
||||||
|
{{ range $idx, $tag := .buildTags }}
|
||||||
|
//go:build {{$tag}}
|
||||||
|
// +build {{$tag}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
package {{.package}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
{{end -}}
|
||||||
|
|
||||||
|
{{- define "enum"}}
|
||||||
|
const (
|
||||||
|
{{- $enumName := .enum.Name -}}
|
||||||
|
{{- $enumType := .enum.Type -}}
|
||||||
|
{{- $noComments := .nocomments -}}
|
||||||
|
{{- $vars := dict "lastoffset" "0" -}}
|
||||||
|
{{ range $rIndex, $value := .enum.Values }}
|
||||||
|
{{- $lastOffset := pluck "lastoffset" $vars | first }}{{ $offset := offset $rIndex $enumType $value }}
|
||||||
|
{{- if $noComments }}{{else}}
|
||||||
|
{{ if eq $value.Name "_"}}// Skipped value.{{else}}// {{$value.PrefixedName}} is a {{$enumName}} of type {{$value.Name}}.{{end}}{{end}}
|
||||||
|
{{- if $value.Comment}}
|
||||||
|
// {{$value.Comment}}
|
||||||
|
{{- end}}
|
||||||
|
{{$value.PrefixedName}} {{ if eq $rIndex 0 }}{{$enumName}} = iota{{ if ne "0" $offset }} + {{ $offset }}{{end}}{{else if ne $lastOffset $offset }}{{$enumName}} = iota + {{ $offset }}{{end}}{{$_ := set $vars "lastoffset" $offset}}
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
{{if .names -}}
|
||||||
|
var ErrInvalid{{.enum.Name}} = fmt.Errorf("not a valid {{.enum.Name}}, try [%s]", strings.Join(_{{.enum.Name}}Names, ", "))
|
||||||
|
{{- else -}}
|
||||||
|
var ErrInvalid{{.enum.Name}} = errors.New("not a valid {{.enum.Name}}")
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{ template "stringer" . }}
|
||||||
|
|
||||||
|
var _{{.enum.Name}}Map = {{ mapify .enum }}
|
||||||
|
|
||||||
|
// String implements the Stringer interface.
|
||||||
|
func (x {{.enum.Name}}) String() string {
|
||||||
|
if str, ok := _{{.enum.Name}}Map[x]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("{{.enum.Name}}(%d)", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid provides a quick way to determine if the typed value is
|
||||||
|
// part of the allowed enumerated values
|
||||||
|
func (x {{.enum.Name}}) IsValid() bool {
|
||||||
|
_, ok := _{{.enum.Name}}Map[x]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
var _{{.enum.Name}}Value = {{ unmapify .enum .lowercase }}
|
||||||
|
|
||||||
|
// Parse{{.enum.Name}} attempts to convert a string to a {{.enum.Name}}.
|
||||||
|
func Parse{{.enum.Name}}(name string) ({{.enum.Name}}, error) {
|
||||||
|
if x, ok := _{{.enum.Name}}Value[name]; ok {
|
||||||
|
return x, nil
|
||||||
|
}{{if .nocase }}
|
||||||
|
// Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to.
|
||||||
|
if x, ok := _{{.enum.Name}}Value[strings.ToLower(name)]; ok {
|
||||||
|
return x, nil
|
||||||
|
}{{- end}}
|
||||||
|
return {{.enum.Name}}(0), fmt.Errorf("%s is %w", name, ErrInvalid{{.enum.Name}})
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if .mustparse }}
|
||||||
|
// MustParse{{.enum.Name}} converts a string to a {{.enum.Name}}, and panics if is not valid.
|
||||||
|
func MustParse{{.enum.Name}}(name string) {{.enum.Name}} {
|
||||||
|
val, err := Parse{{.enum.Name}}(name)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if .ptr }}
|
||||||
|
func (x {{.enum.Name}}) Ptr() *{{.enum.Name}} {
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalText implements the text marshaller method.
|
||||||
|
func (x {{.enum.Name}}) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(x.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the text unmarshaller method.
|
||||||
|
func (x *{{.enum.Name}}) UnmarshalText(text []byte) error {
|
||||||
|
name := string(text)
|
||||||
|
tmp, err := Parse{{.enum.Name}}(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = tmp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if or .sql .sqlnullint .sqlnullstr}}
|
||||||
|
var err{{.enum.Name}}NilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (x *{{.enum.Name}}) Scan(value interface{}) (err error) {
|
||||||
|
if value == nil {
|
||||||
|
*x = {{.enum.Name}}(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wider range of scannable types.
|
||||||
|
// driver.Value values at the top of the list for expediency
|
||||||
|
switch v := value.(type) {
|
||||||
|
case int64:
|
||||||
|
*x = {{.enum.Name}}(v)
|
||||||
|
case string:
|
||||||
|
*x, err = Parse{{.enum.Name}}(v){{if .sqlnullint }}
|
||||||
|
if err != nil {
|
||||||
|
// try parsing the integer value as a string
|
||||||
|
if val, verr := strconv.Atoi(v); verr == nil {
|
||||||
|
*x, err = {{.enum.Name}}(val), nil
|
||||||
|
}
|
||||||
|
}{{end}}
|
||||||
|
case []byte:
|
||||||
|
*x, err = Parse{{.enum.Name}}(string(v)){{if .sqlnullint }}
|
||||||
|
if err != nil {
|
||||||
|
// try parsing the integer value as a string
|
||||||
|
if val, verr := strconv.Atoi(string(v)); verr == nil {
|
||||||
|
*x, err = {{.enum.Name}}(val), nil
|
||||||
|
}
|
||||||
|
}{{end}}
|
||||||
|
case {{.enum.Name}}:
|
||||||
|
*x = v
|
||||||
|
case int:
|
||||||
|
*x = {{.enum.Name}}(v)
|
||||||
|
case *{{.enum.Name}}:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = *v
|
||||||
|
case uint:
|
||||||
|
*x = {{.enum.Name}}(v)
|
||||||
|
case uint64:
|
||||||
|
*x = {{.enum.Name}}(v)
|
||||||
|
case *int:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = {{.enum.Name}}(*v)
|
||||||
|
case *int64:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = {{.enum.Name}}(*v)
|
||||||
|
case float64: // json marshals everything as a float64 if it's a number
|
||||||
|
*x = {{.enum.Name}}(v)
|
||||||
|
case *float64: // json marshals everything as a float64 if it's a number
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = {{.enum.Name}}(*v)
|
||||||
|
case *uint:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = {{.enum.Name}}(*v)
|
||||||
|
case *uint64:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = {{.enum.Name}}(*v)
|
||||||
|
case *string:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = Parse{{.enum.Name}}(*v){{if .sqlnullint }}
|
||||||
|
if err != nil {
|
||||||
|
// try parsing the integer value as a string
|
||||||
|
if val, verr := strconv.Atoi(*v); verr == nil {
|
||||||
|
*x, err = {{.enum.Name}}(val), nil
|
||||||
|
}
|
||||||
|
}{{end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if or .sql .sqlnullstr }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x {{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
return {{.enum.Type }}(x), nil
|
||||||
|
}
|
||||||
|
{{ else }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x {{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
return int64(x), nil
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
{{ if .flag }}
|
||||||
|
// Set implements the Golang flag.Value interface func.
|
||||||
|
func (x *{{.enum.Name}}) Set(val string) error {
|
||||||
|
v, err := Parse{{.enum.Name}}(val)
|
||||||
|
*x = v
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get implements the Golang flag.Getter interface func.
|
||||||
|
func (x *{{.enum.Name}}) Get() interface{} {
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements the github.com/spf13/pFlag Value interface.
|
||||||
|
func (x *{{.enum.Name}}) Type() string {
|
||||||
|
return "{{.enum.Name}}"
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if or .sqlnullint .sqlnullstr }}
|
||||||
|
type Null{{.enum.Name}} struct{
|
||||||
|
{{.enum.Name}} {{.enum.Name}}
|
||||||
|
Valid bool{{/* Add some info as to whether this value was set during unmarshalling or not */}}{{if .marshal }}
|
||||||
|
Set bool{{ end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNull{{.enum.Name}}(val interface{}) (x Null{{.enum.Name}}) {
|
||||||
|
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (x *Null{{.enum.Name}}) Scan(value interface{}) (err error) {
|
||||||
|
{{- if .marshal }}x.Set = true{{ end }}
|
||||||
|
if value == nil {
|
||||||
|
x.{{.enum.Name}}, x.Valid = {{.enum.Name}}(0), false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = x.{{.enum.Name}}.Scan(value)
|
||||||
|
x.Valid = (err == nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if .sqlnullint }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// driver.Value accepts int64 for int values.
|
||||||
|
return int64(x.{{.enum.Name}}), nil
|
||||||
|
}
|
||||||
|
{{ else }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return x.{{.enum.Name}}.String(), nil
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalJSON correctly serializes a Null{{.enum.Name}} to JSON.
|
||||||
|
func (n Null{{.enum.Name}}) MarshalJSON() ([]byte, error) {
|
||||||
|
const nullStr = "null"
|
||||||
|
if n.Valid {
|
||||||
|
return json.Marshal(n.{{.enum.Name}})
|
||||||
|
}
|
||||||
|
return []byte(nullStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON correctly deserializes a Null{{.enum.Name}} from JSON.
|
||||||
|
func (n *Null{{.enum.Name}}) UnmarshalJSON(b []byte) error {
|
||||||
|
n.Set = true
|
||||||
|
var x interface{}
|
||||||
|
err := json.Unmarshal(b, &x)
|
||||||
|
if err != nil{
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = n.Scan(x)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if and .sqlnullint .sqlnullstr }}
|
||||||
|
type Null{{.enum.Name}}Str struct {
|
||||||
|
Null{{.enum.Name}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNull{{.enum.Name}}Str(val interface{}) (x Null{{.enum.Name}}Str) {
|
||||||
|
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}Str) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return x.{{.enum.Name}}.String(), nil
|
||||||
|
}
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalJSON correctly serializes a Null{{.enum.Name}} to JSON.
|
||||||
|
func (n Null{{.enum.Name}}Str) MarshalJSON() ([]byte, error) {
|
||||||
|
const nullStr = "null"
|
||||||
|
if n.Valid {
|
||||||
|
return json.Marshal(n.{{.enum.Name}})
|
||||||
|
}
|
||||||
|
return []byte(nullStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON correctly deserializes a Null{{.enum.Name}} from JSON.
|
||||||
|
func (n *Null{{.enum.Name}}Str) UnmarshalJSON(b []byte) error {
|
||||||
|
n.Set = true
|
||||||
|
var x interface{}
|
||||||
|
err := json.Unmarshal(b, &x)
|
||||||
|
if err != nil{
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = n.Scan(x)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
{{- define "stringer"}}
|
||||||
|
const _{{.enum.Name}}Name = "{{ stringify .enum .forcelower }}"
|
||||||
|
|
||||||
|
{{ if .names }}var _{{.enum.Name}}Names = {{namify .enum}}
|
||||||
|
|
||||||
|
// {{.enum.Name}}Names returns a list of possible string values of {{.enum.Name}}.
|
||||||
|
func {{.enum.Name}}Names() []string {
|
||||||
|
tmp := make([]string, len(_{{.enum.Name}}Names))
|
||||||
|
copy(tmp, _{{.enum.Name}}Names)
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ if .values }}
|
||||||
|
|
||||||
|
// {{.enum.Name}}Values returns a list of the values for {{.enum.Name}}
|
||||||
|
func {{.enum.Name}}Values() []{{.enum.Name}} {
|
||||||
|
return []{{.enum.Name}}{ {{ range $rIndex, $value := .enum.Values }}{{ if ne $value.Name "_"}}
|
||||||
|
{{$value.PrefixedName}},{{ end }}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
384
pkg/utils/generator/enum_string.tmpl
Normal file
384
pkg/utils/generator/enum_string.tmpl
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
{{- define "enum_string"}}
|
||||||
|
const (
|
||||||
|
{{- $enumName := .enum.Name -}}
|
||||||
|
{{- $enumType := .enum.Type -}}
|
||||||
|
{{- $noComments := .nocomments -}}
|
||||||
|
{{- $vars := dict "lastoffset" "0" -}}
|
||||||
|
{{ range $rIndex, $value := .enum.Values }}
|
||||||
|
{{- if $noComments }}{{else}}
|
||||||
|
{{ if eq $value.Name "_"}}// Skipped value.{{else}}// {{$value.PrefixedName}} is a {{$enumName}} of type {{$value.RawName}}.{{end}}{{end}}
|
||||||
|
{{- if $value.Comment}}
|
||||||
|
// {{$value.Comment}}
|
||||||
|
{{- end}}
|
||||||
|
{{$value.PrefixedName}} {{$enumName}} = "{{$value.ValueStr}}"
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
{{if .names -}}
|
||||||
|
var ErrInvalid{{.enum.Name}} = fmt.Errorf("not a valid {{.enum.Name}}, try [%s]", strings.Join(_{{.enum.Name}}Names, ", "))
|
||||||
|
{{- else -}}
|
||||||
|
var ErrInvalid{{.enum.Name}} = errors.New("not a valid {{.enum.Name}}")
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{ if .names }}var _{{.enum.Name}}Names = {{namify .enum}}
|
||||||
|
|
||||||
|
// {{.enum.Name}}Names returns a list of possible string values of {{.enum.Name}}.
|
||||||
|
func {{.enum.Name}}Names() []string {
|
||||||
|
tmp := make([]string, len(_{{.enum.Name}}Names))
|
||||||
|
copy(tmp, _{{.enum.Name}}Names)
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
|
||||||
|
{{ if .values }}
|
||||||
|
|
||||||
|
// {{.enum.Name}}Values returns a list of the values for {{.enum.Name}}
|
||||||
|
func {{.enum.Name}}Values() []{{.enum.Name}} {
|
||||||
|
return []{{.enum.Name}}{ {{ range $rIndex, $value := .enum.Values }}{{ if ne $value.Name "_"}}
|
||||||
|
{{$value.PrefixedName}},{{ end }}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
// String implements the Stringer interface.
|
||||||
|
func (x {{.enum.Name}}) String() string {
|
||||||
|
return string(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid provides a quick way to determine if the typed value is
|
||||||
|
// part of the allowed enumerated values
|
||||||
|
func (x {{.enum.Name}}) IsValid() bool {
|
||||||
|
_, err := Parse{{.enum.Name}}(string(x))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _{{.enum.Name}}Value = {{ unmapify .enum .lowercase }}
|
||||||
|
|
||||||
|
// Parse{{.enum.Name}} attempts to convert a string to a {{.enum.Name}}.
|
||||||
|
func Parse{{.enum.Name}}(name string) ({{.enum.Name}}, error) {
|
||||||
|
if x, ok := _{{.enum.Name}}Value[name]; ok {
|
||||||
|
return x, nil
|
||||||
|
}{{if .nocase }}
|
||||||
|
// Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to.
|
||||||
|
if x, ok := _{{.enum.Name}}Value[strings.ToLower(name)]; ok {
|
||||||
|
return x, nil
|
||||||
|
}{{- end}}
|
||||||
|
return {{.enum.Name}}(""), fmt.Errorf("%s is %w", name, ErrInvalid{{.enum.Name}})
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if .mustparse }}
|
||||||
|
// MustParse{{.enum.Name}} converts a string to a {{.enum.Name}}, and panics if is not valid.
|
||||||
|
func MustParse{{.enum.Name}}(name string) {{.enum.Name}} {
|
||||||
|
val, err := Parse{{.enum.Name}}(name)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if .ptr }}
|
||||||
|
func (x {{.enum.Name}}) Ptr() *{{.enum.Name}} {
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalText implements the text marshaller method.
|
||||||
|
func (x {{.enum.Name}}) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(string(x)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the text unmarshaller method.
|
||||||
|
func (x *{{.enum.Name}}) UnmarshalText(text []byte) error {
|
||||||
|
tmp, err := Parse{{.enum.Name}}(string(text))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = tmp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if .anySQLEnabled }}
|
||||||
|
var err{{.enum.Name}}NilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* SQL stored as a string value */}}
|
||||||
|
{{ if .sql }}
|
||||||
|
{{ if eq .enum.Type "string" }}
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (x *{{.enum.Name}}) Scan(value interface{}) (err error) {
|
||||||
|
if value == nil {
|
||||||
|
*x = {{.enum.Name}}("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wider range of scannable types.
|
||||||
|
// driver.Value values at the top of the list for expediency
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
*x, err = Parse{{.enum.Name}}(v)
|
||||||
|
case []byte:
|
||||||
|
*x, err = Parse{{.enum.Name}}(string(v))
|
||||||
|
case {{.enum.Name}}:
|
||||||
|
*x = v
|
||||||
|
case *{{.enum.Name}}:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = *v
|
||||||
|
case *string:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = Parse{{.enum.Name}}(*v)
|
||||||
|
default:
|
||||||
|
return errors.New("invalid type for {{.enum.Name}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x {{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
return x.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ else if or .sqlint .sqlnullint }}
|
||||||
|
{{/* SQL stored as an integer value */}}
|
||||||
|
var sqlInt{{.enum.Name}}Map = map[int64]{{.enum.Name}}{ {{ range $rIndex, $value := .enum.Values }}{{ if ne $value.Name "_"}}
|
||||||
|
{{ $value.ValueInt }}: {{ $value.PrefixedName }},{{end}}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqlInt{{.enum.Name}}Value = map[{{.enum.Name}}]int64{ {{ range $rIndex, $value := .enum.Values }}{{ if ne $value.Name "_"}}
|
||||||
|
{{ $value.PrefixedName }}: {{ $value.ValueInt }},{{end}}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupSqlInt{{.enum.Name}}(val int64) ({{.enum.Name}}, error){
|
||||||
|
x, ok := sqlInt{{.enum.Name}}Map[val]
|
||||||
|
if !ok{
|
||||||
|
return x, fmt.Errorf("%v is not %w", val, ErrInvalid{{.enum.Name}})
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (x *{{.enum.Name}}) Scan(value interface{}) (err error) {
|
||||||
|
if value == nil {
|
||||||
|
*x = {{.enum.Name}}("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wider range of scannable types.
|
||||||
|
// driver.Value values at the top of the list for expediency
|
||||||
|
switch v := value.(type) {
|
||||||
|
case int64:
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(v)
|
||||||
|
case string:
|
||||||
|
*x, err = Parse{{.enum.Name}}(v)
|
||||||
|
case []byte:
|
||||||
|
if val, verr := strconv.ParseInt(string(v), 10, 64); verr == nil {
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(val)
|
||||||
|
} else {
|
||||||
|
// try parsing the value as a string
|
||||||
|
*x, err = Parse{{.enum.Name}}(string(v))
|
||||||
|
}
|
||||||
|
case {{.enum.Name}}:
|
||||||
|
*x = v
|
||||||
|
case int:
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(v))
|
||||||
|
case *{{.enum.Name}}:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x = *v
|
||||||
|
case uint:
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(v))
|
||||||
|
case uint64:
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(v))
|
||||||
|
case *int:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(*v))
|
||||||
|
case *int64:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(*v))
|
||||||
|
case float64: // json marshals everything as a float64 if it's a number
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(v))
|
||||||
|
case *float64: // json marshals everything as a float64 if it's a number
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(*v))
|
||||||
|
case *uint:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(*v))
|
||||||
|
case *uint64:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = lookupSqlInt{{.enum.Name}}(int64(*v))
|
||||||
|
case *string:
|
||||||
|
if v == nil{
|
||||||
|
return err{{.enum.Name}}NilPtr
|
||||||
|
}
|
||||||
|
*x, err = Parse{{.enum.Name}}(*v)
|
||||||
|
default:
|
||||||
|
return errors.New("invalid type for {{.enum.Name}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x {{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
val, ok := sqlInt{{.enum.Name}}Value[x]
|
||||||
|
if !ok{
|
||||||
|
return nil, ErrInvalid{{.enum.Name}}
|
||||||
|
}
|
||||||
|
return int64(val), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
{{ if .flag }}
|
||||||
|
// Set implements the Golang flag.Value interface func.
|
||||||
|
func (x *{{.enum.Name}}) Set(val string) error {
|
||||||
|
v, err := Parse{{.enum.Name}}(val)
|
||||||
|
*x = v
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get implements the Golang flag.Getter interface func.
|
||||||
|
func (x *{{.enum.Name}}) Get() interface{} {
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements the github.com/spf13/pFlag Value interface.
|
||||||
|
func (x *{{.enum.Name}}) Type() string {
|
||||||
|
return "{{.enum.Name}}"
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{ if or .sqlnullint .sqlnullstr }}
|
||||||
|
type Null{{.enum.Name}} struct{
|
||||||
|
{{.enum.Name}} {{.enum.Name}}
|
||||||
|
Valid bool{{/* Add some info as to whether this value was set during unmarshalling or not */}}{{if .marshal }}
|
||||||
|
Set bool{{ end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNull{{.enum.Name}}(val interface{}) (x Null{{.enum.Name}}) {
|
||||||
|
err := x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||||
|
_ = err // make any errcheck linters happy
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (x *Null{{.enum.Name}}) Scan(value interface{}) (err error) {
|
||||||
|
if value == nil {
|
||||||
|
x.{{.enum.Name}}, x.Valid = {{.enum.Name}}(""), false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = x.{{.enum.Name}}.Scan(value)
|
||||||
|
x.Valid = (err == nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if .sqlnullint }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// driver.Value accepts int64 for int values.
|
||||||
|
return string(x.{{.enum.Name}}), nil
|
||||||
|
}
|
||||||
|
{{ else }}
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return x.{{.enum.Name}}.String(), nil
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalJSON correctly serializes a Null{{.enum.Name}} to JSON.
|
||||||
|
func (n Null{{.enum.Name}}) MarshalJSON() ([]byte, error) {
|
||||||
|
const nullStr = "null"
|
||||||
|
if n.Valid {
|
||||||
|
return json.Marshal(n.{{.enum.Name}})
|
||||||
|
}
|
||||||
|
return []byte(nullStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON correctly deserializes a Null{{.enum.Name}} from JSON.
|
||||||
|
func (n *Null{{.enum.Name}}) UnmarshalJSON(b []byte) error {
|
||||||
|
n.Set = true
|
||||||
|
var x interface{}
|
||||||
|
err := json.Unmarshal(b, &x)
|
||||||
|
if err != nil{
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = n.Scan(x)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if and .sqlnullint .sqlnullstr }}
|
||||||
|
type Null{{.enum.Name}}Str struct {
|
||||||
|
Null{{.enum.Name}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNull{{.enum.Name}}Str(val interface{}) (x Null{{.enum.Name}}Str) {
|
||||||
|
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (x Null{{.enum.Name}}Str) Value() (driver.Value, error) {
|
||||||
|
if !x.Valid{
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return x.{{.enum.Name}}.String(), nil
|
||||||
|
}
|
||||||
|
{{ if .marshal }}
|
||||||
|
// MarshalJSON correctly serializes a Null{{.enum.Name}} to JSON.
|
||||||
|
func (n Null{{.enum.Name}}Str) MarshalJSON() ([]byte, error) {
|
||||||
|
const nullStr = "null"
|
||||||
|
if n.Valid {
|
||||||
|
return json.Marshal(n.{{.enum.Name}})
|
||||||
|
}
|
||||||
|
return []byte(nullStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON correctly deserializes a Null{{.enum.Name}} from JSON.
|
||||||
|
func (n *Null{{.enum.Name}}Str) UnmarshalJSON(b []byte) error {
|
||||||
|
n.Set = true
|
||||||
|
var x interface{}
|
||||||
|
err := json.Unmarshal(b, &x)
|
||||||
|
if err != nil{
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = n.Scan(x)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
22
pkg/utils/generator/example_1.18_test.go
Normal file
22
pkg/utils/generator/example_1.18_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package generator
|
||||||
|
|
||||||
|
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
|
||||||
|
// as types for map values.
|
||||||
|
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
|
||||||
|
var s V
|
||||||
|
for _, v := range m {
|
||||||
|
s += v
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeType is a type of change detected.
|
||||||
|
/* ENUM(
|
||||||
|
Create
|
||||||
|
Update
|
||||||
|
Delete
|
||||||
|
) */
|
||||||
|
type ChangeType int
|
||||||
151
pkg/utils/generator/example_test.go
Normal file
151
pkg/utils/generator/example_test.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package generator
|
||||||
|
|
||||||
|
// X is doc'ed
|
||||||
|
type X struct{}
|
||||||
|
|
||||||
|
// Color is an enumeration of colors that are allowed.
|
||||||
|
// ENUM(
|
||||||
|
// Black, White, Red
|
||||||
|
// Green
|
||||||
|
// Blue=33
|
||||||
|
// grey=
|
||||||
|
// yellow
|
||||||
|
// ).
|
||||||
|
type Color int
|
||||||
|
|
||||||
|
// Animal x ENUM(
|
||||||
|
// Cat,
|
||||||
|
// Dog,
|
||||||
|
// Fish
|
||||||
|
// ) Some other line of info
|
||||||
|
type Animal int32
|
||||||
|
|
||||||
|
// Model x ENUM(Toyota,_,Chevy,_,Ford).
|
||||||
|
type Model int32
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
Coke
|
||||||
|
Pepsi
|
||||||
|
MtnDew
|
||||||
|
|
||||||
|
).
|
||||||
|
*/
|
||||||
|
type Soda int64
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
test_lower
|
||||||
|
Test_capital
|
||||||
|
anotherLowerCaseStart
|
||||||
|
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
type Cases int64
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
test-Hyphen
|
||||||
|
-hyphenStart
|
||||||
|
_underscoreFirst
|
||||||
|
0numberFirst
|
||||||
|
123456789a
|
||||||
|
123123-asdf
|
||||||
|
ending-hyphen-
|
||||||
|
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
type Sanitizing int64
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
startWithNum=23
|
||||||
|
nextNum
|
||||||
|
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
type StartNotZero int64
|
||||||
|
|
||||||
|
// ENUM(
|
||||||
|
// Black, White, Red
|
||||||
|
// Green
|
||||||
|
// Blue=33 // Blue starts with 33.
|
||||||
|
// grey=
|
||||||
|
// yellow
|
||||||
|
// )
|
||||||
|
type ColorWithComment int
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
Black, White, Red
|
||||||
|
Green
|
||||||
|
Blue=33 // Blue starts with 33
|
||||||
|
grey=
|
||||||
|
yellow
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
type ColorWithComment2 int
|
||||||
|
|
||||||
|
/* ENUM(
|
||||||
|
Black, White, Red
|
||||||
|
Green = 33 // Green starts with 33
|
||||||
|
*/
|
||||||
|
// Blue
|
||||||
|
// grey=
|
||||||
|
// yellow
|
||||||
|
// blue-green // blue-green comment
|
||||||
|
// red-orange
|
||||||
|
// red-orange-blue
|
||||||
|
// )
|
||||||
|
type ColorWithComment3 int
|
||||||
|
|
||||||
|
/* ENUM(
|
||||||
|
_, // Placeholder
|
||||||
|
Black, White, Red
|
||||||
|
Green = 33 // Green starts with 33
|
||||||
|
*/
|
||||||
|
// Blue
|
||||||
|
// grey=
|
||||||
|
// yellow // Where did all the (somewhat) bad fish go? (something else that goes in parentheses at the end of the line)
|
||||||
|
// blue-green // blue-green comment
|
||||||
|
// red-orange // has a , in it!?!
|
||||||
|
// )
|
||||||
|
type ColorWithComment4 int
|
||||||
|
|
||||||
|
/*
|
||||||
|
ENUM(
|
||||||
|
|
||||||
|
Unknown= 0
|
||||||
|
E2P15 = 32768
|
||||||
|
E2P16 = 65536
|
||||||
|
E2P17 = 131072
|
||||||
|
E2P18 = 262144
|
||||||
|
E2P19 = 524288
|
||||||
|
E2P20 = 1048576
|
||||||
|
E2P21 = 2097152
|
||||||
|
E2P22 = 33554432
|
||||||
|
E2P23 = 67108864
|
||||||
|
E2P28 = 536870912
|
||||||
|
E2P30 = 1073741824
|
||||||
|
E2P31 = 2147483648
|
||||||
|
E2P32 = 4294967296
|
||||||
|
E2P33 = 8454967296
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
type Enum64bit uint64
|
||||||
|
|
||||||
|
// NonASCII
|
||||||
|
// ENUM(
|
||||||
|
// Продам = 1114
|
||||||
|
// 車庫 = 300
|
||||||
|
// էժան = 1
|
||||||
|
// )
|
||||||
|
type NonASCII int
|
||||||
|
|
||||||
|
// StringEnum.
|
||||||
|
// ENUM(
|
||||||
|
// random = 1114
|
||||||
|
// values = 300
|
||||||
|
// here = 1
|
||||||
|
// )
|
||||||
|
type StringEnum string
|
||||||
745
pkg/utils/generator/generator.go
Normal file
745
pkg/utils/generator/generator.go
Normal file
@@ -0,0 +1,745 @@
|
|||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/Masterminds/sprig/v3"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
"golang.org/x/tools/imports"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
skipHolder = `_`
|
||||||
|
parseCommentPrefix = `//`
|
||||||
|
)
|
||||||
|
|
||||||
|
var replacementNames = map[string]string{}
|
||||||
|
|
||||||
|
// Generator is responsible for generating validation files for the given in a go source file.
|
||||||
|
type Generator struct {
|
||||||
|
Version string
|
||||||
|
Revision string
|
||||||
|
BuildDate string
|
||||||
|
BuiltBy string
|
||||||
|
t *template.Template
|
||||||
|
knownTemplates map[string]*template.Template
|
||||||
|
userTemplateNames []string
|
||||||
|
fileSet *token.FileSet
|
||||||
|
noPrefix bool
|
||||||
|
lowercaseLookup bool
|
||||||
|
caseInsensitive bool
|
||||||
|
marshal bool
|
||||||
|
sql bool
|
||||||
|
sqlint bool
|
||||||
|
flag bool
|
||||||
|
names bool
|
||||||
|
values bool
|
||||||
|
leaveSnakeCase bool
|
||||||
|
prefix string
|
||||||
|
sqlNullInt bool
|
||||||
|
sqlNullStr bool
|
||||||
|
ptr bool
|
||||||
|
mustParse bool
|
||||||
|
forceLower bool
|
||||||
|
noComments bool
|
||||||
|
buildTags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum holds data for a discovered enum in the parsed source
|
||||||
|
type Enum struct {
|
||||||
|
Name string
|
||||||
|
Prefix string
|
||||||
|
Type string
|
||||||
|
Values []EnumValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumValue holds the individual data for each enum value within the found enum.
|
||||||
|
type EnumValue struct {
|
||||||
|
RawName string
|
||||||
|
Name string
|
||||||
|
PrefixedName string
|
||||||
|
ValueStr string
|
||||||
|
ValueInt interface{}
|
||||||
|
Comment string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenerator is a constructor method for creating a new Generator with default
|
||||||
|
// templates loaded.
|
||||||
|
func NewGenerator() *Generator {
|
||||||
|
g := &Generator{
|
||||||
|
Version: "-",
|
||||||
|
Revision: "-",
|
||||||
|
BuildDate: "-",
|
||||||
|
BuiltBy: "-",
|
||||||
|
knownTemplates: make(map[string]*template.Template),
|
||||||
|
userTemplateNames: make([]string, 0),
|
||||||
|
t: template.New("generator"),
|
||||||
|
fileSet: token.NewFileSet(),
|
||||||
|
noPrefix: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs := sprig.TxtFuncMap()
|
||||||
|
|
||||||
|
funcs["stringify"] = Stringify
|
||||||
|
funcs["mapify"] = Mapify
|
||||||
|
funcs["unmapify"] = Unmapify
|
||||||
|
funcs["namify"] = Namify
|
||||||
|
funcs["offset"] = Offset
|
||||||
|
|
||||||
|
g.t.Funcs(funcs)
|
||||||
|
|
||||||
|
g.addEmbeddedTemplates()
|
||||||
|
|
||||||
|
g.updateTemplates()
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNoPrefix is used to change the enum const values generated to not have the enum on them.
|
||||||
|
func (g *Generator) WithNoPrefix() *Generator {
|
||||||
|
g.noPrefix = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLowercaseVariant is used to change the enum const values generated to not have the enum on them.
|
||||||
|
func (g *Generator) WithLowercaseVariant() *Generator {
|
||||||
|
g.lowercaseLookup = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLowercaseVariant is used to change the enum const values generated to not have the enum on them.
|
||||||
|
func (g *Generator) WithCaseInsensitiveParse() *Generator {
|
||||||
|
g.lowercaseLookup = true
|
||||||
|
g.caseInsensitive = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMarshal is used to add marshalling to the enum
|
||||||
|
func (g *Generator) WithMarshal() *Generator {
|
||||||
|
g.marshal = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSQLDriver is used to add marshalling to the enum
|
||||||
|
func (g *Generator) WithSQLDriver() *Generator {
|
||||||
|
g.sql = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSQLInt is used to signal a string to be stored as an int.
|
||||||
|
func (g *Generator) WithSQLInt() *Generator {
|
||||||
|
g.sqlint = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFlag is used to add flag methods to the enum
|
||||||
|
func (g *Generator) WithFlag() *Generator {
|
||||||
|
g.flag = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNames is used to add Names methods to the enum
|
||||||
|
func (g *Generator) WithNames() *Generator {
|
||||||
|
g.names = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValues is used to add Values methods to the enum
|
||||||
|
func (g *Generator) WithValues() *Generator {
|
||||||
|
g.values = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutSnakeToCamel is used to add flag methods to the enum
|
||||||
|
func (g *Generator) WithoutSnakeToCamel() *Generator {
|
||||||
|
g.leaveSnakeCase = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPrefix is used to add a custom prefix to the enum constants
|
||||||
|
func (g *Generator) WithPrefix(prefix string) *Generator {
|
||||||
|
g.prefix = prefix
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPtr adds a way to get a pointer value straight from the const value.
|
||||||
|
func (g *Generator) WithPtr() *Generator {
|
||||||
|
g.ptr = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSQLNullInt is used to add a null int option for SQL interactions.
|
||||||
|
func (g *Generator) WithSQLNullInt() *Generator {
|
||||||
|
g.sqlNullInt = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSQLNullStr is used to add a null string option for SQL interactions.
|
||||||
|
func (g *Generator) WithSQLNullStr() *Generator {
|
||||||
|
g.sqlNullStr = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMustParse is used to add a method `MustParse` that will panic on failure.
|
||||||
|
func (g *Generator) WithMustParse() *Generator {
|
||||||
|
g.mustParse = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithForceLower is used to force enums names to lower case while keeping variable names the same.
|
||||||
|
func (g *Generator) WithForceLower() *Generator {
|
||||||
|
g.forceLower = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNoComments is used to remove auto generated comments from the enum.
|
||||||
|
func (g *Generator) WithNoComments() *Generator {
|
||||||
|
g.noComments = true
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBuildTags will add build tags to the generated file.
|
||||||
|
func (g *Generator) WithBuildTags(tags ...string) *Generator {
|
||||||
|
g.buildTags = append(g.buildTags, tags...)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) anySQLEnabled() bool {
|
||||||
|
return g.sql || g.sqlNullStr || g.sqlint || g.sqlNullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAliases is used to add aliases to replace during name sanitization.
|
||||||
|
func ParseAliases(aliases []string) error {
|
||||||
|
aliasMap := map[string]string{}
|
||||||
|
|
||||||
|
for _, str := range aliases {
|
||||||
|
kvps := strings.Split(str, ",")
|
||||||
|
for _, kvp := range kvps {
|
||||||
|
parts := strings.Split(kvp, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf("invalid formatted alias entry %q, must be in the format \"key:value\"", kvp)
|
||||||
|
}
|
||||||
|
aliasMap[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range aliasMap {
|
||||||
|
replacementNames[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTemplates is used to provide the filenames of additional templates.
|
||||||
|
func (g *Generator) WithTemplates(filenames ...string) *Generator {
|
||||||
|
for _, ut := range template.Must(g.t.ParseFiles(filenames...)).Templates() {
|
||||||
|
if _, ok := g.knownTemplates[ut.Name()]; !ok {
|
||||||
|
g.userTemplateNames = append(g.userTemplateNames, ut.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.updateTemplates()
|
||||||
|
sort.Strings(g.userTemplateNames)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateFromFile is responsible for orchestrating the Code generation. It results in a byte array
|
||||||
|
// that can be written to any file desired. It has already had goimports run on the code before being returned.
|
||||||
|
func (g *Generator) GenerateFromFile(inputFile string) ([]byte, error) {
|
||||||
|
f, err := g.parseFile(inputFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("generate: error parsing input file '%s': %s", inputFile, err)
|
||||||
|
}
|
||||||
|
return g.Generate(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate does the heavy lifting for the code generation starting from the parsed AST file.
|
||||||
|
func (g *Generator) Generate(f *ast.File) ([]byte, error) {
|
||||||
|
enums := g.inspect(f)
|
||||||
|
if len(enums) <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := f.Name.Name
|
||||||
|
|
||||||
|
vBuff := bytes.NewBuffer([]byte{})
|
||||||
|
err := g.t.ExecuteTemplate(vBuff, "header", map[string]interface{}{
|
||||||
|
"package": pkg,
|
||||||
|
"version": g.Version,
|
||||||
|
"revision": g.Revision,
|
||||||
|
"buildDate": g.BuildDate,
|
||||||
|
"builtBy": g.BuiltBy,
|
||||||
|
"buildTags": g.buildTags,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed writing header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the output more consistent by iterating over sorted keys of map
|
||||||
|
var keys []string
|
||||||
|
for key := range enums {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
var created int
|
||||||
|
for _, name := range keys {
|
||||||
|
ts := enums[name]
|
||||||
|
|
||||||
|
// Parse the enum doc statement
|
||||||
|
enum, pErr := g.parseEnum(ts)
|
||||||
|
if pErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
created++
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"enum": enum,
|
||||||
|
"name": name,
|
||||||
|
"lowercase": g.lowercaseLookup,
|
||||||
|
"nocase": g.caseInsensitive,
|
||||||
|
"nocomments": g.noComments,
|
||||||
|
"marshal": g.marshal,
|
||||||
|
"sql": g.sql,
|
||||||
|
"sqlint": g.sqlint,
|
||||||
|
"flag": g.flag,
|
||||||
|
"names": g.names,
|
||||||
|
"ptr": g.ptr,
|
||||||
|
"values": g.values,
|
||||||
|
"anySQLEnabled": g.anySQLEnabled(),
|
||||||
|
"sqlnullint": g.sqlNullInt,
|
||||||
|
"sqlnullstr": g.sqlNullStr,
|
||||||
|
"mustparse": g.mustParse,
|
||||||
|
"forcelower": g.forceLower,
|
||||||
|
}
|
||||||
|
|
||||||
|
templateName := "enum"
|
||||||
|
if enum.Type == "string" {
|
||||||
|
templateName = "enum_string"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.t.ExecuteTemplate(vBuff, templateName, data)
|
||||||
|
if err != nil {
|
||||||
|
return vBuff.Bytes(), fmt.Errorf("failed writing enum data for enum: %q: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userTemplateName := range g.userTemplateNames {
|
||||||
|
err = g.t.ExecuteTemplate(vBuff, userTemplateName, data)
|
||||||
|
if err != nil {
|
||||||
|
return vBuff.Bytes(), fmt.Errorf("failed writing enum data for enum: %q, template: %v: %w", name, userTemplateName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if created < 1 {
|
||||||
|
// Don't save anything if we didn't actually generate any successful enums.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted, err := imports.Process(pkg, vBuff.Bytes(), nil)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("generate: error formatting code %s\n\n%s", err, vBuff.String())
|
||||||
|
}
|
||||||
|
return formatted, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateTemplates will update the lookup map for validation checks that are
|
||||||
|
// allowed within the template engine.
|
||||||
|
func (g *Generator) updateTemplates() {
|
||||||
|
for _, template := range g.t.Templates() {
|
||||||
|
g.knownTemplates[template.Name()] = template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFile simply calls the go/parser ParseFile function with an empty token.FileSet
|
||||||
|
func (g *Generator) parseFile(fileName string) (*ast.File, error) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
return parser.ParseFile(g.fileSet, fileName, nil, parser.ParseComments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEnum looks for the ENUM(x,y,z) formatted documentation from the type definition
|
||||||
|
func (g *Generator) parseEnum(ts *ast.TypeSpec) (*Enum, error) {
|
||||||
|
if ts.Doc == nil {
|
||||||
|
return nil, errors.New("no doc on enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum := &Enum{}
|
||||||
|
|
||||||
|
enum.Name = ts.Name.Name
|
||||||
|
enum.Type = fmt.Sprintf("%s", ts.Type)
|
||||||
|
if !g.noPrefix {
|
||||||
|
enum.Prefix = ts.Name.Name
|
||||||
|
}
|
||||||
|
if g.prefix != "" {
|
||||||
|
enum.Prefix = g.prefix + enum.Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
enumDecl := getEnumDeclFromComments(ts.Doc.List)
|
||||||
|
if enumDecl == "" {
|
||||||
|
return nil, errors.New("failed parsing enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
values := strings.Split(strings.TrimSuffix(strings.TrimPrefix(enumDecl, `ENUM(`), `)`), `,`)
|
||||||
|
var (
|
||||||
|
data interface{}
|
||||||
|
unsigned bool
|
||||||
|
)
|
||||||
|
if strings.HasPrefix(enum.Type, "u") {
|
||||||
|
data = uint64(0)
|
||||||
|
unsigned = true
|
||||||
|
} else {
|
||||||
|
data = int64(0)
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
var comment string
|
||||||
|
|
||||||
|
// Trim and store comments
|
||||||
|
if strings.Contains(value, parseCommentPrefix) {
|
||||||
|
commentStartIndex := strings.Index(value, parseCommentPrefix)
|
||||||
|
comment = value[commentStartIndex+len(parseCommentPrefix):]
|
||||||
|
comment = strings.TrimSpace(unescapeComment(comment))
|
||||||
|
// value without comment
|
||||||
|
value = value[:commentStartIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure to leave out any empty parts
|
||||||
|
if value != "" {
|
||||||
|
rawName := value
|
||||||
|
valueStr := value
|
||||||
|
|
||||||
|
if strings.Contains(value, `=`) {
|
||||||
|
// Get the value specified and set the data to that value.
|
||||||
|
equalIndex := strings.Index(value, `=`)
|
||||||
|
dataVal := strings.TrimSpace(value[equalIndex+1:])
|
||||||
|
if dataVal != "" {
|
||||||
|
valueStr = dataVal
|
||||||
|
rawName = value[:equalIndex]
|
||||||
|
if enum.Type == "string" {
|
||||||
|
if parsed, err := strconv.ParseInt(dataVal, 10, 64); err == nil {
|
||||||
|
data = parsed
|
||||||
|
valueStr = rawName
|
||||||
|
}
|
||||||
|
if isQuoted(dataVal) {
|
||||||
|
valueStr = trimQuotes(dataVal)
|
||||||
|
}
|
||||||
|
} else if unsigned {
|
||||||
|
newData, err := strconv.ParseUint(dataVal, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed parsing the data part of enum value '%s': %w", value, err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = newData
|
||||||
|
} else {
|
||||||
|
newData, err := strconv.ParseInt(dataVal, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed parsing the data part of enum value '%s': %w", value, err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = newData
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rawName = strings.TrimSuffix(rawName, `=`)
|
||||||
|
fmt.Printf("Ignoring enum with '=' but no value after: %s\n", rawName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawName = strings.TrimSpace(rawName)
|
||||||
|
valueStr = strings.TrimSpace(valueStr)
|
||||||
|
name := cases.Title(language.Und, cases.NoLower).String(rawName)
|
||||||
|
prefixedName := name
|
||||||
|
if name != skipHolder {
|
||||||
|
prefixedName = enum.Prefix + name
|
||||||
|
prefixedName = sanitizeValue(prefixedName)
|
||||||
|
if !g.leaveSnakeCase {
|
||||||
|
prefixedName = snakeToCamelCase(prefixedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ev := EnumValue{Name: name, RawName: rawName, PrefixedName: prefixedName, ValueStr: valueStr, ValueInt: data, Comment: comment}
|
||||||
|
enum.Values = append(enum.Values, ev)
|
||||||
|
data = increment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("###\nENUM: %+v\n###\n", enum)
|
||||||
|
|
||||||
|
return enum, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQuoted(s string) bool {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
return (strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`)) || (strings.HasPrefix(s, `'`) && strings.HasSuffix(s, `'`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimQuotes(s string) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
for _, quote := range []string{`"`, `'`} {
|
||||||
|
s = strings.TrimPrefix(s, quote)
|
||||||
|
s = strings.TrimSuffix(s, quote)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func increment(d interface{}) interface{} {
|
||||||
|
switch v := d.(type) {
|
||||||
|
case uint64:
|
||||||
|
return v + 1
|
||||||
|
case int64:
|
||||||
|
return v + 1
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func unescapeComment(comment string) string {
|
||||||
|
val, err := url.QueryUnescape(comment)
|
||||||
|
if err != nil {
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitizeValue will ensure the value name generated adheres to golang's
|
||||||
|
// identifier syntax as described here: https://golang.org/ref/spec#Identifiers
|
||||||
|
// identifier = letter { letter | unicode_digit }
|
||||||
|
// where letter can be unicode_letter or '_'
|
||||||
|
func sanitizeValue(value string) string {
|
||||||
|
// Keep skip value holders
|
||||||
|
if value == skipHolder {
|
||||||
|
return skipHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
replacedValue := value
|
||||||
|
for k, v := range replacementNames {
|
||||||
|
replacedValue = strings.ReplaceAll(replacedValue, k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBuilder := strings.Builder{}
|
||||||
|
nameBuilder.Grow(len(replacedValue))
|
||||||
|
|
||||||
|
for i, r := range replacedValue {
|
||||||
|
// If the start character is not a unicode letter (this check includes the case of '_')
|
||||||
|
// then we need to add an exported prefix, so tack on a 'X' at the beginning
|
||||||
|
if i == 0 && !unicode.IsLetter(r) {
|
||||||
|
nameBuilder.WriteRune('X')
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' {
|
||||||
|
nameBuilder.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameBuilder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func snakeToCamelCase(value string) string {
|
||||||
|
parts := strings.Split(value, "_")
|
||||||
|
title := cases.Title(language.Und, cases.NoLower)
|
||||||
|
|
||||||
|
for i, part := range parts {
|
||||||
|
parts[i] = title.String(part)
|
||||||
|
}
|
||||||
|
value = strings.Join(parts, "")
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnumDeclFromComments parses the array of comment strings and creates a single Enum Declaration statement
|
||||||
|
// that is easier to deal with for the remainder of parsing. It turns multi line declarations and makes a single
|
||||||
|
// string declaration.
|
||||||
|
func getEnumDeclFromComments(comments []*ast.Comment) string {
|
||||||
|
const EnumPrefix = "ENUM("
|
||||||
|
var (
|
||||||
|
parts []string
|
||||||
|
lines []string
|
||||||
|
store bool
|
||||||
|
enumParamLevel int
|
||||||
|
filteredLines []string
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, comment := range comments {
|
||||||
|
lines = append(lines, breakCommentIntoLines(comment)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredLines = make([]string, 0, len(lines))
|
||||||
|
for idx := range lines {
|
||||||
|
line := lines[idx]
|
||||||
|
// If we're not in the enum, and this line doesn't contain the
|
||||||
|
// start string, then move along
|
||||||
|
if !store && !strings.Contains(line, EnumPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !store {
|
||||||
|
// We must have had the start value in here
|
||||||
|
store = true
|
||||||
|
enumParamLevel = 1
|
||||||
|
start := strings.Index(line, EnumPrefix)
|
||||||
|
line = line[start+len(EnumPrefix):]
|
||||||
|
}
|
||||||
|
lineParamLevel := strings.Count(line, "(")
|
||||||
|
lineParamLevel = lineParamLevel - strings.Count(line, ")")
|
||||||
|
|
||||||
|
if enumParamLevel+lineParamLevel < 1 {
|
||||||
|
// We've ended, either with more than we need, or with just enough. Now we need to find the end.
|
||||||
|
for lineIdx, ch := range line {
|
||||||
|
if ch == '(' {
|
||||||
|
enumParamLevel = enumParamLevel + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ch == ')' {
|
||||||
|
enumParamLevel = enumParamLevel - 1
|
||||||
|
if enumParamLevel == 0 {
|
||||||
|
// We've found the end of the ENUM() definition,
|
||||||
|
// Cut off the suffix and break out of the loop
|
||||||
|
line = line[:lineIdx]
|
||||||
|
store = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredLines = append(filteredLines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enumParamLevel > 0 {
|
||||||
|
fmt.Println("ENUM Parse error, there is a dangling '(' in your comment.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go over all the lines in this comment block
|
||||||
|
for _, line := range filteredLines {
|
||||||
|
_, trimmed := parseLinePart(line)
|
||||||
|
if trimmed != "" {
|
||||||
|
parts = append(parts, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joined := fmt.Sprintf("ENUM(%s)", strings.Join(parts, `,`))
|
||||||
|
return joined
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinePart(line string) (paramLevel int, trimmed string) {
|
||||||
|
trimmed = line
|
||||||
|
comment := ""
|
||||||
|
if idx := strings.Index(line, parseCommentPrefix); idx >= 0 {
|
||||||
|
trimmed = line[:idx]
|
||||||
|
comment = "//" + url.QueryEscape(strings.TrimSpace(line[idx+2:]))
|
||||||
|
}
|
||||||
|
trimmed = trimAllTheThings(trimmed)
|
||||||
|
trimmed += comment
|
||||||
|
opens := strings.Count(line, `(`)
|
||||||
|
closes := strings.Count(line, `)`)
|
||||||
|
if opens > 0 {
|
||||||
|
paramLevel += opens
|
||||||
|
}
|
||||||
|
if closes > 0 {
|
||||||
|
paramLevel -= closes
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// breakCommentIntoLines takes the comment and since single line comments are already broken into lines
|
||||||
|
// we break multiline comments into separate lines for processing.
|
||||||
|
func breakCommentIntoLines(comment *ast.Comment) []string {
|
||||||
|
lines := []string{}
|
||||||
|
text := comment.Text
|
||||||
|
if strings.HasPrefix(text, `/*`) {
|
||||||
|
// deal with multi line comment
|
||||||
|
multiline := strings.TrimSuffix(strings.TrimPrefix(text, `/*`), `*/`)
|
||||||
|
lines = append(lines, strings.Split(multiline, "\n")...)
|
||||||
|
} else {
|
||||||
|
lines = append(lines, strings.TrimPrefix(text, `//`))
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimAllTheThings takes off all the cruft of a line that we don't need.
|
||||||
|
// These lines should be pre-filtered so that we don't have to worry about
|
||||||
|
// the `ENUM(` prefix and the `)` suffix... those should already be removed.
|
||||||
|
func trimAllTheThings(thing string) string {
|
||||||
|
preTrimmed := strings.TrimSuffix(strings.TrimSpace(thing), `,`)
|
||||||
|
return strings.TrimSpace(preTrimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inspect will walk the ast and fill a map of names and their struct information
|
||||||
|
// for use in the generation template.
|
||||||
|
func (g *Generator) inspect(f ast.Node) map[string]*ast.TypeSpec {
|
||||||
|
enums := make(map[string]*ast.TypeSpec)
|
||||||
|
// Inspect the AST and find all structs.
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
copyGenDeclCommentsToSpecs(x)
|
||||||
|
case *ast.Ident:
|
||||||
|
if x.Obj != nil {
|
||||||
|
// fmt.Printf("Node: %#v\n", x.Obj)
|
||||||
|
// Make sure it's a Type Identifier
|
||||||
|
if x.Obj.Kind == ast.Typ {
|
||||||
|
// Make sure it's a spec (Type Identifiers can be throughout the code)
|
||||||
|
if ts, ok := x.Obj.Decl.(*ast.TypeSpec); ok {
|
||||||
|
// fmt.Printf("Type: %+v\n", ts)
|
||||||
|
isEnum := isTypeSpecEnum(ts)
|
||||||
|
// Only store documented enums
|
||||||
|
if isEnum {
|
||||||
|
// fmt.Printf("EnumType: %T\n", ts.Type)
|
||||||
|
enums[x.Name] = ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return true to continue through the tree
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return enums
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyDocsToSpecs will take the GenDecl level documents and copy them
|
||||||
|
// to the children Type and Value specs. I think this is actually working
|
||||||
|
// around a bug in the AST, but it works for now.
|
||||||
|
func copyGenDeclCommentsToSpecs(x *ast.GenDecl) {
|
||||||
|
// Copy the doc spec to the type or value spec
|
||||||
|
// cause they missed this... whoops
|
||||||
|
if x.Doc != nil {
|
||||||
|
for _, spec := range x.Specs {
|
||||||
|
switch s := spec.(type) {
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
if s.Doc == nil {
|
||||||
|
s.Doc = x.Doc
|
||||||
|
}
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
if s.Doc == nil {
|
||||||
|
s.Doc = x.Doc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTypeSpecEnum checks the comments on the type spec to determine if there is an enum
|
||||||
|
// declaration for the type.
|
||||||
|
func isTypeSpecEnum(ts *ast.TypeSpec) bool {
|
||||||
|
isEnum := false
|
||||||
|
if ts.Doc != nil {
|
||||||
|
for _, comment := range ts.Doc.List {
|
||||||
|
if strings.Contains(comment.Text, `ENUM(`) {
|
||||||
|
isEnum = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEnum
|
||||||
|
}
|
||||||
321
pkg/utils/generator/generator_1.18_test.go
Normal file
321
pkg/utils/generator/generator_1.18_test.go
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/parser"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bradleyjkemp/cupaloy/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testExampleFiles = map[string]string{
|
||||||
|
"og": `example_test.go`,
|
||||||
|
"1.18": `example_1.18_test.go`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNoStructInputFile
|
||||||
|
func Test118NoStructFile(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// Behavior
|
||||||
|
type SomeInterface interface{
|
||||||
|
|
||||||
|
}
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNoFile
|
||||||
|
func Test118NoFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
// Parse the file given in arguments
|
||||||
|
_, err := g.GenerateFromFile("")
|
||||||
|
assert.NotNil(t, err, "Error generating formatted code")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func Test118ExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithSQLDriver().
|
||||||
|
WithCaseInsensitiveParse().
|
||||||
|
WithNames().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
|
||||||
|
for name, testExample := range testExampleFiles {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFileMoreOptions
|
||||||
|
func Test118ExampleFileMoreOptions(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithSQLDriver().
|
||||||
|
WithCaseInsensitiveParse().
|
||||||
|
WithNames().
|
||||||
|
WithoutSnakeToCamel().
|
||||||
|
WithMustParse().
|
||||||
|
WithForceLower().
|
||||||
|
WithTemplates(`../example/user_template.tmpl`)
|
||||||
|
for name, testExample := range testExampleFiles {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func Test118NoPrefixExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
for name, testExample := range testExampleFiles {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func Test118NoPrefixExampleFileWithSnakeToCamel(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag()
|
||||||
|
|
||||||
|
for name, testExample := range testExampleFiles {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCustomPrefixExampleFile
|
||||||
|
func Test118CustomPrefixExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag().
|
||||||
|
WithoutSnakeToCamel().
|
||||||
|
WithPtr().
|
||||||
|
WithSQLNullInt().
|
||||||
|
WithSQLNullStr().
|
||||||
|
WithPrefix("Custom_prefix_")
|
||||||
|
for name, testExample := range testExampleFiles {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test118AliasParsing(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
input []string
|
||||||
|
resultingMap map[string]string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"no aliases": {
|
||||||
|
resultingMap: map[string]string{},
|
||||||
|
},
|
||||||
|
"multiple arrays": {
|
||||||
|
input: []string{
|
||||||
|
`!:Bang,a:a`,
|
||||||
|
`@:AT`,
|
||||||
|
`&:AND,|:OR`,
|
||||||
|
},
|
||||||
|
resultingMap: map[string]string{
|
||||||
|
"a": "a",
|
||||||
|
"!": "Bang",
|
||||||
|
"@": "AT",
|
||||||
|
"&": "AND",
|
||||||
|
"|": "OR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"more types": {
|
||||||
|
input: []string{
|
||||||
|
`*:star,+:PLUS`,
|
||||||
|
`-:less`,
|
||||||
|
`#:HASH,!:Bang`,
|
||||||
|
},
|
||||||
|
resultingMap: map[string]string{
|
||||||
|
"*": "star",
|
||||||
|
"+": "PLUS",
|
||||||
|
"-": "less",
|
||||||
|
"#": "HASH",
|
||||||
|
"!": "Bang",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
replacementNames = map[string]string{}
|
||||||
|
}()
|
||||||
|
err := ParseAliases(tc.input)
|
||||||
|
if tc.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.EqualError(t, err, tc.err.Error())
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.resultingMap, replacementNames)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEnumParseFailure
|
||||||
|
func Test118EnumParseFailure(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// Behavior
|
||||||
|
type SomeInterface interface{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENUM(
|
||||||
|
// a,
|
||||||
|
//}
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUintInvalidParsing
|
||||||
|
func Test118UintInvalidParsing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(
|
||||||
|
// a=-1,
|
||||||
|
//)
|
||||||
|
type Animal uint
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIntInvalidParsing
|
||||||
|
func Test118IntInvalidParsing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(
|
||||||
|
// a=c,
|
||||||
|
//)
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAliasing
|
||||||
|
func Test118Aliasing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(a,b,CDEF) with some extra text
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
_ = ParseAliases([]string{"CDEF:C"})
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Contains(t, string(output), "// AnimalC is a Animal of type CDEF.")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
368
pkg/utils/generator/generator_test.go
Normal file
368
pkg/utils/generator/generator_test.go
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/parser"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bradleyjkemp/cupaloy/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testExample = `example_test.go`
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestNoStructInputFile
|
||||||
|
func TestNoStructFile(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// Behavior
|
||||||
|
type SomeInterface interface{
|
||||||
|
|
||||||
|
}
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNoFile
|
||||||
|
func TestNoFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
// Parse the file given in arguments
|
||||||
|
_, err := g.GenerateFromFile("")
|
||||||
|
assert.NotNil(t, err, "Error generating formatted code")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func TestExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithSQLDriver().
|
||||||
|
WithCaseInsensitiveParse().
|
||||||
|
WithNames().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFileMoreOptions
|
||||||
|
func TestExampleFileMoreOptions(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithSQLDriver().
|
||||||
|
WithCaseInsensitiveParse().
|
||||||
|
WithNames().
|
||||||
|
WithoutSnakeToCamel().
|
||||||
|
WithMustParse().
|
||||||
|
WithForceLower().
|
||||||
|
WithTemplates(`../example/user_template.tmpl`)
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func TestNoPrefixExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func TestReplacePrefixExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithPrefix("MyPrefix_").
|
||||||
|
WithFlag().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExampleFile
|
||||||
|
func TestNoPrefixExampleFileWithSnakeToCamel(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag()
|
||||||
|
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCustomPrefixExampleFile
|
||||||
|
func TestCustomPrefixExampleFile(t *testing.T) {
|
||||||
|
g := NewGenerator().
|
||||||
|
WithMarshal().
|
||||||
|
WithLowercaseVariant().
|
||||||
|
WithNoPrefix().
|
||||||
|
WithFlag().
|
||||||
|
WithoutSnakeToCamel().
|
||||||
|
WithPtr().
|
||||||
|
WithSQLNullInt().
|
||||||
|
WithSQLNullStr().
|
||||||
|
WithPrefix("Custom_prefix_")
|
||||||
|
// Parse the file given in arguments
|
||||||
|
imported, err := g.GenerateFromFile(testExample)
|
||||||
|
require.Nil(t, err, "Error generating formatted code")
|
||||||
|
|
||||||
|
outputLines := strings.Split(string(imported), "\n")
|
||||||
|
cupaloy.SnapshotT(t, outputLines)
|
||||||
|
|
||||||
|
if false {
|
||||||
|
fmt.Println(string(imported))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasParsing(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
input []string
|
||||||
|
resultingMap map[string]string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"no aliases": {
|
||||||
|
resultingMap: map[string]string{},
|
||||||
|
},
|
||||||
|
"bad input": {
|
||||||
|
input: []string{"a:b,c"},
|
||||||
|
err: errors.New(`invalid formatted alias entry "c", must be in the format "key:value"`),
|
||||||
|
},
|
||||||
|
"multiple arrays": {
|
||||||
|
input: []string{
|
||||||
|
`!:Bang,a:a`,
|
||||||
|
`@:AT`,
|
||||||
|
`&:AND,|:OR`,
|
||||||
|
},
|
||||||
|
resultingMap: map[string]string{
|
||||||
|
"a": "a",
|
||||||
|
"!": "Bang",
|
||||||
|
"@": "AT",
|
||||||
|
"&": "AND",
|
||||||
|
"|": "OR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"more types": {
|
||||||
|
input: []string{
|
||||||
|
`*:star,+:PLUS`,
|
||||||
|
`-:less`,
|
||||||
|
`#:HASH,!:Bang`,
|
||||||
|
},
|
||||||
|
resultingMap: map[string]string{
|
||||||
|
"*": "star",
|
||||||
|
"+": "PLUS",
|
||||||
|
"-": "less",
|
||||||
|
"#": "HASH",
|
||||||
|
"!": "Bang",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
replacementNames = map[string]string{}
|
||||||
|
err := ParseAliases(tc.input)
|
||||||
|
if tc.err != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.EqualError(t, err, tc.err.Error())
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.resultingMap, replacementNames)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEnumParseFailure
|
||||||
|
func TestEnumParseFailure(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// Behavior
|
||||||
|
type SomeInterface interface{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENUM(
|
||||||
|
// a,
|
||||||
|
//}
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUintInvalidParsing
|
||||||
|
func TestUintInvalidParsing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(
|
||||||
|
// a=-1,
|
||||||
|
//)
|
||||||
|
type Animal uint
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIntInvalidParsing
|
||||||
|
func TestIntInvalidParsing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(
|
||||||
|
// a=c,
|
||||||
|
//)
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Empty(t, string(output))
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAliasing
|
||||||
|
func TestAliasing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// ENUM(a,b,CDEF) with some extra text
|
||||||
|
type Animal int
|
||||||
|
`
|
||||||
|
g := NewGenerator().
|
||||||
|
WithoutSnakeToCamel()
|
||||||
|
_ = ParseAliases([]string{"CDEF:C"})
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Contains(t, string(output), "// AnimalC is a Animal of type CDEF.")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParanthesesParsing
|
||||||
|
func TestParenthesesParsing(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// This is a pre-enum comment that needs (to be handled properly)
|
||||||
|
// ENUM(
|
||||||
|
// abc (x),
|
||||||
|
//). This is an extra string comment (With parentheses of it's own)
|
||||||
|
// And (another line) with Parentheses
|
||||||
|
type Animal string
|
||||||
|
`
|
||||||
|
g := NewGenerator()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Contains(t, string(output), "// AnimalAbcX is a Animal of type abc (x).")
|
||||||
|
assert.NotContains(t, string(output), "// AnimalAnd")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestQuotedStrings
|
||||||
|
func TestQuotedStrings(t *testing.T) {
|
||||||
|
input := `package test
|
||||||
|
// This is a pre-enum comment that needs (to be handled properly)
|
||||||
|
// ENUM(
|
||||||
|
// abc (x),
|
||||||
|
// ghi = "20",
|
||||||
|
//). This is an extra string comment (With parentheses of it's own)
|
||||||
|
// And (another line) with Parentheses
|
||||||
|
type Animal string
|
||||||
|
`
|
||||||
|
g := NewGenerator()
|
||||||
|
f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments)
|
||||||
|
assert.Nil(t, err, "Error parsing no struct input")
|
||||||
|
|
||||||
|
output, err := g.Generate(f)
|
||||||
|
assert.Nil(t, err, "Error generating formatted code")
|
||||||
|
assert.Contains(t, string(output), "// AnimalAbcX is a Animal of type abc (x).")
|
||||||
|
assert.Contains(t, string(output), "AnimalGhi Animal = \"20\"")
|
||||||
|
assert.NotContains(t, string(output), "// AnimalAnd")
|
||||||
|
if false { // Debugging statement
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
126
pkg/utils/generator/template_funcs.go
Normal file
126
pkg/utils/generator/template_funcs.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stringify returns a string that is all of the enum value names concatenated without a separator
|
||||||
|
func Stringify(e Enum, forceLower bool) (ret string, err error) {
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
next := val.RawName
|
||||||
|
if forceLower {
|
||||||
|
next = strings.ToLower(next)
|
||||||
|
}
|
||||||
|
ret = ret + next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapify returns a map that is all of the indexes for a string value lookup
|
||||||
|
func Mapify(e Enum) (ret string, err error) {
|
||||||
|
strName := fmt.Sprintf(`_%sName`, e.Name)
|
||||||
|
ret = fmt.Sprintf("map[%s]string{\n", e.Name)
|
||||||
|
index := 0
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
nextIndex := index + len(val.Name)
|
||||||
|
ret = fmt.Sprintf("%s%s: %s[%d:%d],\n", ret, val.PrefixedName, strName, index, nextIndex)
|
||||||
|
index = nextIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = ret + `}`
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmapify returns a map that is all of the indexes for a string value lookup
|
||||||
|
func Unmapify(e Enum, lowercase bool) (ret string, err error) {
|
||||||
|
if e.Type == "string" {
|
||||||
|
return UnmapifyStringEnum(e, lowercase)
|
||||||
|
}
|
||||||
|
strName := fmt.Sprintf(`_%sName`, e.Name)
|
||||||
|
ret = fmt.Sprintf("map[string]%s{\n", e.Name)
|
||||||
|
index := 0
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
nextIndex := index + len(val.Name)
|
||||||
|
ret = fmt.Sprintf("%s%s[%d:%d]: %s,\n", ret, strName, index, nextIndex, val.PrefixedName)
|
||||||
|
if lowercase {
|
||||||
|
ret = fmt.Sprintf("%sstrings.ToLower(%s[%d:%d]): %s,\n", ret, strName, index, nextIndex, val.PrefixedName)
|
||||||
|
}
|
||||||
|
index = nextIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = ret + `}`
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmapify returns a map that is all of the indexes for a string value lookup
|
||||||
|
func UnmapifyStringEnum(e Enum, lowercase bool) (ret string, err error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
_, err = builder.WriteString("map[string]" + e.Name + "{\n")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
_, err = builder.WriteString(fmt.Sprintf("%q:%s,\n", val.ValueStr, val.PrefixedName))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lowercase && strings.ToLower(val.ValueStr) != val.ValueStr {
|
||||||
|
_, err = builder.WriteString(fmt.Sprintf("%q:%s,\n", strings.ToLower(val.ValueStr), val.PrefixedName))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.WriteByte('}')
|
||||||
|
ret = builder.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namify returns a slice that is all of the possible names for an enum in a slice
|
||||||
|
func Namify(e Enum) (ret string, err error) {
|
||||||
|
if e.Type == "string" {
|
||||||
|
return namifyStringEnum(e)
|
||||||
|
}
|
||||||
|
strName := fmt.Sprintf(`_%sName`, e.Name)
|
||||||
|
ret = "[]string{\n"
|
||||||
|
index := 0
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
nextIndex := index + len(val.Name)
|
||||||
|
ret = fmt.Sprintf("%s%s[%d:%d],\n", ret, strName, index, nextIndex)
|
||||||
|
index = nextIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = ret + "}"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namify returns a slice that is all of the possible names for an enum in a slice
|
||||||
|
func namifyStringEnum(e Enum) (ret string, err error) {
|
||||||
|
ret = "[]string{\n"
|
||||||
|
for _, val := range e.Values {
|
||||||
|
if val.Name != skipHolder {
|
||||||
|
ret = fmt.Sprintf("%sstring(%s),\n", ret, val.PrefixedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = ret + "}"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Offset(index int, enumType string, val EnumValue) (strResult string) {
|
||||||
|
if strings.HasPrefix(enumType, "u") {
|
||||||
|
// Unsigned
|
||||||
|
return strconv.FormatUint(val.ValueInt.(uint64)-uint64(index), 10)
|
||||||
|
} else {
|
||||||
|
// Signed
|
||||||
|
return strconv.FormatInt(val.ValueInt.(int64)-int64(index), 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user