mirror of
https://github.com/netbirdio/easyjson.git
synced 2026-05-22 18:44:42 -07:00
Added camel_case_functions flag which make CamelCase function names (according to default Go naming convention)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, "_")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ var testCases = []struct {
|
||||
{&unexportedStructValue, unexportedStructString},
|
||||
{&excludedFieldValue, excludedFieldString},
|
||||
{&mapsValue, mapsString},
|
||||
{&camelCasesFunctionsValue, camelCasesFunctionsString},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package tests
|
||||
|
||||
//easyjson:json
|
||||
type CamelCasesFunctions struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
var camelCasesFunctionsValue = CamelCasesFunctions{Field: "test"}
|
||||
var camelCasesFunctionsString = `{"Field":"test"}`
|
||||
Reference in New Issue
Block a user