Merge pull request #5 from dmarkham/serjlee

Serjlee
This commit is contained in:
Dan Markham 2019-04-04 17:15:40 -07:00 committed by GitHub
commit 72a8443d38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 839 additions and 83 deletions

View File

@ -1,32 +1,36 @@
# Enumer [![GoDoc](https://godoc.org/github.com/alvaroloes/enumer?status.svg)](https://godoc.org/github.com/alvaroloes/enumer) [![Go Report Card](https://goreportcard.com/badge/github.com/alvaroloes/enumer)](https://goreportcard.com/report/github.com/alvaroloes/enumer) [![cover.run go](https://cover.run/go/github.com/alvaroloes/enumer.svg?tag=golang-1.10)](https://cover.run/go/github.com/alvaroloes/enumer?tag=golang-1.10) # Enumer [![GoDoc](https://godoc.org/github.com/alvaroloes/enumer?status.svg)](https://godoc.org/github.com/alvaroloes/enumer) [![Go Report Card](https://goreportcard.com/badge/github.com/alvaroloes/enumer)](https://goreportcard.com/report/github.com/alvaroloes/enumer) [![cover.run go](https://cover.run/go/github.com/alvaroloes/enumer.svg?tag=golang-1.10)](https://cover.run/go/github.com/alvaroloes/enumer?tag=golang-1.10)
Enumer is a tool to generate Go code that adds useful methods to Go enums (constants with a specific type). Enumer is a tool to generate Go code that adds useful methods to Go enums (constants with a specific type).
It started as a fork of [Rob Pikes Stringer tool](https://godoc.org/golang.org/x/tools/cmd/stringer). It started as a fork of [Rob Pikes Stringer tool](https://godoc.org/golang.org/x/tools/cmd/stringer).
## Generated functions and methods ## Generated functions and methods
When Enumer is applied to a type, it will generate: When Enumer is applied to a type, it will generate:
* The following basic methods/functions: - The following basic methods/functions:
* Method `String()`: returns the string representation of the enum value. This makes the enum conform - Method `String()`: returns the string representation of the enum value. This makes the enum conform
the `Stringer` interface, so whenever you print an enum value, you'll get the string name instead of a number. the `Stringer` interface, so whenever you print an enum value, you'll get the string name instead of a number.
* Function `<Type>String(s string)`: returns the enum value from its string representation. This is useful - Function `<Type>String(s string)`: returns the enum value from its string representation. This is useful
when you need to read enum values from command line arguments, from a configuration file, or when you need to read enum values from command line arguments, from a configuration file, or
from a REST API request... In short, from those places where using the real enum value (an integer) would from a REST API request... In short, from those places where using the real enum value (an integer) would
be almost meaningless or hard to trace or use by a human. be almost meaningless or hard to trace or use by a human.
* Function `<Type>Values()`: returns a slice with all the values of the enum - Function `<Type>Values()`: returns a slice with all the values of the enum
* Method `IsA<Type>()`: returns true only if the current value is among the values of the enum. Useful for validations. - Method `IsA<Type>()`: returns true only if the current value is among the values of the enum. Useful for validations.
* When the flag `json` is provided, two additional methods will be generated, `MarshalJSON()` and `UnmarshalJSON()`. These make
- When the flag `json` is provided, two additional methods will be generated, `MarshalJSON()` and `UnmarshalJSON()`. These make
the enum conform to the `json.Marshaler` and `json.Unmarshaler` interfaces. Very useful to use it in JSON APIs. the enum conform to the `json.Marshaler` and `json.Unmarshaler` interfaces. Very useful to use it in JSON APIs.
* When the flag `text` is provided, two additional methods will be generated, `MarshalText()` and `UnmarshalText()`. These make - When the flag `text` is provided, two additional methods will be generated, `MarshalText()` and `UnmarshalText()`. These make
the enum conform to the `encoding.TextMarshaler` and `encoding.TextUnmarshaler` interfaces. the enum conform to the `encoding.TextMarshaler` and `encoding.TextUnmarshaler` interfaces.
**Note:** If you use your enum values as keys in a map and you encode the map as _JSON_, you need this flag set to true to properly **Note:** If you use your enum values as keys in a map and you encode the map as _JSON_, you need this flag set to true to properly
convert the map keys to json (strings). If not, the numeric values will be used instead convert the map keys to json (strings). If not, the numeric values will be used instead
* When the flag `yaml` is provided, two additional methods will be generated, `MarshalYAML()` and `UnmarshalYAML()`. These make - When the flag `yaml` is provided, two additional methods will be generated, `MarshalYAML()` and `UnmarshalYAML()`. These make
the enum conform to the `gopkg.in/yaml.v2.Marshaler` and `gopkg.in/yaml.v2.Unmarshaler` interfaces. 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 will be also generated. - When the flag `sql` is provided, the methods for implementing the Scanner and Valuer interfaces will be also generated.
Useful when storing the enum in a database. Useful when storing the enum in a database.
For example, if we have an enum type called `Pill`, For example, if we have an enum type called `Pill`,
```go ```go
type Pill int type Pill int
@ -38,7 +42,9 @@ const (
Acetaminophen = Paracetamol Acetaminophen = Paracetamol
) )
``` ```
executing `enumer -type=Pill -json` will generate a new file with four basic methods and two extra for JSON: executing `enumer -type=Pill -json` will generate a new file with four basic methods and two extra for JSON:
```go ```go
func (i Pill) String() string { func (i Pill) String() string {
//... //...
@ -64,7 +70,9 @@ func (i *Pill) UnmarshalJSON(data []byte) error {
//... //...
} }
``` ```
From now on, we can: From now on, we can:
```go ```go
// Convert any Pill value to string // Convert any Pill value to string
var aspirinString string = Aspirin.String() var aspirinString string = Aspirin.String()
@ -119,15 +127,30 @@ For example, the command `enumer -type=MyType -json -transform=snake` would gene
```go ```go
name := MyTypeValue.String() // name => "my_type_value" name := MyTypeValue.String() // name => "my_type_value"
``` ```
**Note**: The transformation only works form CamelCase to snake_case or kebab-case, not the other way around. **Note**: The transformation only works form CamelCase to snake_case or kebab-case, not the other way around.
### Transformers
- snake
- snake-upper
- kebab
- kebab-upper
- lower (lowercase)
- upper (UPPERCASE)
- title (TitleCase)
- title-lower (titleCase)
- first (Use first character of string)
- first-lower (same as first only lower case)
- first-upper (same as first only upper case)
## How to use ## How to use
The usage of Enumer is the same as Stringer, so you can refer to the [Stringer docs](https://godoc.org/golang.org/x/tools/cmd/stringer) The usage of Enumer is the same as Stringer, so you can refer to the [Stringer docs](https://godoc.org/golang.org/x/tools/cmd/stringer)
for more information. for more information.
There are four boolean flags: `json`, `text`, `yaml` and `sql`. You can use any combination of them (i.e. `enumer -type=Pill -json -text`), There are four boolean flags: `json`, `text`, `yaml` and `sql`. You can use any combination of them (i.e. `enumer -type=Pill -json -text`),
For enum string representation transformation the `transform` and `trimprefix` flags For enum string representation transformation the `transform` and `trimprefix` flags
were added (i.e. `enumer -type=MyType -json -transform=snake`). were added (i.e. `enumer -type=MyType -json -transform=snake`).
Possible transform values are `snake` and `kebab` for transformation to snake_case and kebab-case accordingly. Possible transform values are `snake` and `kebab` for transformation to snake_case and kebab-case accordingly.
@ -136,6 +159,9 @@ The default value for `transform` flag is `noop` which means no transformation w
If a prefix is provided via the `trimprefix` flag, it will be trimmed from the start of each name (before If a prefix is provided via the `trimprefix` flag, it will be trimmed from the start of each name (before
it is transformed). If a name doesn't have the prefix it will be passed unchanged. it is transformed). If a name doesn't have the prefix it will be passed unchanged.
If a prefix is provided via the `addprefix` flag, it will be added to the start of each name (after trimming and after transforming).
## Inspiring projects ## Inspiring projects
* [Stringer](https://godoc.org/golang.org/x/tools/cmd/stringer)
* [jsonenums](https://github.com/campoy/jsonenums) - [Stringer](https://godoc.org/golang.org/x/tools/cmd/stringer)
- [jsonenums](https://github.com/campoy/jsonenums)

View File

@ -72,13 +72,45 @@ func TestEndToEnd(t *testing.T) {
t.Logf("cgo is no enabled for %s", name) t.Logf("cgo is no enabled for %s", name)
continue continue
} }
// Names are known to be ASCII and long enough.
typeName := fmt.Sprintf("%c%s", name[0]+'A'-'a', name[1:len(name)-len(".go")])
transformNameMethod := "noop"
if name == "transform.go" { // Names are known to be ASCII and long enough.
typeName = "CamelCaseValue" var typeName string
var transformNameMethod string
switch name {
case "transform_snake.go":
typeName = "SnakeCaseValue"
transformNameMethod = "snake" transformNameMethod = "snake"
case "transform_snake_upper.go":
typeName = "SnakeUpperCaseValue"
transformNameMethod = "snake-upper"
case "transform_kebab.go":
typeName = "KebabCaseValue"
transformNameMethod = "kebab"
case "transform_kebab_upper.go":
typeName = "KebabUpperCaseValue"
transformNameMethod = "kebab-upper"
case "transform_upper.go":
typeName = "UpperCaseValue"
transformNameMethod = "upper"
case "transform_lower.go":
typeName = "LowerCaseValue"
transformNameMethod = "lower"
case "transform_title.go":
typeName = "TitleCaseValue"
transformNameMethod = "title"
case "transform_first.go":
typeName = "FirstCaseValue"
transformNameMethod = "first"
case "transform_first_upper.go":
typeName = "FirstUpperCaseValue"
transformNameMethod = "first-upper"
case "transform_first_lower.go":
typeName = "FirstLowerCaseValue"
transformNameMethod = "first-lower"
default:
typeName = fmt.Sprintf("%c%s", name[0]+'A'-'a', name[1:len(name)-len(".go")])
transformNameMethod = "noop"
} }
stringerCompileAndRun(t, dir, stringer, typeName, name, transformNameMethod) stringerCompileAndRun(t, dir, stringer, typeName, name, transformNameMethod)

View File

@ -22,6 +22,16 @@ func %[1]sValues() []%[1]s {
} }
` `
// Arguments to format are:
// [1]: type name
const stringsMethod = `// %[1]sStrings returns a slice of all String values of the enum
func %[1]sStrings() []string {
strs := make([]string, len(_%[1]sNames))
copy(strs, _%[1]sNames)
return strs
}
`
// Arguments to format are: // Arguments to format are:
// [1]: type name // [1]: type name
const stringBelongsMethodLoop = `// IsA%[1]s returns "true" if the value is listed in the enum definition. "false" otherwise const stringBelongsMethodLoop = `// IsA%[1]s returns "true" if the value is listed in the enum definition. "false" otherwise
@ -57,8 +67,26 @@ func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThresh
g.Printf("}\n\n") g.Printf("}\n\n")
// Print the map between name and value // Print the map between name and value
g.Printf("\nvar _%sNameToValueMap = map[string]%s{\n", typeName, typeName) g.printValueMap(runs, typeName, runsThreshold)
// Print the slice of names
g.printNamesSlice(runs, typeName, runsThreshold)
// Print the basic extra methods
g.Printf(stringNameToValueMethod, typeName)
g.Printf(stringValuesMethod, typeName)
g.Printf(stringsMethod, typeName)
if len(runs) < runsThreshold {
g.Printf(stringBelongsMethodLoop, typeName)
} else { // There is a map of values, the code is simpler then
g.Printf(stringBelongsMethodSet, typeName)
}
}
func (g *Generator) printValueMap(runs [][]Value, typeName string, runsThreshold int) {
thereAreRuns := len(runs) > 1 && len(runs) <= runsThreshold thereAreRuns := len(runs) > 1 && len(runs) <= runsThreshold
g.Printf("\nvar _%sNameToValueMap = map[string]%s{\n", typeName, typeName)
var n int var n int
var runID string var runID string
for i, values := range runs { for i, values := range runs {
@ -75,15 +103,27 @@ func (g *Generator) buildBasicExtras(runs [][]Value, typeName string, runsThresh
} }
} }
g.Printf("}\n\n") g.Printf("}\n\n")
// Print the basic extra methods
g.Printf(stringNameToValueMethod, typeName)
g.Printf(stringValuesMethod, typeName)
if len(runs) < runsThreshold {
g.Printf(stringBelongsMethodLoop, typeName)
} else { // There is a map of values, the code is simpler then
g.Printf(stringBelongsMethodSet, typeName)
} }
func (g *Generator) printNamesSlice(runs [][]Value, typeName string, runsThreshold int) {
thereAreRuns := len(runs) > 1 && len(runs) <= runsThreshold
g.Printf("\nvar _%sNames = []string{\n", typeName)
var n int
var runID string
for i, values := range runs {
if thereAreRuns {
runID = "_" + fmt.Sprintf("%d", i)
n = 0
} else {
runID = ""
}
for _, value := range values {
g.Printf("\t_%sName%s[%d:%d],\n", typeName, runID, n, n+len(value.name))
n += len(value.name)
}
}
g.Printf("}\n\n")
} }
// Arguments to format are: // Arguments to format are:

8
go.sum Normal file
View File

@ -0,0 +1,8 @@
github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1 h1:/I3lTljEEDNYLho3/FUB7iD/oc2cEFgVmbHzV+O0PtU=
github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1/go.mod h1:eD5JxqMiuNYyFNmyY9rkJ/slN8y59oEu4Ei7F8OoKWQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190404132500-923d25813098 h1:MtqjsZmyGRgMmLUgxnmMJ6RYdvd2ib8ipiayHhqSxs4=
golang.org/x/tools v0.0.0-20190404132500-923d25813098/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -52,8 +52,16 @@ var goldenJSONAndSQL = []Golden{
{"prime", primeJsonAndSqlIn, primeJsonAndSqlOut}, {"prime", primeJsonAndSqlIn, primeJsonAndSqlOut},
} }
var goldenPrefix = []Golden{ var goldenTrimPrefix = []Golden{
{"prefix", prefixIn, dayOut}, {"trim prefix", trimPrefixIn, dayOut},
}
var goldenWithPrefix = []Golden{
{"with prefix", dayIn, prefixedDayOut},
}
var goldenTrimAndAddPrefix = []Golden{
{"trim and add prefix", trimPrefixIn, trimmedPrefixedDayOut},
} }
// Each example starts with "type XXX [u]int", with a single space separating them. // Each example starts with "type XXX [u]int", with a single space separating them.
@ -95,6 +103,16 @@ var _DayNameToValueMap = map[string]Day{
_DayName[44:50]: 6, _DayName[44:50]: 6,
} }
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. // DayString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func DayString(s string) (Day, error) { func DayString(s string) (Day, error) {
@ -109,6 +127,145 @@ func DayValues() []Day {
return _DayValues 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
}
`
const prefixedDayOut = `
const _DayName = "DayMondayDayTuesdayDayWednesdayDayThursdayDayFridayDaySaturdayDaySunday"
var _DayIndex = [...]uint8{0, 9, 19, 31, 42, 51, 62, 71}
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]]
}
var _DayValues = []Day{0, 1, 2, 3, 4, 5, 6}
var _DayNameToValueMap = map[string]Day{
_DayName[0:9]: 0,
_DayName[9:19]: 1,
_DayName[19:31]: 2,
_DayName[31:42]: 3,
_DayName[42:51]: 4,
_DayName[51:62]: 5,
_DayName[62:71]: 6,
}
var _DayNames = []string{
_DayName[0:9],
_DayName[9:19],
_DayName[19:31],
_DayName[31:42],
_DayName[42:51],
_DayName[51:62],
_DayName[62:71],
}
// 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
}
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
}
`
const trimmedPrefixedDayOut = `
const _DayName = "NightMondayNightTuesdayNightWednesdayNightThursdayNightFridayNightSaturdayNightSunday"
var _DayIndex = [...]uint8{0, 11, 23, 37, 50, 61, 74, 85}
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]]
}
var _DayValues = []Day{0, 1, 2, 3, 4, 5, 6}
var _DayNameToValueMap = map[string]Day{
_DayName[0:11]: 0,
_DayName[11:23]: 1,
_DayName[23:37]: 2,
_DayName[37:50]: 3,
_DayName[50:61]: 4,
_DayName[61:74]: 5,
_DayName[74:85]: 6,
}
var _DayNames = []string{
_DayName[0:11],
_DayName[11:23],
_DayName[23:37],
_DayName[37:50],
_DayName[50:61],
_DayName[61:74],
_DayName[74:85],
}
// 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
}
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 // IsADay returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Day) IsADay() bool { func (i Day) IsADay() bool {
for _, v := range _DayValues { for _, v := range _DayValues {
@ -153,6 +310,12 @@ var _NumberNameToValueMap = map[string]Number{
_NumberName[6:11]: 3, _NumberName[6:11]: 3,
} }
var _NumberNames = []string{
_NumberName[0:3],
_NumberName[3:6],
_NumberName[6:11],
}
// NumberString retrieves an enum value from the enum constants string name. // NumberString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func NumberString(s string) (Number, error) { func NumberString(s string) (Number, error) {
@ -167,6 +330,13 @@ func NumberValues() []Number {
return _NumberValues return _NumberValues
} }
// NumberStrings returns a slice of all String values of the enum
func NumberStrings() []string {
strs := make([]string, len(_NumberNames))
copy(strs, _NumberNames)
return strs
}
// IsANumber returns "true" if the value is listed in the enum definition. "false" otherwise // IsANumber returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Number) IsANumber() bool { func (i Number) IsANumber() bool {
for _, v := range _NumberValues { for _, v := range _NumberValues {
@ -233,6 +403,17 @@ var _GapNameToValueMap = map[string]Gap{
_GapName_2[0:6]: 11, _GapName_2[0:6]: 11,
} }
var _GapNames = []string{
_GapName_0[0:3],
_GapName_0[3:8],
_GapName_1[0:4],
_GapName_1[4:7],
_GapName_1[7:12],
_GapName_1[12:17],
_GapName_1[17:21],
_GapName_2[0:6],
}
// GapString retrieves an enum value from the enum constants string name. // GapString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func GapString(s string) (Gap, error) { func GapString(s string) (Gap, error) {
@ -247,6 +428,13 @@ func GapValues() []Gap {
return _GapValues return _GapValues
} }
// GapStrings returns a slice of all String values of the enum
func GapStrings() []string {
strs := make([]string, len(_GapNames))
copy(strs, _GapNames)
return strs
}
// IsAGap returns "true" if the value is listed in the enum definition. "false" otherwise // IsAGap returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Gap) IsAGap() bool { func (i Gap) IsAGap() bool {
for _, v := range _GapValues { for _, v := range _GapValues {
@ -292,6 +480,14 @@ var _NumNameToValueMap = map[string]Num{
_NumName[10:12]: 2, _NumName[10:12]: 2,
} }
var _NumNames = []string{
_NumName[0:3],
_NumName[3:6],
_NumName[6:8],
_NumName[8:10],
_NumName[10:12],
}
// NumString retrieves an enum value from the enum constants string name. // NumString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func NumString(s string) (Num, error) { func NumString(s string) (Num, error) {
@ -306,6 +502,13 @@ func NumValues() []Num {
return _NumValues return _NumValues
} }
// NumStrings returns a slice of all String values of the enum
func NumStrings() []string {
strs := make([]string, len(_NumNames))
copy(strs, _NumNames)
return strs
}
// IsANum returns "true" if the value is listed in the enum definition. "false" otherwise // IsANum returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Num) IsANum() bool { func (i Num) IsANum() bool {
for _, v := range _NumValues { for _, v := range _NumValues {
@ -364,6 +567,14 @@ var _UnumNameToValueMap = map[string]Unum{
_UnumName_1[3:6]: 254, _UnumName_1[3:6]: 254,
} }
var _UnumNames = []string{
_UnumName_0[0:2],
_UnumName_0[2:4],
_UnumName_0[4:6],
_UnumName_1[0:3],
_UnumName_1[3:6],
}
// UnumString retrieves an enum value from the enum constants string name. // UnumString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func UnumString(s string) (Unum, error) { func UnumString(s string) (Unum, error) {
@ -378,6 +589,13 @@ func UnumValues() []Unum {
return _UnumValues return _UnumValues
} }
// UnumStrings returns a slice of all String values of the enum
func UnumStrings() []string {
strs := make([]string, len(_UnumNames))
copy(strs, _UnumNames)
return strs
}
// IsAUnum returns "true" if the value is listed in the enum definition. "false" otherwise // IsAUnum returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Unum) IsAUnum() bool { func (i Unum) IsAUnum() bool {
for _, v := range _UnumValues { for _, v := range _UnumValues {
@ -454,6 +672,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -468,6 +702,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -537,6 +778,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -551,6 +808,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -638,6 +902,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -652,6 +932,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -734,6 +1021,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -748,6 +1051,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -835,6 +1145,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -849,6 +1175,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -947,6 +1280,22 @@ var _PrimeNameToValueMap = map[string]Prime{
_PrimeName[32:35]: 43, _PrimeName[32:35]: 43,
} }
var _PrimeNames = []string{
_PrimeName[0:2],
_PrimeName[2:4],
_PrimeName[4:6],
_PrimeName[6:8],
_PrimeName[8:11],
_PrimeName[11:14],
_PrimeName[14:17],
_PrimeName[17:20],
_PrimeName[20:23],
_PrimeName[23:26],
_PrimeName[26:29],
_PrimeName[29:32],
_PrimeName[32:35],
}
// PrimeString retrieves an enum value from the enum constants string name. // PrimeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum. // Throws an error if the param is not part of the enum.
func PrimeString(s string) (Prime, error) { func PrimeString(s string) (Prime, error) {
@ -961,6 +1310,13 @@ func PrimeValues() []Prime {
return _PrimeValues return _PrimeValues
} }
// PrimeStrings returns a slice of all String values of the enum
func PrimeStrings() []string {
strs := make([]string, len(_PrimeNames))
copy(strs, _PrimeNames)
return strs
}
// IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise // IsAPrime returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Prime) IsAPrime() bool { func (i Prime) IsAPrime() bool {
_, ok := _PrimeMap[i] _, ok := _PrimeMap[i]
@ -1013,7 +1369,7 @@ func (i *Prime) Scan(value interface{}) error {
} }
` `
const prefixIn = `type Day int const trimPrefixIn = `type Day int
const ( const (
DayMonday Day = iota DayMonday Day = iota
DayTuesday DayTuesday
@ -1027,29 +1383,35 @@ const (
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for _, test := range golden { for _, test := range golden {
runGoldenTest(t, test, false, false, false, false, "") runGoldenTest(t, test, false, false, false, false, "", "")
} }
for _, test := range goldenJSON { for _, test := range goldenJSON {
runGoldenTest(t, test, true, false, false, false, "") runGoldenTest(t, test, true, false, false, false, "", "")
} }
for _, test := range goldenText { for _, test := range goldenText {
runGoldenTest(t, test, false, false, false, true, "") runGoldenTest(t, test, false, false, false, true, "", "")
} }
for _, test := range goldenYAML { for _, test := range goldenYAML {
runGoldenTest(t, test, false, true, false, false, "") runGoldenTest(t, test, false, true, false, false, "", "")
} }
for _, test := range goldenSQL { for _, test := range goldenSQL {
runGoldenTest(t, test, false, false, true, false, "") runGoldenTest(t, test, false, false, true, false, "", "")
} }
for _, test := range goldenJSONAndSQL { for _, test := range goldenJSONAndSQL {
runGoldenTest(t, test, true, false, true, false, "") runGoldenTest(t, test, true, false, true, false, "", "")
} }
for _, test := range goldenPrefix { for _, test := range goldenTrimPrefix {
runGoldenTest(t, test, false, false, false, false, "Day") runGoldenTest(t, test, false, false, false, false, "Day", "")
}
for _, test := range goldenWithPrefix {
runGoldenTest(t, test, false, false, false, false, "", "Day")
}
for _, test := range goldenTrimAndAddPrefix {
runGoldenTest(t, test, false, false, false, false, "Day", "Night")
} }
} }
func runGoldenTest(t *testing.T, test Golden, generateJSON, generateYAML, generateSQL, generateText bool, prefix string) { func runGoldenTest(t *testing.T, test Golden, generateJSON, generateYAML, generateSQL, generateText bool, trimPrefix string, prefix string) {
var g Generator var g Generator
file := test.name + ".go" file := test.name + ".go"
input := "package test\n" + test.input input := "package test\n" + test.input
@ -1076,9 +1438,14 @@ func runGoldenTest(t *testing.T, test Golden, generateJSON, generateYAML, genera
if len(tokens) != 3 { if len(tokens) != 3 {
t.Fatalf("%s: need type declaration on first line", test.name) t.Fatalf("%s: need type declaration on first line", test.name)
} }
g.generate(tokens[1], generateJSON, generateYAML, generateSQL, generateText, "noop", prefix) g.generate(tokens[1], generateJSON, generateYAML, generateSQL, generateText, "noop", trimPrefix, prefix)
got := string(g.format()) got := string(g.format())
if got != test.output { if got != test.output {
// dmp := diffmatchpatch.New()
// diffs := dmp.DiffMain(got, test.output, false)
// t.Errorf(dmp.DiffPrettyText(diffs))
t.Errorf("%s: got\n====\n%s====\nexpected\n====%s", test.name, got, test.output) t.Errorf("%s: got\n====\n%s====\nexpected\n====%s", test.name, got, test.output)
} }
} }

View File

@ -24,6 +24,8 @@ import (
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"unicode"
"unicode/utf8"
"golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages"
@ -50,6 +52,7 @@ var (
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go") output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
transformMethod = flag.String("transform", "noop", "enum item name transformation method. Default: noop") transformMethod = flag.String("transform", "noop", "enum item name transformation method. Default: noop")
trimPrefix = flag.String("trimprefix", "", "transform each item name by removing a prefix. Default: \"\"") 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: \"\"")
) )
var comments arrayFlags var comments arrayFlags
@ -122,7 +125,7 @@ func main() {
// Run generate for each type. // Run generate for each type.
for _, typeName := range typs { for _, typeName := range typs {
g.generate(typeName, *json, *yaml, *sql, *text, *transformMethod, *trimPrefix) g.generate(typeName, *json, *yaml, *sql, *text, *transformMethod, *trimPrefix, *addPrefix)
} }
// Format the output. // Format the output.
@ -312,18 +315,63 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
} }
func (g *Generator) transformValueNames(values []Value, transformMethod string) { func (g *Generator) transformValueNames(values []Value, transformMethod string) {
var sep rune var fn func(src string) string
switch transformMethod { switch transformMethod {
case "snake": case "snake":
sep = '_' fn = func(s string) string {
return strings.ToLower(name.Delimit(s, '_'))
}
case "snake_upper", "snake-upper":
fn = func(s string) string {
return strings.ToUpper(name.Delimit(s, '_'))
}
case "kebab": case "kebab":
sep = '-' fn = func(s string) string {
return strings.ToLower(name.Delimit(s, '-'))
}
case "kebab_upper", "kebab-upper":
fn = func(s string) string {
return strings.ToUpper(name.Delimit(s, '-'))
}
case "upper":
fn = func(s string) string {
return strings.ToUpper(s)
}
case "lower":
fn = func(s string) string {
return strings.ToLower(s)
}
case "title":
fn = func(s string) string {
return strings.Title(s)
}
case "title-lower":
fn = func(s string) string {
title := []rune(strings.Title(s))
title[0] = unicode.ToLower(title[0])
return string(title)
}
case "first":
fn = func(s string) string {
r, _ := utf8.DecodeRuneInString(s)
return string(r)
}
case "first_upper", "first-upper":
fn = func(s string) string {
r, _ := utf8.DecodeRuneInString(s)
return strings.ToUpper(string(r))
}
case "first_lower", "first-lower":
fn = func(s string) string {
r, _ := utf8.DecodeRuneInString(s)
return strings.ToLower(string(r))
}
default: default:
return return
} }
for i := range values { for i := range values {
values[i].name = strings.ToLower(name.Delimit(values[i].name, sep)) values[i].name = fn(values[i].name)
} }
} }
@ -334,8 +382,16 @@ func (g *Generator) trimValueNames(values []Value, prefix string) {
} }
} }
// prefixValueNames adds a prefix to each name
func (g *Generator) prefixValueNames(values []Value, prefix string) {
for i := range values {
values[i].name = prefix + values[i].name
}
}
// generate produces the String method for the named type. // generate produces the String method for the named type.
func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeSQL, includeText bool, transformMethod string, trimPrefix string) { func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeSQL, includeText bool,
transformMethod string, trimPrefix string, addPrefix string) {
values := make([]Value, 0, 100) values := make([]Value, 0, 100)
for _, file := range g.pkg.files { for _, file := range g.pkg.files {
// Set the state for this run of the walker. // Set the state for this run of the walker.
@ -355,6 +411,8 @@ func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeS
g.transformValueNames(values, transformMethod) g.transformValueNames(values, transformMethod)
g.prefixValueNames(values, addPrefix)
runs := splitIntoRuns(values) runs := splitIntoRuns(values)
// The decision of which pattern to use depends on the number of // The decision of which pattern to use depends on the number of
// runs in the numbers. If there's only one, it's easy. For more than // runs in the numbers. If there's only one, it's easy. For more than

25
testdata/transform.go vendored
View File

@ -1,25 +0,0 @@
package main
import "fmt"
type CamelCaseValue int
const (
CamelCaseValueOne CamelCaseValue = iota
CamelCaseValueTwo
CamelCaseValueThree
)
func main() {
ck(CamelCaseValueOne, "camel_case_value_one")
ck(CamelCaseValueTwo, "camel_case_value_two")
ck(CamelCaseValueThree, "camel_case_value_three")
ck(-127, "CamelCaseValue(-127)")
ck(127, "CamelCaseValue(127)")
}
func ck(value CamelCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform.go: " + str)
}
}

25
testdata/transform_first.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type FirstCaseValue int
const (
Male FirstCaseValue = iota
Female
unknown
)
func main() {
ck(Male, "M")
ck(Female, "F")
ck(unknown, "u")
ck(-127, "FirstCaseValue(-127)")
ck(127, "FirstCaseValue(127)")
}
func ck(value FirstCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_first.go: " + str)
}
}

25
testdata/transform_first_lower.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type FirstLowerCaseValue int
const (
Male FirstLowerCaseValue = iota
Female
Unknown
)
func main() {
ck(Male, "m")
ck(Female, "f")
ck(Unknown, "u")
ck(-127, "FirstLowerCaseValue(-127)")
ck(127, "FirstLowerCaseValue(127)")
}
func ck(value FirstLowerCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_first_lower.go: " + str)
}
}

25
testdata/transform_first_upper.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type FirstUpperCaseValue int
const (
male FirstUpperCaseValue = iota
female
unknown
)
func main() {
ck(male, "M")
ck(female, "F")
ck(unknown, "U")
ck(-127, "FirstUpperCaseValue(-127)")
ck(127, "FirstUpperCaseValue(127)")
}
func ck(value FirstUpperCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_first_upper.go: " + str)
}
}

25
testdata/transform_kebab.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type KebabCaseValue int
const (
KebabCaseValueOne KebabCaseValue = iota
KebabCaseValueTwo
KebabCaseValueThree
)
func main() {
ck(KebabCaseValueOne, "kebab-case-value-one")
ck(KebabCaseValueTwo, "kebab-case-value-two")
ck(KebabCaseValueThree, "kebab-case-value-three")
ck(-127, "KebabCaseValue(-127)")
ck(127, "KebabCaseValue(127)")
}
func ck(value KebabCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_kebab.go: " + str)
}
}

25
testdata/transform_kebab_upper.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type KebabUpperCaseValue int
const (
KebabUpperCaseValueOne KebabUpperCaseValue = iota
KebabUpperCaseValueTwo
KebabUpperCaseValueThree
)
func main() {
ck(KebabUpperCaseValueOne, "KEBAB-UPPER-CASE-VALUE-ONE")
ck(KebabUpperCaseValueTwo, "KEBAB-UPPER-CASE-VALUE-TWO")
ck(KebabUpperCaseValueThree, "KEBAB-UPPER-CASE-VALUE-THREE")
ck(-127, "KebabUpperCaseValue(-127)")
ck(127, "KebabUpperCaseValue(127)")
}
func ck(value KebabUpperCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_kebab_upper.go: " + str)
}
}

25
testdata/transform_lower.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type LowerCaseValue int
const (
LowerCaseValueOne LowerCaseValue = iota
LowerCaseValueTwo
LowerCaseValueThree
)
func main() {
ck(LowerCaseValueOne, "lowercasevalueone")
ck(LowerCaseValueTwo, "lowercasevaluetwo")
ck(LowerCaseValueThree, "lowercasevaluethree")
ck(-127, "LowerCaseValue(-127)")
ck(127, "LowerCaseValue(127)")
}
func ck(value LowerCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_lower.go: " + str)
}
}

25
testdata/transform_snake.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type SnakeCaseValue int
const (
SnakeCaseValueOne SnakeCaseValue = iota
SnakeCaseValueTwo
SnakeCaseValueThree
)
func main() {
ck(SnakeCaseValueOne, "snake_case_value_one")
ck(SnakeCaseValueTwo, "snake_case_value_two")
ck(SnakeCaseValueThree, "snake_case_value_three")
ck(-127, "SnakeCaseValue(-127)")
ck(127, "SnakeCaseValue(127)")
}
func ck(value SnakeCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_snake.go: " + str)
}
}

25
testdata/transform_snake_upper.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type SnakeUpperCaseValue int
const (
SnakeUpperCaseValueOne SnakeUpperCaseValue = iota
SnakeUpperCaseValueTwo
SnakeUpperCaseValueThree
)
func main() {
ck(SnakeUpperCaseValueOne, "SNAKE_UPPER_CASE_VALUE_ONE")
ck(SnakeUpperCaseValueTwo, "SNAKE_UPPER_CASE_VALUE_TWO")
ck(SnakeUpperCaseValueThree, "SNAKE_UPPER_CASE_VALUE_THREE")
ck(-127, "SnakeUpperCaseValue(-127)")
ck(127, "SnakeUpperCaseValue(127)")
}
func ck(value SnakeUpperCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_snake_upper.go: " + str)
}
}

25
testdata/transform_title.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type TitleCaseValue int
const (
titlecasevalueone TitleCaseValue = iota
titlecasevaluetwo
titlecasevaluethree
)
func main() {
ck(titlecasevalueone, "Titlecasevalueone")
ck(titlecasevaluetwo, "Titlecasevaluetwo")
ck(titlecasevaluethree, "Titlecasevaluethree")
ck(-127, "TitleCaseValue(-127)")
ck(127, "TitleCaseValue(127)")
}
func ck(value TitleCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_title.go: " + str)
}
}

25
testdata/transform_upper.go vendored Normal file
View File

@ -0,0 +1,25 @@
package main
import "fmt"
type UpperCaseValue int
const (
UpperCaseValueOne UpperCaseValue = iota
UpperCaseValueTwo
UpperCaseValueThree
)
func main() {
ck(UpperCaseValueOne, "UPPERCASEVALUEONE")
ck(UpperCaseValueTwo, "UPPERCASEVALUETWO")
ck(UpperCaseValueThree, "UPPERCASEVALUETHREE")
ck(-127, "UpperCaseValue(-127)")
ck(127, "UpperCaseValue(127)")
}
func ck(value UpperCaseValue, str string) {
if fmt.Sprint(value) != str {
panic("transform_upper.go: " + str)
}
}