From e79a197d22727feb041101a399e1e398dba76e3f Mon Sep 17 00:00:00 2001 From: Guillaume Bailleul Date: Fri, 24 Mar 2017 22:00:42 +0100 Subject: [PATCH] Add strict option --- ffjson.go | 5 +++-- fflib/v1/errors.go | 18 ++++++++++++++++++ generator/generator.go | 8 +++----- generator/inceptionmain.go | 8 ++++++-- inception/decoder.go | 1 + inception/decoder_tpl.go | 14 ++++++++++++++ inception/inception.go | 7 +++++-- 7 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 fflib/v1/errors.go diff --git a/ffjson.go b/ffjson.go index 656a9c3..9cfe05f 100644 --- a/ffjson.go +++ b/ffjson.go @@ -31,9 +31,10 @@ import ( var outputPathFlag = flag.String("w", "", "Write generate code to this path instead of ${input}_ffjson.go.") var goCmdFlag = flag.String("go-cmd", "", "Path to go command; Useful for `goapp` support.") +var strictDecodeFlag = flag.Bool("strict", false, "Returns an error when trying to decode unknow fields.") var importNameFlag = flag.String("import-name", "", "Override import name in case it cannot be detected.") var forceRegenerateFlag = flag.Bool("force-regenerate", false, "Regenerate every input file, without checking modification date.") -var resetFields = flag.Bool("reset-fields", false, "When unmarshalling reset all fields missing in the JSON") +var resetFields = flag.Bool("reset-fields", false, "When unmarshalling reset all fields missing in the JSON.") func usage() { fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0]) @@ -74,7 +75,7 @@ func main() { importName = *importNameFlag } - err := generator.GenerateFiles(goCmd, inputPath, outputPath, importName, *forceRegenerateFlag, *resetFields) + err := generator.GenerateFiles(goCmd, inputPath, outputPath, importName, *forceRegenerateFlag, *resetFields, *strictDecodeFlag) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s:\n\n", err) diff --git a/fflib/v1/errors.go b/fflib/v1/errors.go new file mode 100644 index 0000000..cff0c84 --- /dev/null +++ b/fflib/v1/errors.go @@ -0,0 +1,18 @@ +package v1 + +import "fmt" + +// ErrUnknowFields error is return when some unknow fields are found +type ErrUnknowFields struct { + Fields []string +} + +// NewErrUnknowFields create an instance UnknowFields error +func NewErrUnknowFields(fields []string) *ErrUnknowFields { + return &ErrUnknowFields{ + Fields: fields, + } +} +func (e *ErrUnknowFields) Error() string { + return fmt.Sprintf("unknow fields %v", e.Fields) +} diff --git a/generator/generator.go b/generator/generator.go index 1f50380..124b47d 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -18,13 +18,11 @@ package generator import ( - "errors" "fmt" "os" ) -func GenerateFiles(goCmd string, inputPath string, outputPath string, importName string, forceRegenerate bool, resetFields bool) error { - +func GenerateFiles(goCmd string, inputPath string, outputPath string, importName string, forceRegenerate bool, resetFields bool, strict bool) error { if _, StatErr := os.Stat(outputPath); !os.IsNotExist(StatErr) { inputFileInfo, inputFileErr := os.Stat(inputPath) outputFileInfo, outputFileErr := os.Stat(outputPath) @@ -43,11 +41,11 @@ func GenerateFiles(goCmd string, inputPath string, outputPath string, importName return err } - im := NewInceptionMain(goCmd, inputPath, outputPath, resetFields) + im := NewInceptionMain(goCmd, inputPath, outputPath, resetFields, strict) err = im.Generate(packageName, structs, importName) if err != nil { - return errors.New(fmt.Sprintf("error=%v path=%q", err, im.TempMainPath)) + return fmt.Errorf("error=%v path=%q", err, im.TempMainPath) } err = im.Run() diff --git a/generator/inceptionmain.go b/generator/inceptionmain.go index f5f140b..19f1f84 100644 --- a/generator/inceptionmain.go +++ b/generator/inceptionmain.go @@ -45,7 +45,7 @@ import ( ) func main() { - i := ffjsoninception.NewInception("{{.InputPath}}", "{{.PackageName}}", "{{.OutputPath}}", {{.ResetFields}}) + i := ffjsoninception.NewInception("{{.InputPath}}", "{{.PackageName}}", "{{.OutputPath}}", {{.ResetFields}}, {{.Strict}}) i.AddMany(importedinceptionpackage.FFJSONExpose()) i.Execute() } @@ -84,6 +84,7 @@ type templateCtx struct { InputPath string OutputPath string ResetFields bool + Strict bool } type InceptionMain struct { @@ -96,9 +97,10 @@ type InceptionMain struct { tempMain *os.File tempExpose *os.File resetFields bool + strict bool } -func NewInceptionMain(goCmd string, inputPath string, outputPath string, resetFields bool) *InceptionMain { +func NewInceptionMain(goCmd string, inputPath string, outputPath string, resetFields bool, strict bool) *InceptionMain { exposePath := getExposePath(inputPath) return &InceptionMain{ goCmd: goCmd, @@ -106,6 +108,7 @@ func NewInceptionMain(goCmd string, inputPath string, outputPath string, resetFi outputPath: outputPath, exposePath: exposePath, resetFields: resetFields, + strict: strict, } } @@ -191,6 +194,7 @@ func (im *InceptionMain) Generate(packageName string, si []*StructInfo, importNa InputPath: im.inputPath, OutputPath: im.outputPath, ResetFields: im.resetFields, + Strict: im.strict, } t := template.Must(template.New("inception.go").Parse(inceptionMainTemplate)) diff --git a/inception/decoder.go b/inception/decoder.go index b65279b..5cc7a1e 100644 --- a/inception/decoder.go +++ b/inception/decoder.go @@ -53,6 +53,7 @@ func CreateUnmarshalJSON(ic *Inception, si *StructInfo) error { IC: ic, ValidValues: validValues, ResetFields: ic.ResetFields, + Strict: ic.Strict, }) ic.OutputFuncs = append(ic.OutputFuncs, out) diff --git a/inception/decoder_tpl.go b/inception/decoder_tpl.go index 9eba505..a7b5cf9 100644 --- a/inception/decoder_tpl.go +++ b/inception/decoder_tpl.go @@ -523,6 +523,7 @@ type ujFunc struct { SI *StructInfo ValidValues []string ResetFields bool + Strict bool } var ujFuncTxt = ` @@ -537,6 +538,9 @@ func (uj *{{.SI.Name}}) UnmarshalJSON(input []byte) error { func (uj *{{.SI.Name}}) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { var err error = nil currentKey := ffj_t_{{.SI.Name}}base + {{if eq .Strict true}} + var unknowKeys = make([]string, 0) + {{end}} _ = currentKey tok := fflib.FFTok_init wantedTok := fflib.FFTok_init @@ -610,6 +614,9 @@ mainparse: goto mainparse } {{end}} + {{if eq .Strict true}} + unknowKeys = append(unknowKeys, string(kn)) + {{end}} currentKey = ffj_t_{{.SI.Name}}no_such_key state = fflib.FFParse_want_colon goto mainparse @@ -696,6 +703,13 @@ done: } {{end}} {{end}} + +{{if eq .Strict true}} + if len(unknowKeys) > 0 { + return fs.WrapErr(fflib.NewErrUnknowFields(unknowKeys)) + } +{{end}} + return nil } ` diff --git a/inception/inception.go b/inception/inception.go index 10cb271..a92387c 100644 --- a/inception/inception.go +++ b/inception/inception.go @@ -20,11 +20,12 @@ package ffjsoninception import ( "errors" "fmt" - "github.com/pquerna/ffjson/shared" "io/ioutil" "os" "reflect" "sort" + + "github.com/pquerna/ffjson/shared" ) type Inception struct { @@ -37,9 +38,10 @@ type Inception struct { OutputFuncs []string q ConditionalWrite ResetFields bool + Strict bool } -func NewInception(inputPath string, packageName string, outputPath string, resetFields bool) *Inception { +func NewInception(inputPath string, packageName string, outputPath string, resetFields bool, strict bool) *Inception { return &Inception{ objs: make([]*StructInfo, 0), InputPath: inputPath, @@ -48,6 +50,7 @@ func NewInception(inputPath string, packageName string, outputPath string, reset OutputFuncs: make([]string, 0), OutputImports: make(map[string]bool), ResetFields: resetFields, + Strict: strict, } }