From fe626faec52dd6d33c842cf3a33fd69617a9fb6f Mon Sep 17 00:00:00 2001 From: mkabischev Date: Sun, 15 May 2016 23:02:01 +0300 Subject: [PATCH 1/2] required keys support --- Makefile | 3 +- gen/decoder.go | 65 +++++++++++++++++++++++++++++++++++++----- gen/encoder.go | 3 ++ tests/required.go | 7 +++++ tests/required_test.go | 28 ++++++++++++++++++ 5 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 tests/required.go create mode 100644 tests/required_test.go diff --git a/Makefile b/Makefile index 6192ad8..440c0ea 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,8 @@ generate: root build .root/src/$(PKG)/tests/omitempty.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 -all .root/src/$(PKG)/tests/required.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 -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go diff --git a/gen/decoder.go b/gen/decoder.go index a662329..f178d72 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -170,7 +170,45 @@ func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) } fmt.Fprintf(g.out, " case %q:\n", jsonName) - return g.genTypeDecoder(f.Type, "out."+f.Name, tags, 3) + if err := g.genTypeDecoder(f.Type, "out."+f.Name, tags, 3); err != nil { + return err + } + + if tags.required { + fmt.Fprintf(g.out, "%sSet = true\n", jsonName) + } + + return nil +} + +func (g *Generator) getFieldSetBlock(t reflect.Type, f reflect.StructField) error { + jsonName := g.namer.GetJSONFieldName(t, f) + tags := parseFieldTags(f) + + if !tags.required { + return nil + } + + fmt.Fprintf(g.out, "var %sSet bool\n", jsonName) + + return nil +} + +func (g *Generator) getFieldCheckBlock(t reflect.Type, f reflect.StructField) error { + jsonName := g.namer.GetJSONFieldName(t, f) + tags := parseFieldTags(f) + + if !tags.required { + return nil + } + + g.imports["fmt"] = "fmt" + + fmt.Fprintf(g.out, "if !%sSet {\n", jsonName) + fmt.Fprintf(g.out, " in.AddError(fmt.Errorf(\"key '%s' is required\"))\n", jsonName) + fmt.Fprintf(g.out, "}\n") + + return nil } func mergeStructFields(fields1, fields2 []reflect.StructField) (fields []reflect.StructField) { @@ -250,6 +288,17 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { fmt.Fprintln(g.out, " out."+f.Name+" = new("+g.getType(f.Type.Elem())+")") } + fs, err := getStructFields(t) + if err != nil { + return fmt.Errorf("cannot generate decoder for %v: %v", t, err) + } + + for _, f := range fs { + if err := g.getFieldSetBlock(t, f); err != nil { + return err + } + } + fmt.Fprintln(g.out, " in.Delim('{')") fmt.Fprintln(g.out, " for !in.IsDelim('}') {") fmt.Fprintln(g.out, " key := in.UnsafeString()") @@ -259,13 +308,8 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { fmt.Fprintln(g.out, " in.WantComma()") fmt.Fprintln(g.out, " continue") fmt.Fprintln(g.out, " }") + fmt.Fprintln(g.out, " switch key {") - - fs, err := getStructFields(t) - if err != nil { - return fmt.Errorf("cannot generate decoder for %v: %v", t, err) - } - for _, f := range fs { if err := g.genStructFieldDecoder(t, f); err != nil { return err @@ -278,6 +322,13 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { fmt.Fprintln(g.out, " in.WantComma()") fmt.Fprintln(g.out, " }") fmt.Fprintln(g.out, " in.Delim('}')") + + for _, f := range fs { + if err := g.getFieldCheckBlock(t, f); err != nil { + return err + } + } + fmt.Fprintln(g.out, "}") return nil diff --git a/gen/encoder.go b/gen/encoder.go index dfe21f0..c263c21 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -52,6 +52,7 @@ type fieldTags struct { omitEmpty bool noOmitEmpty bool asString bool + required bool } // parseFieldTags parses the json field tag into a structure. @@ -70,6 +71,8 @@ func parseFieldTags(f reflect.StructField) fieldTags { ret.noOmitEmpty = true case s == "string": ret.asString = true + case s == "required": + ret.required = true } } diff --git a/tests/required.go b/tests/required.go new file mode 100644 index 0000000..a83b907 --- /dev/null +++ b/tests/required.go @@ -0,0 +1,7 @@ +package tests + +//easyjson:json +type RequiredOptionalStruct struct { + FirstName string `json:"first_name,required"` + Lastname string `json:"last_name"` +} diff --git a/tests/required_test.go b/tests/required_test.go new file mode 100644 index 0000000..8b03be6 --- /dev/null +++ b/tests/required_test.go @@ -0,0 +1,28 @@ +package tests + +import ( + "testing" + "fmt" +) + +func TestRequiredField(t *testing.T) { + cases := []struct{ json, errorMessage string }{ + {`{"first_name":"Foo", "last_name": "Bar"}`, ""}, + {`{"last_name":"Bar"}`, "key 'first_name' is required"}, + {"{}", "key 'first_name' is required"}, + } + + for _, tc := range cases { + var v RequiredOptionalStruct + err := v.UnmarshalJSON([]byte(tc.json)) + if tc.errorMessage == "" { + if err != nil { + t.Errorf("%s. UnmarshallJSON didn`t expect error: %v", tc.json, err) + } + } else { + if fmt.Sprintf("%v", err) != tc.errorMessage { + t.Errorf("%s. UnmarshallJSON expected error: %v. got: %v", tc.json, tc.errorMessage, err) + } + } + } +} From da2c02337f4e5d12dbf67cfaaa66fecd0028cb82 Mon Sep 17 00:00:00 2001 From: mkabischev Date: Mon, 16 May 2016 11:06:41 +0300 Subject: [PATCH 2/2] * methods rename * remove unnecessary code --- Makefile | 1 - gen/decoder.go | 27 +++++++++------------------ tests/data.go | 5 +++++ tests/required.go | 7 ------- 4 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 tests/required.go diff --git a/Makefile b/Makefile index 440c0ea..420f306 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,6 @@ generate: root build .root/src/$(PKG)/tests/omitempty.go .root/bin/easyjson -all .root/src/$(PKG)/tests/data.go - .root/bin/easyjson -all .root/src/$(PKG)/tests/required.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 -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go diff --git a/gen/decoder.go b/gen/decoder.go index f178d72..a1291fc 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -175,40 +175,35 @@ func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) } if tags.required { - fmt.Fprintf(g.out, "%sSet = true\n", jsonName) + fmt.Fprintf(g.out, "%sSet = true\n", f.Name) } return nil } -func (g *Generator) getFieldSetBlock(t reflect.Type, f reflect.StructField) error { - jsonName := g.namer.GetJSONFieldName(t, f) +func (g *Generator) genRequiredFieldSet(t reflect.Type, f reflect.StructField) { tags := parseFieldTags(f) if !tags.required { - return nil + return } - fmt.Fprintf(g.out, "var %sSet bool\n", jsonName) - - return nil + fmt.Fprintf(g.out, "var %sSet bool\n", f.Name) } -func (g *Generator) getFieldCheckBlock(t reflect.Type, f reflect.StructField) error { +func (g *Generator) genRequiredFieldCheck(t reflect.Type, f reflect.StructField) { jsonName := g.namer.GetJSONFieldName(t, f) tags := parseFieldTags(f) if !tags.required { - return nil + return } g.imports["fmt"] = "fmt" - fmt.Fprintf(g.out, "if !%sSet {\n", jsonName) + fmt.Fprintf(g.out, "if !%sSet {\n", f.Name) fmt.Fprintf(g.out, " in.AddError(fmt.Errorf(\"key '%s' is required\"))\n", jsonName) fmt.Fprintf(g.out, "}\n") - - return nil } func mergeStructFields(fields1, fields2 []reflect.StructField) (fields []reflect.StructField) { @@ -294,9 +289,7 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { } for _, f := range fs { - if err := g.getFieldSetBlock(t, f); err != nil { - return err - } + g.genRequiredFieldSet(t, f) } fmt.Fprintln(g.out, " in.Delim('{')") @@ -324,9 +317,7 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { fmt.Fprintln(g.out, " in.Delim('}')") for _, f := range fs { - if err := g.getFieldCheckBlock(t, f); err != nil { - return err - } + g.genRequiredFieldCheck(t, f) } fmt.Fprintln(g.out, "}") diff --git a/tests/data.go b/tests/data.go index 73ced30..aa8963f 100644 --- a/tests/data.go +++ b/tests/data.go @@ -435,3 +435,8 @@ var mapsString = `{` + `"NilMap":null,` + `"CustomMap":{"c":"d"}` + `}` + +type RequiredOptionalStruct struct { + FirstName string `json:"first_name,required"` + Lastname string `json:"last_name"` +} diff --git a/tests/required.go b/tests/required.go deleted file mode 100644 index a83b907..0000000 --- a/tests/required.go +++ /dev/null @@ -1,7 +0,0 @@ -package tests - -//easyjson:json -type RequiredOptionalStruct struct { - FirstName string `json:"first_name,required"` - Lastname string `json:"last_name"` -}