diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-06-01 11:23:31 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-06-01 11:23:31 +0200 |
| commit | de6eb51045aeb6509b6087d5a4ed37a79496d15b (patch) | |
| tree | b7ff3a69478dc0965788e84e5c5ddbb35a9f3e01 /pkg | |
| parent | 2cf166ec847e808c72cc8942d696d7c66e78acff (diff) | |
| parent | 84eb5fd389bc4ae247048d1f20ec9577935f4f04 (diff) | |
Merge pull request #212 from google/dvyukov-config
config: split and refactor
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/config/config.go | 61 | ||||
| -rw-r--r-- | pkg/config/config_test.go | 65 |
2 files changed, 126 insertions, 0 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 000000000..feb031c71 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,61 @@ +// Copyright 2017 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "reflect" + "strings" +) + +func Load(filename string, cfg interface{}) error { + if filename == "" { + return fmt.Errorf("no config file specified") + } + data, err := ioutil.ReadFile(filename) + if err != nil { + return fmt.Errorf("failed to read config file: %v", err) + } + return load(data, cfg) +} + +func load(data []byte, cfg interface{}) error { + if err := checkUnknownFields(data, reflect.ValueOf(cfg).Type()); err != nil { + return err + } + if err := json.Unmarshal(data, cfg); err != nil { + return fmt.Errorf("failed to parse config file: %v", err) + } + return nil +} + +func checkUnknownFields(data []byte, typ reflect.Type) error { + if typ.Kind() != reflect.Ptr { + return fmt.Errorf("config type is not pointer to struct") + } + typ = typ.Elem() + if typ.Kind() != reflect.Struct { + return fmt.Errorf("config type is not pointer to struct") + } + fields := make(map[string]bool) + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if field.Tag.Get("json") == "-" { + continue + } + fields[strings.ToLower(field.Name)] = true + } + f := make(map[string]interface{}) + if err := json.Unmarshal(data, &f); err != nil { + return fmt.Errorf("failed to parse config file: %v", err) + } + for k := range f { + if !fields[strings.ToLower(k)] { + return fmt.Errorf("unknown field '%v' in config", k) + } + } + return nil +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go new file mode 100644 index 000000000..73ad3502c --- /dev/null +++ b/pkg/config/config_test.go @@ -0,0 +1,65 @@ +// Copyright 2016 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package config + +import ( + "fmt" + "reflect" + "testing" +) + +func TestUnknown(t *testing.T) { + type Config struct { + Foo int + Bar string + Baz string `json:"-"` + } + tests := []struct { + input string + output Config + err string + }{ + { + `{"foo": 42}`, + Config{ + Foo: 42, + }, + "", + }, + { + `{"BAR": "Baz", "foo": 42}`, + Config{ + Foo: 42, + Bar: "Baz", + }, + "", + }, + { + `{"foobar": 42}`, + Config{}, + "unknown field 'foobar' in config", + }, + { + `{"foo": 1, "baz": "baz", "bar": "bar"}`, + Config{}, + "unknown field 'baz' in config", + }, + } + for i, test := range tests { + t.Run(fmt.Sprint(i), func(t *testing.T) { + var cfg Config + err := load([]byte(test.input), &cfg) + errStr := "" + if err != nil { + errStr = err.Error() + } + if test.err != errStr { + t.Fatalf("bad err: want '%v', got '%v'", test.err, errStr) + } + if !reflect.DeepEqual(test.output, cfg) { + t.Fatalf("bad output: want '%#v', got '%#v'", test.output, cfg) + } + }) + } +} |
