diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-06-02 14:25:44 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-06-03 10:41:09 +0200 |
| commit | 96b8d4e99c7812f91633ea6cd1aee5867965e742 (patch) | |
| tree | aec8a76b057b1b245856f7bcb3ad00a9b9f47bc8 /pkg/config/config.go | |
| parent | 46c6ed89bf1a7de94496b853608ecd6f80776b58 (diff) | |
pkg/config: support nested structs
Diffstat (limited to 'pkg/config/config.go')
| -rw-r--r-- | pkg/config/config.go | 52 |
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) +} |
