From 5d97e5e788a7daa135b98de5db0fc8674d8b8e14 Mon Sep 17 00:00:00 2001 From: Michael Wolber Date: Thu, 4 Jan 2018 12:01:03 +0100 Subject: [PATCH] Revert "Updated stringer.go to current version (2018/01)" This reverts commit 95a15ae31e4a6fcd203ccc3bc7c6c74b63c31ce1. --- stringer.go | 120 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/stringer.go b/stringer.go index e3f5067..fa7c383 100644 --- a/stringer.go +++ b/stringer.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // 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 // 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 @@ -56,8 +58,8 @@ // where t is the lower-cased name of the first type listed. It can be overridden // with the -output flag. // -// ----------------- -// This is a patched version of the original stringer. The original generates source code +// ------ +// This is a patched versin of the original stringer. The original generates source code // using '_' (underscore) as part of generated golang identifiers. This creates a number of // golint warnings. The patched version simply leaves the '_' out @@ -84,10 +86,12 @@ import ( ) var ( - typeNames = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_string.go") - trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") - linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") + 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.") + json = flag.Bool("json", false, "if true, json marshaling methods will be generated. Default: false") + yaml = flag.Bool("yaml", false, "if true, yaml marshaling methods 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") ) // Usage is a replacement usage function for the flags package. @@ -103,7 +107,7 @@ func Usage() { func main() { log.SetFlags(0) - log.SetPrefix("stringer: ") + log.SetPrefix("enumer: ") flag.Usage = Usage flag.Parse() if len(*typeNames) == 0 { @@ -120,11 +124,11 @@ func main() { } // Parse the package once. - var dir string - g := Generator{ - trimPrefix: *trimprefix, - lineComment: *linecomment, - } + var ( + dir string + g Generator + ) + if len(args) == 1 && isDirectory(args[0]) { dir = args[0] g.parsePackageDir(args[0]) @@ -134,15 +138,23 @@ func main() { } // Print the header and package clause. - g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) + g.Printf("// Code generated by \"enumer %s\"; DO NOT EDIT\n", strings.Join(os.Args[1:], " ")) g.Printf("\n") g.Printf("package %s", g.pkg.name) g.Printf("\n") - g.Printf("import \"strconv\"\n") // Used by all methods. + g.Printf("import (\n") + 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. for _, typeName := range types { - g.generate(typeName) + g.generate(typeName, *json, *yaml, *sql, *transformMethod) } // Format the output. @@ -174,9 +186,6 @@ func isDirectory(name string) bool { type Generator struct { buf bytes.Buffer // Accumulated output. pkg *Package // Package we are scanning. - - trimPrefix string - lineComment bool } func (g *Generator) Printf(format string, args ...interface{}) { @@ -190,9 +199,6 @@ type File struct { // These fields are reset for each type being generated. typeName string // Name of the constant type. values []Value // Accumulator for constant values of that type. - - trimPrefix string - lineComment bool } type Package struct { @@ -249,16 +255,14 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac if !strings.HasSuffix(name, ".go") { continue } - parsedFile, err := parser.ParseFile(fs, name, text, parser.ParseComments) + parsedFile, err := parser.ParseFile(fs, name, text, 0) if err != nil { log.Fatalf("parsing package: %s: %s", name, err) } astFiles = append(astFiles, parsedFile) files = append(files, &File{ - file: parsedFile, - pkg: g.pkg, - trimPrefix: g.trimPrefix, - lineComment: g.lineComment, + file: parsedFile, + pkg: g.pkg, }) } if len(astFiles) == 0 { @@ -285,8 +289,24 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) { 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. -func (g *Generator) generate(typeName string) { +func (g *Generator) generate(typeName string, includeJSON, includeYAML, includeSQL bool, transformMethod string) { values := make([]Value, 0, 100) for _, file := range g.pkg.files { // Set the state for this run of the walker. @@ -301,6 +321,9 @@ func (g *Generator) generate(typeName string) { if len(values) == 0 { log.Fatalf("no values defined for type %s", typeName) } + + g.transformValueNames(values, transformMethod) + runs := splitIntoRuns(values) // 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 @@ -314,14 +337,26 @@ func (g *Generator) generate(typeName string) { // being necessary for any realistic example other than bitmasks // is very low. And bitmasks probably deserve their own analysis, // to be done some other day. + const runsThreshold = 10 switch { case len(runs) == 1: g.buildOneRun(runs, typeName) - case len(runs) <= 10: + case len(runs) <= runsThreshold: g.buildMultipleRuns(runs, typeName) default: 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. @@ -371,7 +406,7 @@ func (g *Generator) format() []byte { // Value represents a declared constant. type Value struct { - name string // The name of the constant. + name string // The name of the constant after transformation (i.e. camel case => snake case) // 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 // this matters is when sorting. @@ -470,10 +505,6 @@ func (f *File) genDecl(node ast.Node) bool { signed: info&types.IsUnsigned == 0, 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) } } @@ -503,9 +534,7 @@ func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { var indexes, names []string for i, run := range runs { index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) - if len(run) != 1 { - indexes = append(indexes, index) - } + indexes = append(indexes, index) names = append(names, name) } g.Printf("const (\n") @@ -513,14 +542,11 @@ func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { g.Printf("\t%s\n", name) } g.Printf(")\n\n") - - if len(indexes) > 0 { - g.Printf("var (") - for _, index := range indexes { - g.Printf("\t%s\n", index) - } - g.Printf(")\n\n") + g.Printf("var (") + for _, index := range indexes { + g.Printf("\t%s\n", index) } + g.Printf(")\n\n") } // declareIndexAndNameVar is the single-run version of declareIndexAndNameVars @@ -586,7 +612,7 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) { // [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 "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" + return fmt.Sprintf("%[1]s(%%d)", i) } return _%[1]sName[_%[1]sIndex[i]:_%[1]sIndex[i+1]] } @@ -602,7 +628,7 @@ const stringOneRun = `func (i %[1]s) String() string { const stringOneRunWithOffset = `func (i %[1]s) String() string { i -= %[2]s if %[4]si >= %[1]s(len(_%[1]sIndex)-1) { - return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")" + return fmt.Sprintf("%[1]s(%%d)", i + %[2]s) } return _%[1]sName[_%[1]sIndex[i] : _%[1]sIndex[i+1]] } @@ -629,7 +655,7 @@ func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { typeName, i, typeName, i, typeName, i) } g.Printf("\tdefault:\n") - g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) + g.Printf("\t\treturn fmt.Sprintf(\"%s(%%d)\", i)\n", typeName) g.Printf("\t}\n") g.Printf("}\n") } @@ -656,6 +682,6 @@ const stringMap = `func (i %[1]s) String() string { if str, ok := _%[1]sMap[i]; ok { return str } - return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" + return fmt.Sprintf("%[1]s(%%d)", i) } `