From 1614999bcc5dd68e60b8ca28000e381f804fc6d8 Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Wed, 15 Jan 2025 21:36:04 +0100 Subject: [PATCH 1/2] port code to a new fork --- enumer.go | 15 ++++++ golden_test.go | 35 ++++++++------ stringer.go | 10 +++- testdata/validate.golden | 98 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 testdata/validate.golden diff --git a/enumer.go b/enumer.go index c234209..26ab5c4 100644 --- a/enumer.go +++ b/enumer.go @@ -65,6 +65,17 @@ const altStringValuesMethod = `func (%[1]s) Values() []string { } ` +// Arguments to format are: +// [1]: type name +const validateMethod = `// Validate returns an error if the value is not listed in the enum definition. +func (i %[1]s) Validate() error { + if !i.IsA%[1]s() { + return fmt.Errorf("%%v is not a valid value for %[1]s values", i) + } + return nil +} +` + func (g *Generator) buildAltStringValuesMethod(typeName string) { g.Printf("\n") g.Printf(altStringValuesMethod, typeName) @@ -212,3 +223,7 @@ func (i *%[1]s) UnmarshalYAML(unmarshal func(interface{}) error) error { func (g *Generator) buildYAMLMethods(runs [][]Value, typeName string, runsThreshold int) { g.Printf(yamlMethods, typeName) } + +func (g *Generator) buildValidateMethod(typeName string) { + g.Printf(validateMethod, typeName) +} diff --git a/golden_test.go b/golden_test.go index 03479d2..b12c0ec 100644 --- a/golden_test.go +++ b/golden_test.go @@ -76,6 +76,10 @@ var goldenLinecomment = []Golden{ {"dayWithLinecomment", linecommentIn}, } +var goldenValidateMethod = []Golden{ + {name: "validate", input: dayIn}, +} + // Each example starts with "type XXX [u]int", with a single space separating them. // Simple test: enumeration of type int starting at 0. @@ -315,45 +319,48 @@ const ( func TestGolden(t *testing.T) { for _, test := range golden { - runGoldenTest(t, test, false, false, false, false, false, false, true, "", "") + runGoldenTest(t, test, false, false, false, false, false, false, true, false, "", "") } for _, test := range goldenJSON { - runGoldenTest(t, test, true, false, false, false, false, false, false, "", "") + runGoldenTest(t, test, true, false, false, false, false, false, false, false, "", "") } for _, test := range goldenText { - runGoldenTest(t, test, false, false, false, true, false, false, false, "", "") + runGoldenTest(t, test, false, false, false, true, false, false, false, false, "", "") } for _, test := range goldenYAML { - runGoldenTest(t, test, false, true, false, false, false, false, false, "", "") + runGoldenTest(t, test, false, true, false, false, false, false, false, false, "", "") } for _, test := range goldenSQL { - runGoldenTest(t, test, false, false, true, false, false, false, false, "", "") + runGoldenTest(t, test, false, false, true, false, false, false, false, false, "", "") } for _, test := range goldenJSONAndSQL { - runGoldenTest(t, test, true, false, true, false, false, false, false, "", "") + runGoldenTest(t, test, true, false, true, false, false, false, false, false, "", "") } for _, test := range goldenGQLGen { - runGoldenTest(t, test, false, false, false, false, false, true, false, "", "") + runGoldenTest(t, test, false, false, false, false, false, true, false, false, "", "") } for _, test := range goldenTrimPrefix { - runGoldenTest(t, test, false, false, false, false, false, false, false, "Day", "") + runGoldenTest(t, test, false, false, false, false, false, false, false, false, "Day", "") } for _, test := range goldenTrimPrefixMultiple { - runGoldenTest(t, test, false, false, false, false, false, false, false, "Day,Night", "") + runGoldenTest(t, test, false, false, false, false, false, false, false, false, "Day,Night", "") } for _, test := range goldenWithPrefix { - runGoldenTest(t, test, false, false, false, false, false, false, false, "", "Day") + runGoldenTest(t, test, false, false, false, false, false, false, false, false, "", "Day") } for _, test := range goldenTrimAndAddPrefix { - runGoldenTest(t, test, false, false, false, false, false, false, false, "Day", "Night") + runGoldenTest(t, test, false, false, false, false, false, false, false, false, "Day", "Night") } for _, test := range goldenLinecomment { - runGoldenTest(t, test, false, false, false, false, true, false, false, "", "") + runGoldenTest(t, test, false, false, false, false, true, false, false, false, "", "") + } + for _, test := range goldenValidateMethod { + runGoldenTest(t, test, false, false, false, false, false, false, false, true, "", "") } } func runGoldenTest(t *testing.T, test Golden, - generateJSON, generateYAML, generateSQL, generateText, linecomment, generateGQLGen, generateValuesMethod bool, + generateJSON, generateYAML, generateSQL, generateText, linecomment, generateGQLGen, generateValuesMethod, generateIsValidMethod bool, trimPrefix string, prefix string) { var g Generator @@ -382,7 +389,7 @@ func runGoldenTest(t *testing.T, test Golden, if len(tokens) != 3 { t.Fatalf("%s: need type declaration on first line", test.name) } - g.generate(tokens[1], generateJSON, generateYAML, generateSQL, generateText, generateGQLGen, "noop", trimPrefix, prefix, linecomment, generateValuesMethod) + g.generate(tokens[1], generateJSON, generateYAML, generateSQL, generateText, generateGQLGen, "noop", trimPrefix, prefix, linecomment, generateValuesMethod, generateIsValidMethod) got := string(g.format()) if got != loadGolden(test.name) { // Use this to help build a golden text when changes are needed diff --git a/stringer.go b/stringer.go index 33d3d09..74e5ee6 100644 --- a/stringer.go +++ b/stringer.go @@ -51,6 +51,7 @@ var ( text = flag.Bool("text", false, "if true, text marshaling methods will be generated. Default: false") gqlgen = flag.Bool("gqlgen", false, "if true, GraphQL marshaling methods for gqlgen will be generated. Default: false") altValuesFunc = flag.Bool("values", false, "if true, alternative string values method will be generated. Default: false") + validateFunc = flag.Bool("validate", false, "if true, a `Validate() error` method will be generated. Default: false") output = flag.String("output", "", "output file name; default srcdir/_string.go") transformMethod = flag.String("transform", "noop", "enum item name transformation method. Default: noop") trimPrefix = flag.String("trimprefix", "", "transform each item name by removing a prefix or comma separated list of prefixes. Default: \"\"") @@ -135,7 +136,7 @@ func main() { // Run generate for each type. for _, typeName := range typs { - g.generate(typeName, *json, *yaml, *sql, *text, *gqlgen, *transformMethod, *trimPrefix, *addPrefix, *linecomment, *altValuesFunc) + g.generate(typeName, *json, *yaml, *sql, *text, *gqlgen, *transformMethod, *trimPrefix, *addPrefix, *linecomment, *altValuesFunc, *validateFunc) } // Format the output. @@ -415,7 +416,9 @@ func (g *Generator) prefixValueNames(values []Value, prefix string) { // generate produces the String method for the named type. func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeSQL, includeText, includeGQLGen bool, - transformMethod string, trimPrefix string, addPrefix string, lineComment bool, includeValuesMethod bool) { + transformMethod string, trimPrefix string, addPrefix string, + lineComment, includeValuesMethod, includeValidateFunc bool, +) { values := make([]Value, 0, 100) for _, file := range g.pkg.files { file.lineComment = lineComment @@ -465,6 +468,9 @@ func (g *Generator) generate(typeName string, if includeValuesMethod { g.buildAltStringValuesMethod(typeName) } + if includeValidateFunc { + g.buildValidateMethod(typeName) + } g.buildNoOpOrderChangeDetect(runs, typeName) diff --git a/testdata/validate.golden b/testdata/validate.golden new file mode 100644 index 0000000..3f70b72 --- /dev/null +++ b/testdata/validate.golden @@ -0,0 +1,98 @@ + +const _DayName = "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" + +var _DayIndex = [...]uint8{0, 6, 13, 22, 30, 36, 44, 50} + +const _DayLowerName = "mondaytuesdaywednesdaythursdayfridaysaturdaysunday" + +func (i Day) String() string { + if i < 0 || i >= Day(len(_DayIndex)-1) { + return fmt.Sprintf("Day(%d)", i) + } + return _DayName[_DayIndex[i]:_DayIndex[i+1]] +} + +// Validate returns an error if the value is not listed in the enum definition. +func (i Day) Validate() error { + if !i.IsADay() { + return fmt.Errorf("%v is not a valid value for Day values", i) + } + return nil +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _DayNoOp() { + var x [1]struct{} + _ = x[Monday-(0)] + _ = x[Tuesday-(1)] + _ = x[Wednesday-(2)] + _ = x[Thursday-(3)] + _ = x[Friday-(4)] + _ = x[Saturday-(5)] + _ = x[Sunday-(6)] +} + +var _DayValues = []Day{Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday} + +var _DayNameToValueMap = map[string]Day{ + _DayName[0:6]: Monday, + _DayLowerName[0:6]: Monday, + _DayName[6:13]: Tuesday, + _DayLowerName[6:13]: Tuesday, + _DayName[13:22]: Wednesday, + _DayLowerName[13:22]: Wednesday, + _DayName[22:30]: Thursday, + _DayLowerName[22:30]: Thursday, + _DayName[30:36]: Friday, + _DayLowerName[30:36]: Friday, + _DayName[36:44]: Saturday, + _DayLowerName[36:44]: Saturday, + _DayName[44:50]: Sunday, + _DayLowerName[44:50]: Sunday, +} + +var _DayNames = []string{ + _DayName[0:6], + _DayName[6:13], + _DayName[13:22], + _DayName[22:30], + _DayName[30:36], + _DayName[36:44], + _DayName[44:50], +} + +// DayString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func DayString(s string) (Day, error) { + if val, ok := _DayNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _DayNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Day values", s) +} + +// DayValues returns all values of the enum +func DayValues() []Day { + return _DayValues +} + +// DayStrings returns a slice of all String values of the enum +func DayStrings() []string { + strs := make([]string, len(_DayNames)) + copy(strs, _DayNames) + return strs +} + +// IsADay returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Day) IsADay() bool { + for _, v := range _DayValues { + if i == v { + return true + } + } + return false +} From cd189bfbdfb2f222eaac1a20992b6d7d0953e11f Mon Sep 17 00:00:00 2001 From: Tiago Peczenyj Date: Sat, 16 Aug 2025 10:57:48 +0200 Subject: [PATCH 2/2] fix style --- enumer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/enumer.go b/enumer.go index 376fd98..18f48d9 100644 --- a/enumer.go +++ b/enumer.go @@ -59,8 +59,7 @@ const altStringValuesMethod = `func (%[1]s) Values() []string { } ` -// Arguments to format are: -// [1]: type name +// Arguments to format are: [1]: type name const validateMethod = `// Validate returns an error if the value is not listed in the enum definition. func (i %[1]s) Validate() error { if !i.IsA%[1]s() {