golang.org/x/tools@v0.21.0/cmd/stringer/stringer.go (about)

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