Added camel_case_functions flag which make CamelCase function names (according to default Go naming convention)

This commit is contained in:
Vadim Petrov
2016-06-08 18:08:55 +03:00
parent cd72c97ec0
commit 6ee8be15d5
9 changed files with 153 additions and 47 deletions
+4 -2
View File
@@ -20,11 +20,13 @@ generate: root build
.root/bin/easyjson -stubs \
.root/src/$(PKG)/tests/snake.go \
.root/src/$(PKG)/tests/data.go \
.root/src/$(PKG)/tests/omitempty.go
.root/src/$(PKG)/tests/omitempty.go \
.root/src/$(PKG)/tests/camel_case_functions.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.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 -camel_case_functions .root/src/$(PKG)/tests/camel_case_functions.go
.root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go
test: generate root
+9 -5
View File
@@ -21,9 +21,10 @@ type Generator struct {
PkgPath, PkgName string
Types []string
NoStdMarshalers bool
SnakeCase bool
OmitEmpty bool
NoStdMarshalers bool
SnakeCaseFields bool
CamelCaseFunctions bool
OmitEmpty bool
OutName string
BuildTags string
@@ -100,8 +101,11 @@ func (g *Generator) writeMain() (path string, err error) {
if g.BuildTags != "" {
fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags)
}
if g.SnakeCase {
fmt.Fprintln(f, " g.UseSnakeCase()")
if g.SnakeCaseFields {
fmt.Fprintln(f, " g.UseSnakeCaseFieldNamer()")
}
if g.CamelCaseFunctions {
fmt.Fprintln(f, " g.UseCamelCaseFunctionNamer()")
}
if g.OmitEmpty {
fmt.Fprintln(f, " g.OmitEmpty()")
+15 -13
View File
@@ -15,7 +15,8 @@ import (
)
var buildTags = flag.String("build_tags", "", "build tags to add to generated file")
var snakeCase = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default")
var snakeCaseFields = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default")
var camelCaseFunctions = flag.Bool("camel_case_functions", false, "create functions with CamelCase names instead of under_score by default")
var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON methods")
var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default")
var allStructs = flag.Bool("all", false, "generate un-/marshallers for all structs in a file")
@@ -40,19 +41,20 @@ func generate(fname string) (err error) {
if *specifiedName != "" {
outName = *specifiedName
}
g := bootstrap.Generator{
BuildTags: *buildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
NoStdMarshalers: *noStdMarshalers,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
NoFormat: *noformat,
BuildTags: *buildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCaseFields: *snakeCaseFields,
CamelCaseFunctions: *camelCaseFunctions,
NoStdMarshalers: *noStdMarshalers,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
NoFormat: *noformat,
}
if err := g.Run(); err != nil {
+5 -3
View File
@@ -14,7 +14,7 @@ import (
const minSliceBytes = 64
func (g *Generator) getStructDecoderName(t reflect.Type) string {
return g.functionName("decode_", t)
return g.functionName("decode", t)
}
var primitiveDecoders = map[reflect.Kind]string{
@@ -162,7 +162,7 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i
}
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 {
@@ -192,7 +192,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 {
@@ -334,6 +334,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)")
@@ -341,6 +342,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, "}")
+10 -8
View File
@@ -11,7 +11,7 @@ import (
)
func (g *Generator) getStructEncoderName(t reflect.Type) string {
return g.functionName("encode_", t)
return g.functionName("encode", t)
}
var primitiveEncoders = map[reflect.Kind]string{
@@ -147,14 +147,14 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in
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('}')")
@@ -199,7 +199,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 {
@@ -262,6 +262,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)")
@@ -269,6 +270,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, "}")
+63 -16
View File
@@ -20,6 +20,11 @@ type FieldNamer interface {
GetJSONFieldName(t reflect.Type, f reflect.StructField) string
}
// FuncNamer defines a policy for generating function names
type FunctionNamer interface {
GetName(keepFirst bool, parts ...string) string
}
// Generator generates the requested marshallers/unmarshallers.
type Generator struct {
out *bytes.Buffer
@@ -27,13 +32,14 @@ type Generator struct {
pkgName string
pkgPath string
buildTags string
funcPrefix string
hashString string
varCounter int
noStdMarshalers bool
omitEmpty bool
namer FieldNamer
fieldNamer FieldNamer
functionNamer FunctionNamer
// package path to local alias map for tracking imports
imports map[string]string
@@ -60,7 +66,9 @@ func NewGenerator(filename string) *Generator {
pkgLexer: "jlexer",
"encoding/json": "json",
},
namer: DefaultFieldNamer{},
fieldNamer: DefaultFieldNamer{},
functionNamer: UnderScoreFunctionNamer{},
//functionNamer: CamelCaseFunctionNamer{},
marshallers: make(map[reflect.Type]bool),
typesSeen: make(map[reflect.Type]bool),
functionNames: make(map[string]reflect.Type),
@@ -70,7 +78,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 +96,22 @@ 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{}
// UseSnakeCaseFieldNamer sets snake_case field naming strategy.
func (g *Generator) UseSnakeCaseFieldNamer() {
g.fieldNamer = SnakeCaseFieldNamer{}
}
// SetFunctionNamer sets function naming strategy.
func (g *Generator) SetFunctionNamer(n FunctionNamer) {
g.functionNamer = n
}
// UseCamelCaseFunctionNamer sets CamelCase function naming strategy.
func (g *Generator) UseCamelCaseFunctionNamer() {
g.functionNamer = CamelCaseFunctionNamer{}
}
// NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON
@@ -243,7 +261,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"
@@ -251,15 +269,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 g.functionNamer.GetName(false, parts...)
}
// functionName returns a function name for a given type with a given prefix. If a function
@@ -267,8 +287,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 = g.functionNamer.GetName(true, "easyjson", g.hashString, prefix)
name := g.functionNamer.GetName(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 {
@@ -362,3 +382,30 @@ func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructFiel
return camelToSnake(f.Name)
}
// CamelCaseFunctionNamer implements FunctionNamer interface with CamelCase format
type CamelCaseFunctionNamer struct{}
func (CamelCaseFunctionNamer) GetName(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()
}
// UnderScoreFunctionNamer implements FunctionNamer interface with under_score format
type UnderScoreFunctionNamer struct{}
func (UnderScoreFunctionNamer) GetName(keepFirst bool, parts ...string) string {
return strings.Join(parts, "_")
}
+37
View File
@@ -4,6 +4,13 @@ import (
"testing"
)
type functionNamerCase struct {
keepFirst bool
parts []string
camelCaseOut string
underScoreOut string
}
func TestCamelToSnake(t *testing.T) {
for i, test := range []struct {
In, Out string
@@ -26,5 +33,35 @@ func TestCamelToSnake(t *testing.T) {
t.Errorf("[%d] camelToSnake(%s) = %s; want %s", i, test.In, got, test.Out)
}
}
}
func getFunctionNamerCases() []functionNamerCase {
return []functionNamerCase{
functionNamerCase{false, []string{}, "", ""},
functionNamerCase{false, []string{"a"}, "A", "a"},
functionNamerCase{false, []string{"simple", "example"}, "SimpleExample", "simple_example"},
functionNamerCase{true, []string{"first", "example"}, "firstExample", "first_example"},
functionNamerCase{false, []string{"some", "UPPER", "case"}, "SomeUPPERCase", "some_UPPER_case"},
functionNamerCase{false, []string{"number", "123"}, "Number123", "number_123"},
}
}
func TestCamelCaseFunctionNamer(t *testing.T) {
namer := CamelCaseFunctionNamer{}
for i, test := range getFunctionNamerCases() {
got := namer.GetName(test.keepFirst, test.parts...)
if got != test.camelCaseOut {
t.Errorf("[%d] CamelCaseFunctionNamer.GetName(%v) = %s; want %s", i, test.parts, got, test.camelCaseOut)
}
}
}
func TestUnderScoreFunctionNamer(t *testing.T) {
namer := UnderScoreFunctionNamer{}
for i, test := range getFunctionNamerCases() {
got := namer.GetName(test.keepFirst, test.parts...)
if got != test.underScoreOut {
t.Errorf("[%d] UnderScoreFunctionNamer.GetName(%v) = %s; want %s", i, test.parts, got, test.underScoreOut)
}
}
}
+1
View File
@@ -30,6 +30,7 @@ var testCases = []struct {
{&unexportedStructValue, unexportedStructString},
{&excludedFieldValue, excludedFieldString},
{&mapsValue, mapsString},
{&camelCasesFunctionsValue, camelCasesFunctionsString},
}
func TestMarshal(t *testing.T) {
+9
View File
@@ -0,0 +1,9 @@
package tests
//easyjson:json
type CamelCasesFunctions struct {
Field string
}
var camelCasesFunctionsValue = CamelCasesFunctions{Field: "test"}
var camelCasesFunctionsString = `{"Field":"test"}`