summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mailru/easyjson/gen/generator.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mailru/easyjson/gen/generator.go')
-rw-r--r--vendor/github.com/mailru/easyjson/gen/generator.go523
1 files changed, 523 insertions, 0 deletions
diff --git a/vendor/github.com/mailru/easyjson/gen/generator.go b/vendor/github.com/mailru/easyjson/gen/generator.go
new file mode 100644
index 000000000..eb0d70ba2
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/gen/generator.go
@@ -0,0 +1,523 @@
+package gen
+
+import (
+ "bytes"
+ "fmt"
+ "hash/fnv"
+ "io"
+ "path"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+const pkgWriter = "github.com/mailru/easyjson/jwriter"
+const pkgLexer = "github.com/mailru/easyjson/jlexer"
+const pkgEasyJSON = "github.com/mailru/easyjson"
+
+// FieldNamer defines a policy for generating names for struct fields.
+type FieldNamer interface {
+ GetJSONFieldName(t reflect.Type, f reflect.StructField) string
+}
+
+// Generator generates the requested marshaler/unmarshalers.
+type Generator struct {
+ out *bytes.Buffer
+
+ pkgName string
+ pkgPath string
+ buildTags string
+ hashString string
+
+ varCounter int
+
+ noStdMarshalers bool
+ omitEmpty bool
+ fieldNamer FieldNamer
+
+ // package path to local alias map for tracking imports
+ imports map[string]string
+
+ // types that marshalers were requested for by user
+ marshalers map[reflect.Type]bool
+
+ // types that encoders were already generated for
+ typesSeen map[reflect.Type]bool
+
+ // types that encoders were requested for (e.g. by encoders of other types)
+ typesUnseen []reflect.Type
+
+ // function name to relevant type maps to track names of de-/encoders in
+ // case of a name clash or unnamed structs
+ functionNames map[string]reflect.Type
+}
+
+// NewGenerator initializes and returns a Generator.
+func NewGenerator(filename string) *Generator {
+ ret := &Generator{
+ imports: map[string]string{
+ pkgWriter: "jwriter",
+ pkgLexer: "jlexer",
+ pkgEasyJSON: "easyjson",
+ "encoding/json": "json",
+ },
+ fieldNamer: DefaultFieldNamer{},
+ marshalers: make(map[reflect.Type]bool),
+ typesSeen: make(map[reflect.Type]bool),
+ functionNames: make(map[string]reflect.Type),
+ }
+
+ // Use a file-unique prefix on all auxiliary funcs to avoid
+ // name clashes.
+ hash := fnv.New32()
+ hash.Write([]byte(filename))
+ ret.hashString = fmt.Sprintf("%x", hash.Sum32())
+
+ return ret
+}
+
+// SetPkg sets the name and path of output package.
+func (g *Generator) SetPkg(name, path string) {
+ g.pkgName = name
+ g.pkgPath = path
+}
+
+// SetBuildTags sets build tags for the output file.
+func (g *Generator) SetBuildTags(tags string) {
+ g.buildTags = tags
+}
+
+// SetFieldNamer sets field naming strategy.
+func (g *Generator) SetFieldNamer(n FieldNamer) {
+ g.fieldNamer = n
+}
+
+// UseSnakeCase sets snake_case field naming strategy.
+func (g *Generator) UseSnakeCase() {
+ g.fieldNamer = SnakeCaseFieldNamer{}
+}
+
+// UseLowerCamelCase sets lowerCamelCase field naming strategy.
+func (g *Generator) UseLowerCamelCase() {
+ g.fieldNamer = LowerCamelCaseFieldNamer{}
+}
+
+// NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON
+// methods (only the custom interface).
+func (g *Generator) NoStdMarshalers() {
+ g.noStdMarshalers = true
+}
+
+// OmitEmpty triggers `json=",omitempty"` behaviour by default.
+func (g *Generator) OmitEmpty() {
+ g.omitEmpty = true
+}
+
+// addTypes requests to generate encoding/decoding funcs for the given type.
+func (g *Generator) addType(t reflect.Type) {
+ if g.typesSeen[t] {
+ return
+ }
+ for _, t1 := range g.typesUnseen {
+ if t1 == t {
+ return
+ }
+ }
+ g.typesUnseen = append(g.typesUnseen, t)
+}
+
+// Add requests to generate marshaler/unmarshalers and encoding/decoding
+// funcs for the type of given object.
+func (g *Generator) Add(obj interface{}) {
+ t := reflect.TypeOf(obj)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ g.addType(t)
+ g.marshalers[t] = true
+}
+
+// printHeader prints package declaration and imports.
+func (g *Generator) printHeader() {
+ if g.buildTags != "" {
+ fmt.Println("// +build ", g.buildTags)
+ fmt.Println()
+ }
+ fmt.Println("// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.")
+ fmt.Println()
+ fmt.Println("package ", g.pkgName)
+ fmt.Println()
+
+ byAlias := map[string]string{}
+ var aliases []string
+ for path, alias := range g.imports {
+ aliases = append(aliases, alias)
+ byAlias[alias] = path
+ }
+
+ sort.Strings(aliases)
+ fmt.Println("import (")
+ for _, alias := range aliases {
+ fmt.Printf(" %s %q\n", alias, byAlias[alias])
+ }
+
+ fmt.Println(")")
+ fmt.Println("")
+ fmt.Println("// suppress unused package warning")
+ fmt.Println("var (")
+ fmt.Println(" _ *json.RawMessage")
+ fmt.Println(" _ *jlexer.Lexer")
+ fmt.Println(" _ *jwriter.Writer")
+ fmt.Println(" _ easyjson.Marshaler")
+ fmt.Println(")")
+
+ fmt.Println()
+}
+
+// Run runs the generator and outputs generated code to out.
+func (g *Generator) Run(out io.Writer) error {
+ g.out = &bytes.Buffer{}
+
+ for len(g.typesUnseen) > 0 {
+ t := g.typesUnseen[len(g.typesUnseen)-1]
+ g.typesUnseen = g.typesUnseen[:len(g.typesUnseen)-1]
+ g.typesSeen[t] = true
+
+ if err := g.genDecoder(t); err != nil {
+ return err
+ }
+ if err := g.genEncoder(t); err != nil {
+ return err
+ }
+
+ if !g.marshalers[t] {
+ continue
+ }
+
+ if err := g.genStructMarshaler(t); err != nil {
+ return err
+ }
+ if err := g.genStructUnmarshaler(t); err != nil {
+ return err
+ }
+ }
+ g.printHeader()
+ _, err := out.Write(g.out.Bytes())
+ return err
+}
+
+// fixes vendored paths
+func fixPkgPathVendoring(pkgPath string) string {
+ const vendor = "/vendor/"
+ if i := strings.LastIndex(pkgPath, vendor); i != -1 {
+ return pkgPath[i+len(vendor):]
+ }
+ return pkgPath
+}
+
+func fixAliasName(alias string) string {
+ alias = strings.Replace(
+ strings.Replace(alias, ".", "_", -1),
+ "-",
+ "_",
+ -1,
+ )
+
+ if alias[0] == 'v' { // to void conflicting with var names, say v1
+ alias = "_" + alias
+ }
+ return alias
+}
+
+// pkgAlias creates and returns and import alias for a given package.
+func (g *Generator) pkgAlias(pkgPath string) string {
+ pkgPath = fixPkgPathVendoring(pkgPath)
+ if alias := g.imports[pkgPath]; alias != "" {
+ return alias
+ }
+
+ for i := 0; ; i++ {
+ alias := fixAliasName(path.Base(pkgPath))
+ if i > 0 {
+ alias += fmt.Sprint(i)
+ }
+
+ exists := false
+ for _, v := range g.imports {
+ if v == alias {
+ exists = true
+ break
+ }
+ }
+
+ if !exists {
+ g.imports[pkgPath] = alias
+ return alias
+ }
+ }
+}
+
+// getType return the textual type name of given type that can be used in generated code.
+func (g *Generator) getType(t reflect.Type) string {
+ if t.Name() == "" {
+ switch t.Kind() {
+ case reflect.Ptr:
+ return "*" + g.getType(t.Elem())
+ case reflect.Slice:
+ return "[]" + g.getType(t.Elem())
+ case reflect.Array:
+ return "[" + strconv.Itoa(t.Len()) + "]" + g.getType(t.Elem())
+ case reflect.Map:
+ return "map[" + g.getType(t.Key()) + "]" + g.getType(t.Elem())
+ }
+ }
+
+ if t.Name() == "" || t.PkgPath() == "" {
+ if t.Kind() == reflect.Struct {
+ // the fields of an anonymous struct can have named types,
+ // and t.String() will not be sufficient because it does not
+ // remove the package name when it matches g.pkgPath.
+ // so we convert by hand
+ nf := t.NumField()
+ lines := make([]string, 0, nf)
+ for i := 0; i < nf; i++ {
+ f := t.Field(i)
+ line := f.Name + " " + g.getType(f.Type)
+ t := f.Tag
+ if t != "" {
+ line += " " + escapeTag(t)
+ }
+ lines = append(lines, line)
+ }
+ return strings.Join([]string{"struct { ", strings.Join(lines, "; "), " }"}, "")
+ }
+ return t.String()
+ } else if t.PkgPath() == g.pkgPath {
+ return t.Name()
+ }
+ return g.pkgAlias(t.PkgPath()) + "." + t.Name()
+}
+
+// escape a struct field tag string back to source code
+func escapeTag(tag reflect.StructTag) string {
+ t := string(tag)
+ if strings.ContainsRune(t, '`') {
+ // there are ` in the string; we can't use ` to enclose the string
+ return strconv.Quote(t)
+ }
+ return "`" + t + "`"
+}
+
+// uniqueVarName returns a file-unique name that can be used for generated variables.
+func (g *Generator) uniqueVarName() string {
+ g.varCounter++
+ return fmt.Sprint("v", g.varCounter)
+}
+
+// safeName escapes unsafe characters in pkg/type name and returns a string that can be used
+// in encoder/decoder names for the type.
+func (g *Generator) safeName(t reflect.Type) string {
+ name := t.PkgPath()
+ if t.Name() == "" {
+ name += "anonymous"
+ } else {
+ name += "." + t.Name()
+ }
+
+ parts := []string{}
+ part := []rune{}
+ for _, c := range name {
+ if unicode.IsLetter(c) || unicode.IsDigit(c) {
+ part = append(part, c)
+ } else if len(part) > 0 {
+ parts = append(parts, string(part))
+ part = []rune{}
+ }
+ }
+ return joinFunctionNameParts(false, parts...)
+}
+
+// functionName returns a function name for a given type with a given prefix. If a function
+// with this prefix already exists for a type, it is returned.
+//
+// Method is used to track encoder/decoder names for the type.
+func (g *Generator) functionName(prefix string, t reflect.Type) string {
+ prefix = joinFunctionNameParts(true, "easyjson", g.hashString, prefix)
+ name := joinFunctionNameParts(true, prefix, g.safeName(t))
+
+ // Most of the names will be unique, try a shortcut first.
+ if e, ok := g.functionNames[name]; !ok || e == t {
+ g.functionNames[name] = t
+ return name
+ }
+
+ // Search if the function already exists.
+ for name1, t1 := range g.functionNames {
+ if t1 == t && strings.HasPrefix(name1, prefix) {
+ return name1
+ }
+ }
+
+ // Create a new name in the case of a clash.
+ for i := 1; ; i++ {
+ nm := fmt.Sprint(name, i)
+ if _, ok := g.functionNames[nm]; ok {
+ continue
+ }
+ g.functionNames[nm] = t
+ return nm
+ }
+}
+
+// DefaultFieldsNamer implements trivial naming policy equivalent to encoding/json.
+type DefaultFieldNamer struct{}
+
+func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
+ jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
+ if jsonName != "" {
+ return jsonName
+ } else {
+ return f.Name
+ }
+}
+
+// LowerCamelCaseFieldNamer
+type LowerCamelCaseFieldNamer struct{}
+
+func isLower(b byte) bool {
+ return b <= 122 && b >= 97
+}
+
+func isUpper(b byte) bool {
+ return b >= 65 && b <= 90
+}
+
+// convert HTTPRestClient to httpRestClient
+func lowerFirst(s string) string {
+ if s == "" {
+ return ""
+ }
+
+ str := ""
+ strlen := len(s)
+
+ /**
+ Loop each char
+ If is uppercase:
+ If is first char, LOWER it
+ If the following char is lower, LEAVE it
+ If the following char is upper OR numeric, LOWER it
+ If is the end of string, LEAVE it
+ Else lowercase
+ */
+
+ foundLower := false
+ for i := range s {
+ ch := s[i]
+ if isUpper(ch) {
+ if i == 0 {
+ str += string(ch + 32)
+ } else if !foundLower { // Currently just a stream of capitals, eg JSONRESTS[erver]
+ if strlen > (i+1) && isLower(s[i+1]) {
+ // Next char is lower, keep this a capital
+ str += string(ch)
+ } else {
+ // Either at end of string or next char is capital
+ str += string(ch + 32)
+ }
+ } else {
+ str += string(ch)
+ }
+ } else {
+ foundLower = true
+ str += string(ch)
+ }
+ }
+
+ return str
+}
+
+func (LowerCamelCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
+ jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
+ if jsonName != "" {
+ return jsonName
+ } else {
+ return lowerFirst(f.Name)
+ }
+}
+
+// SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names.
+type SnakeCaseFieldNamer struct{}
+
+func camelToSnake(name string) string {
+ var ret bytes.Buffer
+
+ multipleUpper := false
+ var lastUpper rune
+ var beforeUpper rune
+
+ for _, c := range name {
+ // Non-lowercase character after uppercase is considered to be uppercase too.
+ isUpper := (unicode.IsUpper(c) || (lastUpper != 0 && !unicode.IsLower(c)))
+
+ if lastUpper != 0 {
+ // Output a delimiter if last character was either the first uppercase character
+ // in a row, or the last one in a row (e.g. 'S' in "HTTPServer").
+ // Do not output a delimiter at the beginning of the name.
+
+ firstInRow := !multipleUpper
+ lastInRow := !isUpper
+
+ if ret.Len() > 0 && (firstInRow || lastInRow) && beforeUpper != '_' {
+ ret.WriteByte('_')
+ }
+ ret.WriteRune(unicode.ToLower(lastUpper))
+ }
+
+ // Buffer uppercase char, do not output it yet as a delimiter may be required if the
+ // next character is lowercase.
+ if isUpper {
+ multipleUpper = (lastUpper != 0)
+ lastUpper = c
+ continue
+ }
+
+ ret.WriteRune(c)
+ lastUpper = 0
+ beforeUpper = c
+ multipleUpper = false
+ }
+
+ if lastUpper != 0 {
+ ret.WriteRune(unicode.ToLower(lastUpper))
+ }
+ return string(ret.Bytes())
+}
+
+func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
+ jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
+ if jsonName != "" {
+ return jsonName
+ }
+
+ return camelToSnake(f.Name)
+}
+
+func joinFunctionNameParts(keepFirst bool, parts ...string) string {
+ buf := bytes.NewBufferString("")
+ for i, part := range parts {
+ if i == 0 && keepFirst {
+ buf.WriteString(part)
+ } else {
+ if len(part) > 0 {
+ buf.WriteString(strings.ToUpper(string(part[0])))
+ }
+ if len(part) > 1 {
+ buf.WriteString(part[1:])
+ }
+ }
+ }
+ return buf.String()
+}