diff --git a/.travis.yml b/.travis.yml index 2b3c415..3e5ac13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,4 @@ go: install: - go get github.com/ugorji/go/codec - go get github.com/pquerna/ffjson/fflib/v1 + - go get github.com/golang/lint/golint diff --git a/Makefile b/Makefile index 39d52e3..5a95104 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ generate: root build .root/bin/easyjson -all .root/src/$(PKG)/tests/data.go .root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go .root/bin/easyjson -snake_case .root/src/$(PKG)/tests/snake.go - .root/bin/easyjson -omit_empty .root/src/$(PKG)/tests/omitempty.go + .root/bin/easyjson -omit_empty .root/src/$(PKG)/tests/omitempty.go .root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go test: generate root @@ -36,6 +36,7 @@ test: generate root $(PKG)/gen \ $(PKG)/buffer go test -benchmem -tags use_easyjson -bench . $(PKG)/benchmark + golint -set_exit_status .root/src/$(PKG)/tests/*_easyjson.go bench-other: generate root @go test -benchmem -bench . $(PKG)/benchmark diff --git a/easyjson/main.go b/easyjson/main.go index f82e3f7..8ae9223 100644 --- a/easyjson/main.go +++ b/easyjson/main.go @@ -40,7 +40,7 @@ func generate(fname string) (err error) { if *specifiedName != "" { outName = *specifiedName } - + g := bootstrap.Generator{ BuildTags: *buildTags, PkgPath: p.PkgPath, diff --git a/gen/decoder.go b/gen/decoder.go index 7080715..94c1a9a 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -14,7 +14,7 @@ import ( const minSliceBytes = 64 func (g *Generator) getDecoderName(t reflect.Type) string { - return g.functionName("decode_", t) + return g.functionName("decode", t) } var primitiveDecoders = map[reflect.Kind]string{ @@ -169,7 +169,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field } func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) error { - jsonName := g.namer.GetJSONFieldName(t, f) + jsonName := g.fieldNamer.GetJSONFieldName(t, f) tags := parseFieldTags(f) if tags.omit { @@ -199,7 +199,7 @@ func (g *Generator) genRequiredFieldSet(t reflect.Type, f reflect.StructField) { } func (g *Generator) genRequiredFieldCheck(t reflect.Type, f reflect.StructField) { - jsonName := g.namer.GetJSONFieldName(t, f) + jsonName := g.fieldNamer.GetJSONFieldName(t, f) tags := parseFieldTags(f) if !tags.required { @@ -368,6 +368,7 @@ func (g *Generator) genStructUnmarshaller(t reflect.Type) error { typ := g.getType(t) if !g.noStdMarshalers { + fmt.Fprintln(g.out, "// UnmarshalJSON supports json.Unmarshaler interface") fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalJSON(data []byte) error {") fmt.Fprintln(g.out, " r := jlexer.Lexer{Data: data}") fmt.Fprintln(g.out, " "+fname+"(&r, v)") @@ -375,6 +376,7 @@ func (g *Generator) genStructUnmarshaller(t reflect.Type) error { fmt.Fprintln(g.out, "}") } + fmt.Fprintln(g.out, "// UnmarshalEasyJSON supports easyjson.Unmarshaler interface") fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalEasyJSON(l *jlexer.Lexer) {") fmt.Fprintln(g.out, " "+fname+"(l, v)") fmt.Fprintln(g.out, "}") diff --git a/gen/encoder.go b/gen/encoder.go index b6cdee6..e5b6ab3 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -11,7 +11,7 @@ import ( ) func (g *Generator) getEncoderName(t reflect.Type) string { - return g.functionName("encode_", t) + return g.functionName("encode", t) } var primitiveEncoders = map[reflect.Kind]string{ @@ -155,14 +155,14 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT fmt.Fprintln(g.out, ws+" out.RawString(`null`)") fmt.Fprintln(g.out, ws+"} else {") fmt.Fprintln(g.out, ws+" out.RawByte('{')") - fmt.Fprintln(g.out, ws+" "+tmpVar+"_first := true") - fmt.Fprintln(g.out, ws+" for "+tmpVar+"_name, "+tmpVar+"_value := range "+in+" {") - fmt.Fprintln(g.out, ws+" if !"+tmpVar+"_first { out.RawByte(',') }") - fmt.Fprintln(g.out, ws+" "+tmpVar+"_first = false") - fmt.Fprintln(g.out, ws+" out.String(string("+tmpVar+"_name))") + fmt.Fprintln(g.out, ws+" "+tmpVar+"First := true") + fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {") + fmt.Fprintln(g.out, ws+" if !"+tmpVar+"First { out.RawByte(',') }") + fmt.Fprintln(g.out, ws+" "+tmpVar+"First = false") + fmt.Fprintln(g.out, ws+" out.String(string("+tmpVar+"Name))") fmt.Fprintln(g.out, ws+" out.RawByte(':')") - g.genTypeEncoder(t.Elem(), tmpVar+"_value", tags, indent+2) + g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2) fmt.Fprintln(g.out, ws+" }") fmt.Fprintln(g.out, ws+" out.RawByte('}')") @@ -207,7 +207,7 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string { } func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) error { - jsonName := g.namer.GetJSONFieldName(t, f) + jsonName := g.fieldNamer.GetJSONFieldName(t, f) tags := parseFieldTags(f) if tags.omit { @@ -296,6 +296,7 @@ func (g *Generator) genStructMarshaller(t reflect.Type) error { typ := g.getType(t) if !g.noStdMarshalers { + fmt.Fprintln(g.out, "// MarshalJSON supports json.Marshaler interface") fmt.Fprintln(g.out, "func (v "+typ+") MarshalJSON() ([]byte, error) {") fmt.Fprintln(g.out, " w := jwriter.Writer{}") fmt.Fprintln(g.out, " "+fname+"(&w, v)") @@ -303,6 +304,7 @@ func (g *Generator) genStructMarshaller(t reflect.Type) error { fmt.Fprintln(g.out, "}") } + fmt.Fprintln(g.out, "// MarshalEasyJSON supports easyjson.Marshaler interface") fmt.Fprintln(g.out, "func (v "+typ+") MarshalEasyJSON(w *jwriter.Writer) {") fmt.Fprintln(g.out, " "+fname+"(w, v)") fmt.Fprintln(g.out, "}") diff --git a/gen/generator.go b/gen/generator.go index fcf0dda..2a3ef9c 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -27,13 +27,13 @@ type Generator struct { pkgName string pkgPath string buildTags string - funcPrefix string + hashString string varCounter int noStdMarshalers bool omitEmpty bool - namer FieldNamer + fieldNamer FieldNamer // package path to local alias map for tracking imports imports map[string]string @@ -60,7 +60,7 @@ func NewGenerator(filename string) *Generator { pkgLexer: "jlexer", "encoding/json": "json", }, - namer: DefaultFieldNamer{}, + fieldNamer: DefaultFieldNamer{}, marshallers: make(map[reflect.Type]bool), typesSeen: make(map[reflect.Type]bool), functionNames: make(map[string]reflect.Type), @@ -70,7 +70,7 @@ func NewGenerator(filename string) *Generator { // name clashes. hash := fnv.New32() hash.Write([]byte(filename)) - ret.funcPrefix = fmt.Sprintf("easyjson_%x_", hash.Sum32()) + ret.hashString = fmt.Sprintf("%x", hash.Sum32()) return ret } @@ -88,12 +88,12 @@ func (g *Generator) SetBuildTags(tags string) { // SetFieldNamer sets field naming strategy. func (g *Generator) SetFieldNamer(n FieldNamer) { - g.namer = n + g.fieldNamer = n } // UseSnakeCase sets snake_case field naming strategy. func (g *Generator) UseSnakeCase() { - g.namer = SnakeCaseFieldNamer{} + g.fieldNamer = SnakeCaseFieldNamer{} } // NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON @@ -255,7 +255,7 @@ func (g *Generator) uniqueVarName() string { // safeName escapes unsafe characters in pkg/type name and returns a string that can be used // in encoder/decoder names for the type. -func safeName(t reflect.Type) string { +func (g *Generator) safeName(t reflect.Type) string { name := t.PkgPath() if t.Name() == "" { name += "anonymous" @@ -263,15 +263,17 @@ func safeName(t reflect.Type) string { name += "." + t.Name() } - var ret []rune + parts := []string{} + part := []rune{} for _, c := range name { if unicode.IsLetter(c) || unicode.IsDigit(c) { - ret = append(ret, c) - } else { - ret = append(ret, '_') + part = append(part, c) + } else if len(part) > 0 { + parts = append(parts, string(part)) + part = []rune{} } } - return string(ret) + return joinFunctionNameParts(false, parts...) } // functionName returns a function name for a given type with a given prefix. If a function @@ -279,8 +281,8 @@ func safeName(t reflect.Type) string { // // Method is used to track encoder/decoder names for the type. func (g *Generator) functionName(prefix string, t reflect.Type) string { - prefix = g.funcPrefix + prefix - name := prefix + safeName(t) + 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 { @@ -374,3 +376,20 @@ func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructFiel 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() +} diff --git a/gen/generator_test.go b/gen/generator_test.go index 7388bec..d7b6b0f 100644 --- a/gen/generator_test.go +++ b/gen/generator_test.go @@ -26,5 +26,24 @@ func TestCamelToSnake(t *testing.T) { t.Errorf("[%d] camelToSnake(%s) = %s; want %s", i, test.In, got, test.Out) } } +} +func TestJoinFunctionNameParts(t *testing.T) { + for i, test := range []struct { + keepFirst bool + parts []string + out string + }{ + {false, []string{}, ""}, + {false, []string{"a"}, "A"}, + {false, []string{"simple", "example"}, "SimpleExample"}, + {true, []string{"first", "example"}, "firstExample"}, + {false, []string{"some", "UPPER", "case"}, "SomeUPPERCase"}, + {false, []string{"number", "123"}, "Number123"}, + } { + got := joinFunctionNameParts(test.keepFirst, test.parts...) + if got != test.out { + t.Errorf("[%d] joinFunctionNameParts(%v) = %s; want %s", i, test.parts, got, test.out) + } + } }