stringer.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
  5. // interface. Given the name of a (signed or unsigned) integer type T that has constants
  6. // defined, stringer will create a new self-contained Go source file implementing
  7. //
  8. // func (t T) String() string
  9. //
  10. // The file is created in the same package and directory as the package that defines T.
  11. // It has helpful defaults designed for use with go generate.
  12. //
  13. // Stringer works best with constants that are consecutive values such as created using iota,
  14. // but creates good code regardless. In the future it might also provide custom support for
  15. // constant sets that are bit patterns.
  16. //
  17. // For example, given this snippet,
  18. //
  19. // package painkiller
  20. //
  21. // type Pill int
  22. //
  23. // const (
  24. // Placebo Pill = iota
  25. // Aspirin
  26. // Ibuprofen
  27. // Paracetamol
  28. // Acetaminophen = Paracetamol
  29. // )
  30. //
  31. // running this command
  32. //
  33. // stringer -type=Pill
  34. //
  35. // in the same directory will create the file pill_string.go, in package painkiller,
  36. // containing a definition of
  37. //
  38. // func (Pill) String() string
  39. //
  40. // That method will translate the value of a Pill constant to the string representation
  41. // of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
  42. // print the string "Aspirin".
  43. //
  44. // Typically this process would be run using go generate, like this:
  45. //
  46. // //go:generate stringer -type=Pill
  47. //
  48. // If multiple constants have the same value, the lexically first matching name will
  49. // be used (in the example, Acetaminophen will print as "Paracetamol").
  50. //
  51. // With no arguments, it processes the package in the current directory.
  52. // Otherwise, the arguments must name a single directory holding a Go package
  53. // or a set of Go source files that represent a single Go package.
  54. //
  55. // The -type flag accepts a comma-separated list of types so a single run can
  56. // generate methods for multiple types. The default output file is t_string.go,
  57. // where t is the lower-cased name of the first type listed. It can be overridden
  58. // with the -output flag.
  59. //
  60. // The -linecomment flag tells stringer to generate the text of any line comment, trimmed
  61. // of leading spaces, instead of the constant name. For instance, if the constants above had a
  62. // Pill prefix, one could write
  63. //
  64. // PillAspirin // Aspirin
  65. //
  66. // to suppress it in the output.
  67. package main // import "golang.org/x/tools/cmd/stringer"
  68. import (
  69. "bytes"
  70. "flag"
  71. "fmt"
  72. "go/ast"
  73. "go/constant"
  74. "go/format"
  75. "go/token"
  76. "go/types"
  77. "log"
  78. "os"
  79. "path/filepath"
  80. "sort"
  81. "strings"
  82. "golang.org/x/tools/go/packages"
  83. )
  84. var (
  85. typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
  86. output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
  87. trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
  88. linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
  89. buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
  90. )
  91. // Usage is a replacement usage function for the flags package.
  92. func Usage() {
  93. fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
  94. fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
  95. fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
  96. fmt.Fprintf(os.Stderr, "For more information, see:\n")
  97. fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n")
  98. fmt.Fprintf(os.Stderr, "Flags:\n")
  99. flag.PrintDefaults()
  100. }
  101. func main() {
  102. log.SetFlags(0)
  103. log.SetPrefix("stringer: ")
  104. flag.Usage = Usage
  105. flag.Parse()
  106. if len(*typeNames) == 0 {
  107. flag.Usage()
  108. os.Exit(2)
  109. }
  110. types := strings.Split(*typeNames, ",")
  111. var tags []string
  112. if len(*buildTags) > 0 {
  113. tags = strings.Split(*buildTags, ",")
  114. }
  115. // We accept either one directory or a list of files. Which do we have?
  116. args := flag.Args()
  117. if len(args) == 0 {
  118. // Default: process whole package in current directory.
  119. args = []string{"."}
  120. }
  121. // Parse the package once.
  122. var dir string
  123. g := Generator{
  124. trimPrefix: *trimprefix,
  125. lineComment: *linecomment,
  126. }
  127. // TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
  128. if len(args) == 1 && isDirectory(args[0]) {
  129. dir = args[0]
  130. } else {
  131. if len(tags) != 0 {
  132. log.Fatal("-tags option applies only to directories, not when files are specified")
  133. }
  134. dir = filepath.Dir(args[0])
  135. }
  136. g.parsePackage(args, tags)
  137. // Print the header and package clause.
  138. g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
  139. g.Printf("\n")
  140. g.Printf("package %s", g.pkg.name)
  141. g.Printf("\n")
  142. g.Printf("import \"strconv\"\n") // Used by all methods.
  143. // Run generate for each type.
  144. for _, typeName := range types {
  145. g.generate(typeName)
  146. }
  147. // Format the output.
  148. src := g.format()
  149. // Write to file.
  150. outputName := *output
  151. if outputName == "" {
  152. baseName := fmt.Sprintf("%s_string.go", types[0])
  153. outputName = filepath.Join(dir, strings.ToLower(baseName))
  154. }
  155. err := os.WriteFile(outputName, src, 0644)
  156. if err != nil {
  157. log.Fatalf("writing output: %s", err)
  158. }
  159. }
  160. // isDirectory reports whether the named file is a directory.
  161. func isDirectory(name string) bool {
  162. info, err := os.Stat(name)
  163. if err != nil {
  164. log.Fatal(err)
  165. }
  166. return info.IsDir()
  167. }
  168. // Generator holds the state of the analysis. Primarily used to buffer
  169. // the output for format.Source.
  170. type Generator struct {
  171. buf bytes.Buffer // Accumulated output.
  172. pkg *Package // Package we are scanning.
  173. trimPrefix string
  174. lineComment bool
  175. logf func(format string, args ...interface{}) // test logging hook; nil when not testing
  176. }
  177. func (g *Generator) Printf(format string, args ...interface{}) {
  178. fmt.Fprintf(&g.buf, format, args...)
  179. }
  180. // File holds a single parsed file and associated data.
  181. type File struct {
  182. pkg *Package // Package to which this file belongs.
  183. file *ast.File // Parsed AST.
  184. // These fields are reset for each type being generated.
  185. typeName string // Name of the constant type.
  186. values []Value // Accumulator for constant values of that type.
  187. trimPrefix string
  188. lineComment bool
  189. }
  190. type Package struct {
  191. name string
  192. defs map[*ast.Ident]types.Object
  193. files []*File
  194. }
  195. // parsePackage analyzes the single package constructed from the patterns and tags.
  196. // parsePackage exits if there is an error.
  197. func (g *Generator) parsePackage(patterns []string, tags []string) {
  198. cfg := &packages.Config{
  199. Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
  200. // TODO: Need to think about constants in test files. Maybe write type_string_test.go
  201. // in a separate pass? For later.
  202. Tests: false,
  203. BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
  204. Logf: g.logf,
  205. }
  206. pkgs, err := packages.Load(cfg, patterns...)
  207. if err != nil {
  208. log.Fatal(err)
  209. }
  210. if len(pkgs) != 1 {
  211. log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
  212. }
  213. g.addPackage(pkgs[0])
  214. }
  215. // addPackage adds a type checked Package and its syntax files to the generator.
  216. func (g *Generator) addPackage(pkg *packages.Package) {
  217. g.pkg = &Package{
  218. name: pkg.Name,
  219. defs: pkg.TypesInfo.Defs,
  220. files: make([]*File, len(pkg.Syntax)),
  221. }
  222. for i, file := range pkg.Syntax {
  223. g.pkg.files[i] = &File{
  224. file: file,
  225. pkg: g.pkg,
  226. trimPrefix: g.trimPrefix,
  227. lineComment: g.lineComment,
  228. }
  229. }
  230. }
  231. // generate produces the String method for the named type.
  232. func (g *Generator) generate(typeName string) {
  233. values := make([]Value, 0, 100)
  234. for _, file := range g.pkg.files {
  235. // Set the state for this run of the walker.
  236. file.typeName = typeName
  237. file.values = nil
  238. if file.file != nil {
  239. ast.Inspect(file.file, file.genDecl)
  240. values = append(values, file.values...)
  241. }
  242. }
  243. if len(values) == 0 {
  244. log.Fatalf("no values defined for type %s", typeName)
  245. }
  246. // Generate code that will fail if the constants change value.
  247. g.Printf("func _() {\n")
  248. g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n")
  249. g.Printf("\t// Re-run the stringer command to generate them again.\n")
  250. g.Printf("\tvar x [1]struct{}\n")
  251. for _, v := range values {
  252. g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str)
  253. }
  254. g.Printf("}\n")
  255. runs := splitIntoRuns(values)
  256. // The decision of which pattern to use depends on the number of
  257. // runs in the numbers. If there's only one, it's easy. For more than
  258. // one, there's a tradeoff between complexity and size of the data
  259. // and code vs. the simplicity of a map. A map takes more space,
  260. // but so does the code. The decision here (crossover at 10) is
  261. // arbitrary, but considers that for large numbers of runs the cost
  262. // of the linear scan in the switch might become important, and
  263. // rather than use yet another algorithm such as binary search,
  264. // we punt and use a map. In any case, the likelihood of a map
  265. // being necessary for any realistic example other than bitmasks
  266. // is very low. And bitmasks probably deserve their own analysis,
  267. // to be done some other day.
  268. switch {
  269. case len(runs) == 1:
  270. g.buildOneRun(runs, typeName)
  271. case len(runs) <= 10:
  272. g.buildMultipleRuns(runs, typeName)
  273. default:
  274. g.buildMap(runs, typeName)
  275. }
  276. }
  277. // splitIntoRuns breaks the values into runs of contiguous sequences.
  278. // For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
  279. // The input slice is known to be non-empty.
  280. func splitIntoRuns(values []Value) [][]Value {
  281. // We use stable sort so the lexically first name is chosen for equal elements.
  282. sort.Stable(byValue(values))
  283. // Remove duplicates. Stable sort has put the one we want to print first,
  284. // so use that one. The String method won't care about which named constant
  285. // was the argument, so the first name for the given value is the only one to keep.
  286. // We need to do this because identical values would cause the switch or map
  287. // to fail to compile.
  288. j := 1
  289. for i := 1; i < len(values); i++ {
  290. if values[i].value != values[i-1].value {
  291. values[j] = values[i]
  292. j++
  293. }
  294. }
  295. values = values[:j]
  296. runs := make([][]Value, 0, 10)
  297. for len(values) > 0 {
  298. // One contiguous sequence per outer loop.
  299. i := 1
  300. for i < len(values) && values[i].value == values[i-1].value+1 {
  301. i++
  302. }
  303. runs = append(runs, values[:i])
  304. values = values[i:]
  305. }
  306. return runs
  307. }
  308. // format returns the gofmt-ed contents of the Generator's buffer.
  309. func (g *Generator) format() []byte {
  310. src, err := format.Source(g.buf.Bytes())
  311. if err != nil {
  312. // Should never happen, but can arise when developing this code.
  313. // The user can compile the output to see the error.
  314. log.Printf("warning: internal error: invalid Go generated: %s", err)
  315. log.Printf("warning: compile the package to analyze the error")
  316. return g.buf.Bytes()
  317. }
  318. return src
  319. }
  320. // Value represents a declared constant.
  321. type Value struct {
  322. originalName string // The name of the constant.
  323. name string // The name with trimmed prefix.
  324. // The value is stored as a bit pattern alone. The boolean tells us
  325. // whether to interpret it as an int64 or a uint64; the only place
  326. // this matters is when sorting.
  327. // Much of the time the str field is all we need; it is printed
  328. // by Value.String.
  329. value uint64 // Will be converted to int64 when needed.
  330. signed bool // Whether the constant is a signed type.
  331. str string // The string representation given by the "go/constant" package.
  332. }
  333. func (v *Value) String() string {
  334. return v.str
  335. }
  336. // byValue lets us sort the constants into increasing order.
  337. // We take care in the Less method to sort in signed or unsigned order,
  338. // as appropriate.
  339. type byValue []Value
  340. func (b byValue) Len() int { return len(b) }
  341. func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  342. func (b byValue) Less(i, j int) bool {
  343. if b[i].signed {
  344. return int64(b[i].value) < int64(b[j].value)
  345. }
  346. return b[i].value < b[j].value
  347. }
  348. // genDecl processes one declaration clause.
  349. func (f *File) genDecl(node ast.Node) bool {
  350. decl, ok := node.(*ast.GenDecl)
  351. if !ok || decl.Tok != token.CONST {
  352. // We only care about const declarations.
  353. return true
  354. }
  355. // The name of the type of the constants we are declaring.
  356. // Can change if this is a multi-element declaration.
  357. typ := ""
  358. // Loop over the elements of the declaration. Each element is a ValueSpec:
  359. // a list of names possibly followed by a type, possibly followed by values.
  360. // If the type and value are both missing, we carry down the type (and value,
  361. // but the "go/types" package takes care of that).
  362. for _, spec := range decl.Specs {
  363. vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
  364. if vspec.Type == nil && len(vspec.Values) > 0 {
  365. // "X = 1". With no type but a value. If the constant is untyped,
  366. // skip this vspec and reset the remembered type.
  367. typ = ""
  368. // If this is a simple type conversion, remember the type.
  369. // We don't mind if this is actually a call; a qualified call won't
  370. // be matched (that will be SelectorExpr, not Ident), and only unusual
  371. // situations will result in a function call that appears to be
  372. // a type conversion.
  373. ce, ok := vspec.Values[0].(*ast.CallExpr)
  374. if !ok {
  375. continue
  376. }
  377. id, ok := ce.Fun.(*ast.Ident)
  378. if !ok {
  379. continue
  380. }
  381. typ = id.Name
  382. }
  383. if vspec.Type != nil {
  384. // "X T". We have a type. Remember it.
  385. ident, ok := vspec.Type.(*ast.Ident)
  386. if !ok {
  387. continue
  388. }
  389. typ = ident.Name
  390. }
  391. if typ != f.typeName {
  392. // This is not the type we're looking for.
  393. continue
  394. }
  395. // We now have a list of names (from one line of source code) all being
  396. // declared with the desired type.
  397. // Grab their names and actual values and store them in f.values.
  398. for _, name := range vspec.Names {
  399. if name.Name == "_" {
  400. continue
  401. }
  402. // This dance lets the type checker find the values for us. It's a
  403. // bit tricky: look up the object declared by the name, find its
  404. // types.Const, and extract its value.
  405. obj, ok := f.pkg.defs[name]
  406. if !ok {
  407. log.Fatalf("no value for constant %s", name)
  408. }
  409. info := obj.Type().Underlying().(*types.Basic).Info()
  410. if info&types.IsInteger == 0 {
  411. log.Fatalf("can't handle non-integer constant type %s", typ)
  412. }
  413. value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
  414. if value.Kind() != constant.Int {
  415. log.Fatalf("can't happen: constant is not an integer %s", name)
  416. }
  417. i64, isInt := constant.Int64Val(value)
  418. u64, isUint := constant.Uint64Val(value)
  419. if !isInt && !isUint {
  420. log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
  421. }
  422. if !isInt {
  423. u64 = uint64(i64)
  424. }
  425. v := Value{
  426. originalName: name.Name,
  427. value: u64,
  428. signed: info&types.IsUnsigned == 0,
  429. str: value.String(),
  430. }
  431. if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
  432. v.name = strings.TrimSpace(c.Text())
  433. } else {
  434. v.name = strings.TrimPrefix(v.originalName, f.trimPrefix)
  435. }
  436. f.values = append(f.values, v)
  437. }
  438. }
  439. return false
  440. }
  441. // Helpers
  442. // usize returns the number of bits of the smallest unsigned integer
  443. // type that will hold n. Used to create the smallest possible slice of
  444. // integers to use as indexes into the concatenated strings.
  445. func usize(n int) int {
  446. switch {
  447. case n < 1<<8:
  448. return 8
  449. case n < 1<<16:
  450. return 16
  451. default:
  452. // 2^32 is enough constants for anyone.
  453. return 32
  454. }
  455. }
  456. // declareIndexAndNameVars declares the index slices and concatenated names
  457. // strings representing the runs of values.
  458. func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
  459. var indexes, names []string
  460. for i, run := range runs {
  461. index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
  462. if len(run) != 1 {
  463. indexes = append(indexes, index)
  464. }
  465. names = append(names, name)
  466. }
  467. g.Printf("const (\n")
  468. for _, name := range names {
  469. g.Printf("\t%s\n", name)
  470. }
  471. g.Printf(")\n\n")
  472. if len(indexes) > 0 {
  473. g.Printf("var (")
  474. for _, index := range indexes {
  475. g.Printf("\t%s\n", index)
  476. }
  477. g.Printf(")\n\n")
  478. }
  479. }
  480. // declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
  481. func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
  482. index, name := g.createIndexAndNameDecl(run, typeName, "")
  483. g.Printf("const %s\n", name)
  484. g.Printf("var %s\n", index)
  485. }
  486. // createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
  487. func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
  488. b := new(bytes.Buffer)
  489. indexes := make([]int, len(run))
  490. for i := range run {
  491. b.WriteString(run[i].name)
  492. indexes[i] = b.Len()
  493. }
  494. nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
  495. nameLen := b.Len()
  496. b.Reset()
  497. fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
  498. for i, v := range indexes {
  499. if i > 0 {
  500. fmt.Fprintf(b, ", ")
  501. }
  502. fmt.Fprintf(b, "%d", v)
  503. }
  504. fmt.Fprintf(b, "}")
  505. return b.String(), nameConst
  506. }
  507. // declareNameVars declares the concatenated names string representing all the values in the runs.
  508. func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
  509. g.Printf("const _%s_name%s = \"", typeName, suffix)
  510. for _, run := range runs {
  511. for i := range run {
  512. g.Printf("%s", run[i].name)
  513. }
  514. }
  515. g.Printf("\"\n")
  516. }
  517. // buildOneRun generates the variables and String method for a single run of contiguous values.
  518. func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
  519. values := runs[0]
  520. g.Printf("\n")
  521. g.declareIndexAndNameVar(values, typeName)
  522. // The generated code is simple enough to write as a Printf format.
  523. lessThanZero := ""
  524. if values[0].signed {
  525. lessThanZero = "i < 0 || "
  526. }
  527. if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
  528. g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
  529. } else {
  530. g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
  531. }
  532. }
  533. // Arguments to format are:
  534. //
  535. // [1]: type name
  536. // [2]: size of index element (8 for uint8 etc.)
  537. // [3]: less than zero check (for signed types)
  538. const stringOneRun = `func (i %[1]s) String() string {
  539. if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
  540. return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
  541. }
  542. return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
  543. }
  544. `
  545. // Arguments to format are:
  546. // [1]: type name
  547. // [2]: lowest defined value for type, as a string
  548. // [3]: size of index element (8 for uint8 etc.)
  549. // [4]: less than zero check (for signed types)
  550. /*
  551. */
  552. const stringOneRunWithOffset = `func (i %[1]s) String() string {
  553. i -= %[2]s
  554. if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
  555. return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
  556. }
  557. return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
  558. }
  559. `
  560. // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
  561. // For this pattern, a single Printf format won't do.
  562. func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
  563. g.Printf("\n")
  564. g.declareIndexAndNameVars(runs, typeName)
  565. g.Printf("func (i %s) String() string {\n", typeName)
  566. g.Printf("\tswitch {\n")
  567. for i, values := range runs {
  568. if len(values) == 1 {
  569. g.Printf("\tcase i == %s:\n", &values[0])
  570. g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
  571. continue
  572. }
  573. if values[0].value == 0 && !values[0].signed {
  574. // For an unsigned lower bound of 0, "0 <= i" would be redundant.
  575. g.Printf("\tcase i <= %s:\n", &values[len(values)-1])
  576. } else {
  577. g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
  578. }
  579. if values[0].value != 0 {
  580. g.Printf("\t\ti -= %s\n", &values[0])
  581. }
  582. g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
  583. typeName, i, typeName, i, typeName, i)
  584. }
  585. g.Printf("\tdefault:\n")
  586. g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
  587. g.Printf("\t}\n")
  588. g.Printf("}\n")
  589. }
  590. // buildMap handles the case where the space is so sparse a map is a reasonable fallback.
  591. // It's a rare situation but has simple code.
  592. func (g *Generator) buildMap(runs [][]Value, typeName string) {
  593. g.Printf("\n")
  594. g.declareNameVars(runs, typeName, "")
  595. g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
  596. n := 0
  597. for _, values := range runs {
  598. for _, value := range values {
  599. g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
  600. n += len(value.name)
  601. }
  602. }
  603. g.Printf("}\n\n")
  604. g.Printf(stringMap, typeName)
  605. }
  606. // Argument to format is the type name.
  607. const stringMap = `func (i %[1]s) String() string {
  608. if str, ok := _%[1]s_map[i]; ok {
  609. return str
  610. }
  611. return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
  612. }
  613. `