diff --git a/README.md b/README.md index 0e8a563..d19751e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ Usage of easyjson: process the whole package instead of just the given file -snake_case use snake_case names instead of CamelCase by default + -lower_camel_case + use lowerCamelCase instead of CamelCase by default -stubs only generate stubs for marshaler/unmarshaler funcs ``` diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 0322511..3c20e09 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -24,6 +24,7 @@ type Generator struct { NoStdMarshalers bool SnakeCase bool + LowerCamelCase bool OmitEmpty bool OutName string @@ -110,6 +111,9 @@ func (g *Generator) writeMain() (path string, err error) { if g.SnakeCase { fmt.Fprintln(f, " g.UseSnakeCase()") } + if g.LowerCamelCase { + fmt.Fprintln(f, " g.UseLowerCamelCase()") + } if g.OmitEmpty { fmt.Fprintln(f, " g.OmitEmpty()") } diff --git a/easyjson/main.go b/easyjson/main.go index 6287926..1c39497 100644 --- a/easyjson/main.go +++ b/easyjson/main.go @@ -18,6 +18,7 @@ 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 lowerCamelCase = flag.Bool("lower_camel_case", false, "use lowerCamelCase names instead of CamelCase by default") var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON funcs") var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default") var allStructs = flag.Bool("all", false, "generate marshaler/unmarshalers for all structs in a file") @@ -59,6 +60,7 @@ func generate(fname string) (err error) { PkgName: p.PkgName, Types: p.StructNames, SnakeCase: *snakeCase, + LowerCamelCase: *lowerCamelCase, NoStdMarshalers: *noStdMarshalers, OmitEmpty: *omitEmpty, LeaveTemps: *leaveTemps, diff --git a/gen/generator.go b/gen/generator.go index 988a3a5..cae11b6 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -99,6 +99,11 @@ 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() { @@ -374,6 +379,74 @@ func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) } } +// LowerCamelCaseFieldNamer +type LowerCamelCaseFieldNamer struct {} + +func isLower(b byte) bool { + return b <= 122 && b >= 97 +} + +func isUpper(b byte) bool { + return b >= 65 && b <= 90 +} + +func isNumeric(b byte) bool { + return b >= 48 && b <= 57 +} +// 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{} diff --git a/gen/generator_test.go b/gen/generator_test.go index 62c03f0..0c9d278 100644 --- a/gen/generator_test.go +++ b/gen/generator_test.go @@ -28,6 +28,28 @@ func TestCamelToSnake(t *testing.T) { } } +func TestCamelToLowerCamel(t *testing.T) { + for i, test := range []struct { + In, Out string + }{ + {"", ""}, + {"A", "a"}, + {"SimpleExample", "simpleExample"}, + {"internalField", "internalField"}, + + {"SomeHTTPStuff", "someHTTPStuff"}, + {"WriteJSON", "writeJSON"}, + {"HTTP2Server", "http2Server"}, + + {"JSONHTTPRPCServer", "jsonhttprpcServer"}, // nothing can be done here without a dictionary + } { + got := lowerFirst(test.In) + if got != test.Out { + t.Errorf("[%d] lowerFirst(%s) = %s; want %s", i, test.In, got, test.Out) + } + } +} + func TestJoinFunctionNameParts(t *testing.T) { for i, test := range []struct { keepFirst bool