mirror of https://github.com/dmarkham/enumer.git
Updated stringer.go to current version (2018/01)
This commit is contained in:
parent
aa50e29ee8
commit
95a15ae31e
120
stringer.go
120
stringer.go
|
@ -2,8 +2,6 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build go1.5
|
|
||||||
|
|
||||||
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
|
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
|
||||||
// interface. Given the name of a (signed or unsigned) integer type T that has constants
|
// interface. Given the name of a (signed or unsigned) integer type T that has constants
|
||||||
// defined, stringer will create a new self-contained Go source file implementing
|
// defined, stringer will create a new self-contained Go source file implementing
|
||||||
|
@ -58,8 +56,8 @@
|
||||||
// where t is the lower-cased name of the first type listed. It can be overridden
|
// where t is the lower-cased name of the first type listed. It can be overridden
|
||||||
// with the -output flag.
|
// with the -output flag.
|
||||||
//
|
//
|
||||||
// ------
|
// -----------------
|
||||||
// This is a patched versin of the original stringer. The original generates source code
|
// This is a patched version of the original stringer. The original generates source code
|
||||||
// using '_' (underscore) as part of generated golang identifiers. This creates a number of
|
// using '_' (underscore) as part of generated golang identifiers. This creates a number of
|
||||||
// golint warnings. The patched version simply leaves the '_' out
|
// golint warnings. The patched version simply leaves the '_' out
|
||||||
|
|
||||||
|
@ -86,12 +84,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
|
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
|
||||||
sql = flag.Bool("sql", false, "if true, the Scanner and Valuer interface will be implemented.")
|
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
|
||||||
json = flag.Bool("json", false, "if true, json marshaling methods will be generated. Default: false")
|
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
|
||||||
yaml = flag.Bool("yaml", false, "if true, yaml marshaling methods will be generated. Default: false")
|
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
|
||||||
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
|
|
||||||
transformMethod = flag.String("transform", "noop", "enum item name transformation method. Default: noop")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Usage is a replacement usage function for the flags package.
|
// Usage is a replacement usage function for the flags package.
|
||||||
|
@ -107,7 +103,7 @@ func Usage() {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
log.SetPrefix("enumer: ")
|
log.SetPrefix("stringer: ")
|
||||||
flag.Usage = Usage
|
flag.Usage = Usage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if len(*typeNames) == 0 {
|
if len(*typeNames) == 0 {
|
||||||
|
@ -124,11 +120,11 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the package once.
|
// Parse the package once.
|
||||||
var (
|
var dir string
|
||||||
dir string
|
g := Generator{
|
||||||
g Generator
|
trimPrefix: *trimprefix,
|
||||||
)
|
lineComment: *linecomment,
|
||||||
|
}
|
||||||
if len(args) == 1 && isDirectory(args[0]) {
|
if len(args) == 1 && isDirectory(args[0]) {
|
||||||
dir = args[0]
|
dir = args[0]
|
||||||
g.parsePackageDir(args[0])
|
g.parsePackageDir(args[0])
|
||||||
|
@ -138,23 +134,15 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the header and package clause.
|
// Print the header and package clause.
|
||||||
g.Printf("// Code generated by \"enumer %s\"; DO NOT EDIT\n", strings.Join(os.Args[1:], " "))
|
g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
|
||||||
g.Printf("\n")
|
g.Printf("\n")
|
||||||
g.Printf("package %s", g.pkg.name)
|
g.Printf("package %s", g.pkg.name)
|
||||||
g.Printf("\n")
|
g.Printf("\n")
|
||||||
g.Printf("import (\n")
|
g.Printf("import \"strconv\"\n") // Used by all methods.
|
||||||
g.Printf("\t\"fmt\"\n")
|
|
||||||
if *sql {
|
|
||||||
g.Printf("\t\"database/sql/driver\"\n")
|
|
||||||
}
|
|
||||||
if *json {
|
|
||||||
g.Printf("\t\"encoding/json\"\n")
|
|
||||||
}
|
|
||||||
g.Printf(")\n")
|
|
||||||
|
|
||||||
// Run generate for each type.
|
// Run generate for each type.
|
||||||
for _, typeName := range types {
|
for _, typeName := range types {
|
||||||
g.generate(typeName, *json, *yaml, *sql, *transformMethod)
|
g.generate(typeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the output.
|
// Format the output.
|
||||||
|
@ -186,6 +174,9 @@ func isDirectory(name string) bool {
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
buf bytes.Buffer // Accumulated output.
|
buf bytes.Buffer // Accumulated output.
|
||||||
pkg *Package // Package we are scanning.
|
pkg *Package // Package we are scanning.
|
||||||
|
|
||||||
|
trimPrefix string
|
||||||
|
lineComment bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generator) Printf(format string, args ...interface{}) {
|
func (g *Generator) Printf(format string, args ...interface{}) {
|
||||||
|
@ -199,6 +190,9 @@ type File struct {
|
||||||
// These fields are reset for each type being generated.
|
// These fields are reset for each type being generated.
|
||||||
typeName string // Name of the constant type.
|
typeName string // Name of the constant type.
|
||||||
values []Value // Accumulator for constant values of that type.
|
values []Value // Accumulator for constant values of that type.
|
||||||
|
|
||||||
|
trimPrefix string
|
||||||
|
lineComment bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
|
@ -255,14 +249,16 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac
|
||||||
if !strings.HasSuffix(name, ".go") {
|
if !strings.HasSuffix(name, ".go") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parsedFile, err := parser.ParseFile(fs, name, text, 0)
|
parsedFile, err := parser.ParseFile(fs, name, text, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("parsing package: %s: %s", name, err)
|
log.Fatalf("parsing package: %s: %s", name, err)
|
||||||
}
|
}
|
||||||
astFiles = append(astFiles, parsedFile)
|
astFiles = append(astFiles, parsedFile)
|
||||||
files = append(files, &File{
|
files = append(files, &File{
|
||||||
file: parsedFile,
|
file: parsedFile,
|
||||||
pkg: g.pkg,
|
pkg: g.pkg,
|
||||||
|
trimPrefix: g.trimPrefix,
|
||||||
|
lineComment: g.lineComment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(astFiles) == 0 {
|
if len(astFiles) == 0 {
|
||||||
|
@ -289,24 +285,8 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
|
||||||
pkg.typesPkg = typesPkg
|
pkg.typesPkg = typesPkg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generator) transformValueNames(values []Value, transformMethod string) {
|
|
||||||
var transform func(string) string
|
|
||||||
switch transformMethod {
|
|
||||||
case "snake":
|
|
||||||
transform = toSnakeCase
|
|
||||||
case "kebab":
|
|
||||||
transform = toKebabCase
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range values {
|
|
||||||
values[i].name = transform(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 bool, transformMethod string) {
|
func (g *Generator) generate(typeName 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.
|
||||||
|
@ -321,9 +301,6 @@ func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeS
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
log.Fatalf("no values defined for type %s", typeName)
|
log.Fatalf("no values defined for type %s", typeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.transformValueNames(values, transformMethod)
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -337,26 +314,14 @@ func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeS
|
||||||
// being necessary for any realistic example other than bitmasks
|
// being necessary for any realistic example other than bitmasks
|
||||||
// is very low. And bitmasks probably deserve their own analysis,
|
// is very low. And bitmasks probably deserve their own analysis,
|
||||||
// to be done some other day.
|
// to be done some other day.
|
||||||
const runsThreshold = 10
|
|
||||||
switch {
|
switch {
|
||||||
case len(runs) == 1:
|
case len(runs) == 1:
|
||||||
g.buildOneRun(runs, typeName)
|
g.buildOneRun(runs, typeName)
|
||||||
case len(runs) <= runsThreshold:
|
case len(runs) <= 10:
|
||||||
g.buildMultipleRuns(runs, typeName)
|
g.buildMultipleRuns(runs, typeName)
|
||||||
default:
|
default:
|
||||||
g.buildMap(runs, typeName)
|
g.buildMap(runs, typeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.buildValueToNameMap(runs, typeName, runsThreshold)
|
|
||||||
if includeJSON {
|
|
||||||
g.buildJSONMethods(runs, typeName, runsThreshold)
|
|
||||||
}
|
|
||||||
if includeYAML {
|
|
||||||
g.buildYAMLMethods(runs, typeName, runsThreshold)
|
|
||||||
}
|
|
||||||
if includeSQL {
|
|
||||||
g.addValueAndScanMethod(typeName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitIntoRuns breaks the values into runs of contiguous sequences.
|
// splitIntoRuns breaks the values into runs of contiguous sequences.
|
||||||
|
@ -406,7 +371,7 @@ func (g *Generator) format() []byte {
|
||||||
|
|
||||||
// Value represents a declared constant.
|
// Value represents a declared constant.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
name string // The name of the constant after transformation (i.e. camel case => snake case)
|
name string // The name of the constant.
|
||||||
// The value is stored as a bit pattern alone. The boolean tells us
|
// The value is stored as a bit pattern alone. The boolean tells us
|
||||||
// whether to interpret it as an int64 or a uint64; the only place
|
// whether to interpret it as an int64 or a uint64; the only place
|
||||||
// this matters is when sorting.
|
// this matters is when sorting.
|
||||||
|
@ -505,6 +470,10 @@ func (f *File) genDecl(node ast.Node) bool {
|
||||||
signed: info&types.IsUnsigned == 0,
|
signed: info&types.IsUnsigned == 0,
|
||||||
str: value.String(),
|
str: value.String(),
|
||||||
}
|
}
|
||||||
|
if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
|
||||||
|
v.name = strings.TrimSpace(c.Text())
|
||||||
|
}
|
||||||
|
v.name = strings.TrimPrefix(v.name, f.trimPrefix)
|
||||||
f.values = append(f.values, v)
|
f.values = append(f.values, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,7 +503,9 @@ func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
|
||||||
var indexes, names []string
|
var indexes, names []string
|
||||||
for i, run := range runs {
|
for i, run := range runs {
|
||||||
index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
|
index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
|
||||||
indexes = append(indexes, index)
|
if len(run) != 1 {
|
||||||
|
indexes = append(indexes, index)
|
||||||
|
}
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
g.Printf("const (\n")
|
g.Printf("const (\n")
|
||||||
|
@ -542,11 +513,14 @@ func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
|
||||||
g.Printf("\t%s\n", name)
|
g.Printf("\t%s\n", name)
|
||||||
}
|
}
|
||||||
g.Printf(")\n\n")
|
g.Printf(")\n\n")
|
||||||
g.Printf("var (")
|
|
||||||
for _, index := range indexes {
|
if len(indexes) > 0 {
|
||||||
g.Printf("\t%s\n", index)
|
g.Printf("var (")
|
||||||
|
for _, index := range indexes {
|
||||||
|
g.Printf("\t%s\n", index)
|
||||||
|
}
|
||||||
|
g.Printf(")\n\n")
|
||||||
}
|
}
|
||||||
g.Printf(")\n\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
|
// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
|
||||||
|
@ -612,7 +586,7 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
|
||||||
// [3]: less than zero check (for signed types)
|
// [3]: less than zero check (for signed types)
|
||||||
const stringOneRun = `func (i %[1]s) String() string {
|
const stringOneRun = `func (i %[1]s) String() string {
|
||||||
if %[3]si >= %[1]s(len(_%[1]sIndex)-1) {
|
if %[3]si >= %[1]s(len(_%[1]sIndex)-1) {
|
||||||
return fmt.Sprintf("%[1]s(%%d)", i)
|
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
return _%[1]sName[_%[1]sIndex[i]:_%[1]sIndex[i+1]]
|
return _%[1]sName[_%[1]sIndex[i]:_%[1]sIndex[i+1]]
|
||||||
}
|
}
|
||||||
|
@ -628,7 +602,7 @@ const stringOneRun = `func (i %[1]s) String() string {
|
||||||
const stringOneRunWithOffset = `func (i %[1]s) String() string {
|
const stringOneRunWithOffset = `func (i %[1]s) String() string {
|
||||||
i -= %[2]s
|
i -= %[2]s
|
||||||
if %[4]si >= %[1]s(len(_%[1]sIndex)-1) {
|
if %[4]si >= %[1]s(len(_%[1]sIndex)-1) {
|
||||||
return fmt.Sprintf("%[1]s(%%d)", i + %[2]s)
|
return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
|
||||||
}
|
}
|
||||||
return _%[1]sName[_%[1]sIndex[i] : _%[1]sIndex[i+1]]
|
return _%[1]sName[_%[1]sIndex[i] : _%[1]sIndex[i+1]]
|
||||||
}
|
}
|
||||||
|
@ -655,7 +629,7 @@ func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
|
||||||
typeName, i, typeName, i, typeName, i)
|
typeName, i, typeName, i, typeName, i)
|
||||||
}
|
}
|
||||||
g.Printf("\tdefault:\n")
|
g.Printf("\tdefault:\n")
|
||||||
g.Printf("\t\treturn fmt.Sprintf(\"%s(%%d)\", i)\n", typeName)
|
g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
|
||||||
g.Printf("\t}\n")
|
g.Printf("\t}\n")
|
||||||
g.Printf("}\n")
|
g.Printf("}\n")
|
||||||
}
|
}
|
||||||
|
@ -682,6 +656,6 @@ const stringMap = `func (i %[1]s) String() string {
|
||||||
if str, ok := _%[1]sMap[i]; ok {
|
if str, ok := _%[1]sMap[i]; ok {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%[1]s(%%d)", i)
|
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
Loading…
Reference in New Issue