<commit_before>// Package strings generates the String() method for enumeration types.
package strings

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"path/filepath"
	"regexp"
	"strings"
)

type Transform int

const (
	ToLower Transform = iota + 1
	ToUpper
)

type TypeOptions struct {
	Transform  Transform
	SliceBegin int
	SliceEnd   int
}

type Options struct {
	Include     *regexp.Regexp
	Exclude     *regexp.Regexp
	TypeOptions map[string]*TypeOptions
}

func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import \"fmt\"\n\nvar _ = fmt.Sprintf\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	scope := pkg.Scope()
	for _, v := range pkg.Types(include, exclude) {
		methods.Reset()
		if err := genString(v, scope, opts, &methods); err != nil {
			log.Warningf("Skipping %v: %s", v, err)
		}
		buf.WriteString(methods.String())
	}
	out := filepath.Join(pkg.Dir(), "gen_strings.go")
	log.Debugf("Writing autogenerated String() methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func genString(named *types.Named, scope *types.Scope, opts *Options, buf *bytes.Buffer) error {
	var values []string
	for _, v := range scope.Names() {
		obj := scope.Lookup(v)
		if _, ok := obj.(*types.Const); ok {
			if typ, ok := obj.Type().(*types.Named); ok && typ == named {
				values = append(values, obj.Name())
			}
		}
	}
	names := make([]string, len(values))
	copy(names, values)
	name := named.Obj().Name()
	if opts != nil {
		if typOptions := opts.TypeOptions[name]; typOptions != nil {
			sl := sliceFunc(typOptions.SliceBegin, typOptions.SliceEnd)
			for ii, v := range values {
				values[ii] = sl(v)
			}
			switch typOptions.Transform {
			case ToLower:
				for ii, v := range values {
					values[ii] = strings.ToLower(v)
				}
			case ToUpper:
				for ii, v := range values {
					values[ii] = strings.ToUpper(v)
				}
			}
		}
	}
	if len(values) > 0 {
		varname := strings.ToLower(name[:1])
		buf.WriteString(fmt.Sprintf("func (%s %s) String() string {\n", varname, name))
		buf.WriteString(fmt.Sprintf("switch %s {\n", varname))
		for ii, v := range values {
			buf.WriteString(fmt.Sprintf("case %s:\n", names[ii]))
			buf.WriteString(fmt.Sprintf("return %q\n", v))
		}
		buf.WriteString("}\n")
		fmt.Fprintf(buf, "return fmt.Sprintf(\"unknown %s %%v\", %s)", strings.ToLower(name), varname)
		buf.WriteString("}\n")
	}
	return nil
}

func sliceFunc(begin, end int) func(string) string {
	if end > 0 {
		return func(s string) string {
			return s[begin:end]
		}
	}<commit_msg>Avoid an infinite loop on unknown values<commit_after>// Package strings generates the String() method for enumeration types.
package strings

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"path/filepath"
	"regexp"
	"strings"
)

type Transform int

const (
	ToLower Transform = iota + 1
	ToUpper
)

type TypeOptions struct {
	Transform  Transform
	SliceBegin int
	SliceEnd   int
}

type Options struct {
	Include     *regexp.Regexp
	Exclude     *regexp.Regexp
	TypeOptions map[string]*TypeOptions
}

func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import \"fmt\"\n\nvar _ = fmt.Sprintf\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	scope := pkg.Scope()
	for _, v := range pkg.Types(include, exclude) {
		methods.Reset()
		if err := genString(v, scope, opts, &methods); err != nil {
			log.Warningf("Skipping %v: %s", v, err)
		}
		buf.WriteString(methods.String())
	}
	out := filepath.Join(pkg.Dir(), "gen_strings.go")
	log.Debugf("Writing autogenerated String() methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func genString(named *types.Named, scope *types.Scope, opts *Options, buf *bytes.Buffer) error {
	var values []string
	for _, v := range scope.Names() {
		obj := scope.Lookup(v)
		if _, ok := obj.(*types.Const); ok {
			if typ, ok := obj.Type().(*types.Named); ok && typ == named {
				values = append(values, obj.Name())
			}
		}
	}
	names := make([]string, len(values))
	copy(names, values)
	name := named.Obj().Name()
	if opts != nil {
		if typOptions := opts.TypeOptions[name]; typOptions != nil {
			sl := sliceFunc(typOptions.SliceBegin, typOptions.SliceEnd)
			for ii, v := range values {
				values[ii] = sl(v)
			}
			switch typOptions.Transform {
			case ToLower:
				for ii, v := range values {
					values[ii] = strings.ToLower(v)
				}
			case ToUpper:
				for ii, v := range values {
					values[ii] = strings.ToUpper(v)
				}
			}
		}
	}
	if len(values) > 0 {
		varname := strings.ToLower(name[:1])
		buf.WriteString(fmt.Sprintf("func (%s %s) String() string {\n", varname, name))
		buf.WriteString(fmt.Sprintf("switch %s {\n", varname))
		for ii, v := range values {
			buf.WriteString(fmt.Sprintf("case %s:\n", names[ii]))
			buf.WriteString(fmt.Sprintf("return %q\n", v))
		}
		buf.WriteString("}\n")
		fmt.Fprintf(buf, "return fmt.Sprintf(\"unknown %s %%d\", %s)", strings.ToLower(name), varname)
		buf.WriteString("}\n")
	}
	return nil
}

func sliceFunc(begin, end int) func(string) string {
	if end > 0 {
		return func(s string) string {
			return s[begin:end]
		}
	}
{'fraction_non_alphanumeric': 0.15977866159432821, 'fraction_numerical': 0.0013833650354487291, 'mean_word_length': 3.660757453666398, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2605173', 'n_tokens_mistral': 2250, 'n_tokens_neox': 2074, 'n_words': 518}