aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/config/config.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-06-02 14:25:44 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-06-03 10:41:09 +0200
commit96b8d4e99c7812f91633ea6cd1aee5867965e742 (patch)
treeaec8a76b057b1b245856f7bcb3ad00a9b9f47bc8 /pkg/config/config.go
parent46c6ed89bf1a7de94496b853608ecd6f80776b58 (diff)
pkg/config: support nested structs
Diffstat (limited to 'pkg/config/config.go')
-rw-r--r--pkg/config/config.go52
1 files changed, 45 insertions, 7 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go
index feb031c71..461ebdabc 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -33,29 +33,67 @@ func load(data []byte, cfg interface{}) error {
}
func checkUnknownFields(data []byte, typ reflect.Type) error {
- if typ.Kind() != reflect.Ptr {
+ if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct {
return fmt.Errorf("config type is not pointer to struct")
}
- typ = typ.Elem()
+ return checkUnknownFieldsRec(data, "", typ)
+}
+
+func checkUnknownFieldsRec(data []byte, prefix string, typ reflect.Type) error {
+ if typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
if typ.Kind() != reflect.Struct {
return fmt.Errorf("config type is not pointer to struct")
}
- fields := make(map[string]bool)
+ fields := make(map[string]reflect.Type)
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Tag.Get("json") == "-" {
continue
}
- fields[strings.ToLower(field.Name)] = true
+ fields[strings.ToLower(field.Name)] = field.Type
}
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)
+ for k, v := range f {
+ field, ok := fields[strings.ToLower(k)]
+ if !ok {
+ return fmt.Errorf("unknown field '%v%v' in config", prefix, k)
+ }
+ if field.Kind() == reflect.Slice &&
+ (field.PkgPath() != "encoding/json" || field.Name() != "RawMessage") {
+ vv := reflect.ValueOf(v)
+ if vv.Type().Kind() != reflect.Slice {
+ return fmt.Errorf("bad json array type '%v%v'", prefix, k)
+ }
+ for i := 0; i < vv.Len(); i++ {
+ e := vv.Index(i).Interface()
+ prefix1 := fmt.Sprintf("%v%v[%v].", prefix, k, i)
+ if err := checkUnknownFieldsStruct(e, prefix1, field.Elem()); err != nil {
+ return err
+ }
+ }
+ }
+ if err := checkUnknownFieldsStruct(v, prefix+k+".", field); err != nil {
+ return err
}
}
return nil
}
+
+func checkUnknownFieldsStruct(val interface{}, prefix string, typ reflect.Type) error {
+ if typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
+ if typ.Kind() != reflect.Struct {
+ return nil
+ }
+ inner, err := json.Marshal(val)
+ if err != nil {
+ return fmt.Errorf("failed to marshal inner struct '%v%v':", prefix, err)
+ }
+ return checkUnknownFieldsRec(inner, prefix, typ)
+}