Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add -casesensitive flag #96

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions enumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ func %[1]sString(s string) (%[1]s, error) {
}
`

// Arguments to format are:
// [1]: type name
const stringNameToValueMethodCaseSensitive = `// %[1]sString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func %[1]sString(s string) (%[1]s, error) {
if val, ok := _%[1]sNameToValueMap[s]; ok {
return val, nil
}
return 0, fmt.Errorf("%%s does not belong to %[1]s values", s)
}
`

// Arguments to format are:
// [1]: type name
const stringValuesMethod = `// %[1]sValues returns all values of the enum
Expand Down Expand Up @@ -70,7 +82,7 @@ func (g *Generator) buildAltStringValuesMethod(typeName string) {
g.Printf(altStringValuesMethod, typeName)
}

func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThreshold int) {
func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThreshold int, caseSensitive bool) {
// At this moment, either "g.declareIndexAndNameVars()" or "g.declareNameVars()" has been called

// Print the slice of values
Expand All @@ -83,13 +95,17 @@ func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThresh
g.Printf("}\n\n")

// Print the map between name and value
g.printValueMap(runs, typeName, runsThreshold)
g.printValueMap(runs, typeName, runsThreshold, caseSensitive)

// Print the slice of names
g.printNamesSlice(runs, typeName, runsThreshold)

// Print the basic extra methods
g.Printf(stringNameToValueMethod, typeName)
if caseSensitive {
g.Printf(stringNameToValueMethodCaseSensitive, typeName)
} else {
g.Printf(stringNameToValueMethod, typeName)
}
g.Printf(stringValuesMethod, typeName)
g.Printf(stringsMethod, typeName)
if len(runs) <= runsThreshold {
Expand All @@ -99,7 +115,7 @@ func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThresh
}
}

func (g *Generator) printValueMap(runs [][]Value, typeName string, runsThreshold int) {
func (g *Generator) printValueMap(runs [][]Value, typeName string, runsThreshold int, caseSensitive bool) {
thereAreRuns := len(runs) > 1 && len(runs) <= runsThreshold
g.Printf("\nvar _%sNameToValueMap = map[string]%s{\n", typeName, typeName)

Expand All @@ -115,7 +131,9 @@ func (g *Generator) printValueMap(runs [][]Value, typeName string, runsThreshold

for _, value := range values {
g.Printf("\t_%sName%s[%d:%d]: %s,\n", typeName, runID, n, n+len(value.name), value.originalName)
g.Printf("\t_%sLowerName%s[%d:%d]: %s,\n", typeName, runID, n, n+len(value.name), value.originalName)
if !caseSensitive {
g.Printf("\t_%sLowerName%s[%d:%d]: %s,\n", typeName, runID, n, n+len(value.name), value.originalName)
}
n += len(value.name)
}
}
Expand Down
28 changes: 14 additions & 14 deletions golden_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,45 +315,45 @@ 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, "", "")
}
}

func runGoldenTest(t *testing.T, test Golden,
generateJSON, generateYAML, generateSQL, generateText, linecomment, generateGQLGen, generateValuesMethod bool,
generateJSON, generateYAML, generateSQL, generateText, linecomment, generateGQLGen, generateValuesMethod, caseSensitive bool,
trimPrefix string, prefix string) {

var g Generator
Expand Down Expand Up @@ -382,7 +382,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, caseSensitive)
got := string(g.format())
if got != loadGolden(test.name) {
// Use this to help build a golden text when changes are needed
Expand Down
60 changes: 35 additions & 25 deletions stringer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var (
trimPrefix = flag.String("trimprefix", "", "transform each item name by removing a prefix or comma separated list of prefixes. Default: \"\"")
addPrefix = flag.String("addprefix", "", "transform each item name by adding a prefix. Default: \"\"")
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
caseSensitive = flag.Bool("casesensitive", false, "preserve case when creating a value from a string")
)

var comments arrayFlags
Expand Down Expand Up @@ -120,7 +121,9 @@ func main() {
g.Printf("\n")
g.Printf("import (\n")
g.Printf("\t\"fmt\"\n")
g.Printf("\t\"strings\"\n")
if !*caseSensitive {
g.Printf("\t\"strings\"\n")
}
if *sql {
g.Printf("\t\"database/sql/driver\"\n")
}
Expand All @@ -135,7 +138,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, *caseSensitive)
}

// Format the output.
Expand Down Expand Up @@ -415,7 +418,8 @@ 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 bool, includeValuesMethod bool,
caseSensitive bool) {
values := make([]Value, 0, 100)
for _, file := range g.pkg.files {
file.lineComment = lineComment
Expand Down Expand Up @@ -456,19 +460,19 @@ func (g *Generator) generate(typeName string,
const runsThreshold = 10
switch {
case len(runs) == 1:
g.buildOneRun(runs, typeName)
g.buildOneRun(runs, typeName, caseSensitive)
case len(runs) <= runsThreshold:
g.buildMultipleRuns(runs, typeName)
g.buildMultipleRuns(runs, typeName, caseSensitive)
default:
g.buildMap(runs, typeName)
g.buildMap(runs, typeName, caseSensitive)
}
if includeValuesMethod {
g.buildAltStringValuesMethod(typeName)
}

g.buildNoOpOrderChangeDetect(runs, typeName)

g.buildBasicExtras(runs, typeName, runsThreshold)
g.buildBasicExtras(runs, typeName, runsThreshold, caseSensitive)
if includeJSON {
g.buildJSONMethods(runs, typeName, runsThreshold)
}
Expand Down Expand Up @@ -663,14 +667,16 @@ func usize(n int) int {

// declareIndexAndNameVars declares the index slices and concatenated names
// strings representing the runs of values.
func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string, caseSensitive bool) {
var indexes, names []string
for i, run := range runs {
index, n := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
indexes = append(indexes, index)
names = append(names, n)
_, n = g.createLowerIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
names = append(names, n)
if !caseSensitive {
_, n = g.createLowerIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
names = append(names, n)
}
}
g.Printf("const (\n")
for _, n := range names {
Expand All @@ -685,12 +691,14 @@ func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
}

// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
func (g *Generator) declareIndexAndNameVar(run []Value, typeName string, caseSensitive bool) {
index, n := g.createIndexAndNameDecl(run, typeName, "")
g.Printf("const %s\n", n)
g.Printf("var %s\n", index)
index, n = g.createLowerIndexAndNameDecl(run, typeName, "")
g.Printf("const %s\n", n)
if !caseSensitive {
index, n = g.createLowerIndexAndNameDecl(run, typeName, "")
g.Printf("const %s\n", n)
}
//g.Printf("var %s\n", index)
}

Expand Down Expand Up @@ -739,28 +747,30 @@ func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix
}

// declareNameVars declares the concatenated names string representing all the values in the runs.
func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string, caseSensitive bool) {
g.Printf("const _%sName%s = \"", typeName, suffix)
for _, run := range runs {
for i := range run {
g.Printf("%s", run[i].name)
}
}
g.Printf("\"\n")
g.Printf("const _%sLowerName%s = \"", typeName, suffix)
for _, run := range runs {
for i := range run {
g.Printf("%s", strings.ToLower(run[i].name))
if !caseSensitive {
g.Printf("const _%sLowerName%s = \"", typeName, suffix)
for _, run := range runs {
for i := range run {
g.Printf("%s", strings.ToLower(run[i].name))
}
}
g.Printf("\"\n")
}
g.Printf("\"\n")
}

// buildOneRun generates the variables and String method for a single run of contiguous values.
func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
func (g *Generator) buildOneRun(runs [][]Value, typeName string, caseSensitive bool) {
values := runs[0]
g.Printf("\n")
g.declareIndexAndNameVar(values, typeName)
g.declareIndexAndNameVar(values, typeName, caseSensitive)
// The generated code is simple enough to write as a Printf format.
lessThanZero := ""
if values[0].signed {
Expand Down Expand Up @@ -801,9 +811,9 @@ const stringOneRunWithOffset = `func (i %[1]s) String() string {

// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
// For this pattern, a single Printf format won't do.
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string, caseSensitive bool) {
g.Printf("\n")
g.declareIndexAndNameVars(runs, typeName)
g.declareIndexAndNameVars(runs, typeName, caseSensitive)
g.Printf("func (i %s) String() string {\n", typeName)
g.Printf("\tswitch {\n")
for i, values := range runs {
Expand All @@ -827,9 +837,9 @@ func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {

// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
// It's a rare situation but has simple code.
func (g *Generator) buildMap(runs [][]Value, typeName string) {
func (g *Generator) buildMap(runs [][]Value, typeName string, caseSensitive bool) {
g.Printf("\n")
g.declareNameVars(runs, typeName, "")
g.declareNameVars(runs, typeName, "", caseSensitive)
g.Printf("\nvar _%sMap = map[%s]string{\n", typeName, typeName)
n := 0
for _, values := range runs {
Expand Down