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

Option to add a ToString interface where numeric-based strings are generated #86

Open
wants to merge 1 commit 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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Flags:
if true, json marshaling methods will be generated. Default: false
-linecomment
use line comment text as printed text when present
-tostring
if true, ToString function that returns an number-based string will be generated. Default: false
-output string
output file name; default srcdir/<type>_string.go
-sql
Expand Down Expand Up @@ -71,6 +73,9 @@ When Enumer is applied to a type, it will generate:
the enum conform to the `gopkg.in/yaml.v2.Marshaler` and `gopkg.in/yaml.v2.Unmarshaler` interfaces.
- When the flag `sql` is provided, the methods for implementing the `Scanner` and `Valuer` interfaces.
Useful when storing the enum in a database.
- When the flag `tostring` is provided, the methods for implementing the `ToString` interfaces.
the `ToString` interface, Instead of an enumerated string, get a defined number-based string.



For example, if we have an enum type called `Pill`,
Expand Down
40 changes: 26 additions & 14 deletions golden_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ var golden = []Golden{
{"prime", primeIn},
}

var goldenToString = []Golden{
{"tostring_day", dayIn},
{"tostring_offset", offsetIn},
{"tostring_gap", gapIn},
{"tostring_num", numIn},
{"tostring_unum", unumIn},
{"tostring_prime", primeIn},
}

var goldenJSON = []Golden{
{"primeJson", primeJsonIn},
}
Expand Down Expand Up @@ -315,45 +324,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 goldenToString {
runGoldenTest(t, test, false, false, false, false, false, false, false, true, "", "")
}
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, toString bool,
trimPrefix string, prefix string) {

var g Generator
Expand Down Expand Up @@ -382,7 +394,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, toString)
got := string(g.format())
if got != loadGolden(test.name) {
// Use this to help build a golden text when changes are needed
Expand Down
40 changes: 31 additions & 9 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. 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")
tostring = flag.Bool("tostring", false, "if true, ToString function that returns an number-based string will be generated. Default: false")
)

var comments arrayFlags
Expand Down Expand Up @@ -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, *tostring)
}

// Format the output.
Expand Down Expand Up @@ -415,7 +416,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,
toString bool) {
values := make([]Value, 0, 100)
for _, file := range g.pkg.files {
file.lineComment = lineComment
Expand Down Expand Up @@ -465,6 +467,9 @@ func (g *Generator) generate(typeName string,
if includeValuesMethod {
g.buildAltStringValuesMethod(typeName)
}
if toString {
g.buildToStringMethod(typeName)
}

g.buildNoOpOrderChangeDetect(runs, typeName)

Expand Down Expand Up @@ -774,9 +779,10 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
}

// Arguments to format are:
// [1]: type name
// [2]: size of index element (8 for uint8 etc.)
// [3]: less than zero check (for signed types)
//
// [1]: type name
// [2]: size of index element (8 for uint8 etc.)
// [3]: less than zero check (for signed types)
const stringOneRun = `func (i %[1]s) String() string {
if %[3]si >= %[1]s(len(_%[1]sIndex)-1) {
return fmt.Sprintf("%[1]s(%%d)", i)
Expand All @@ -786,10 +792,11 @@ const stringOneRun = `func (i %[1]s) String() string {
`

// Arguments to format are:
// [1]: type name
// [2]: lowest defined value for type, as a string
// [3]: size of index element (8 for uint8 etc.)
// [4]: less than zero check (for signed types)
//
// [1]: type name
// [2]: lowest defined value for type, as a string
// [3]: size of index element (8 for uint8 etc.)
// [4]: less than zero check (for signed types)
const stringOneRunWithOffset = `func (i %[1]s) String() string {
i -= %[2]s
if %[4]si >= %[1]s(len(_%[1]sIndex)-1) {
Expand Down Expand Up @@ -868,3 +875,18 @@ const stringMap = `func (i %[1]s) String() string {
return fmt.Sprintf("%[1]s(%%d)", i)
}
`

func (g *Generator) buildToStringMethod(typeName string) {
g.Printf(toStringFromInt, typeName)
}

// Arguments to format are:
//
// [1]: type name
// [2]: size of index element (8 for uint8 etc.)
// [3]: less than zero check (for signed types)
const toStringFromInt = `// ToString converts %[1]s to an int-based string and returns it.
func (i %[1]s) ToString() string {
return strconv.Itoa(int(i))
}
`
95 changes: 95 additions & 0 deletions testdata/tostring_day.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

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]]
}

// ToString converts Day to an int-based string and returns it.
func (i Day) ToString() string {
return strconv.Itoa(int(i))
}

// 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
}
Loading