diff options
| author | Clement Michaud <clement.michaud34@gmail.com> | 2019-11-17 11:47:07 +0100 |
|---|---|---|
| committer | Clément Michaud <clement.michaud34@gmail.com> | 2019-11-17 16:30:33 +0100 |
| commit | 3b2d733367c88621e4178301f2bcb4bc03613eee (patch) | |
| tree | 41ac41fc5b6cece04db85a08bfa7c32a022f7354 /internal/configuration/schema/validator.go | |
| parent | a06b69dd458e756f1a3d6867eb5b9f54560e2ee1 (diff) | |
Move source code into internal directory to follow standard project layout.
https://github.com/golang-standards/project-layout
Diffstat (limited to 'internal/configuration/schema/validator.go')
| -rw-r--r-- | internal/configuration/schema/validator.go | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/internal/configuration/schema/validator.go b/internal/configuration/schema/validator.go new file mode 100644 index 000000000..8ad09131e --- /dev/null +++ b/internal/configuration/schema/validator.go @@ -0,0 +1,129 @@ +package schema + +import ( + "fmt" + "reflect" + + "github.com/Workiva/go-datastructures/queue" +) + +// ErrorContainer represents a container where we can add errors and retrieve them +type ErrorContainer interface { + Push(err error) + HasErrors() bool + Errors() []error +} + +// Validator represents the validator interface +type Validator struct { + errors map[string][]error +} + +// NewValidator create a validator +func NewValidator() *Validator { + validator := new(Validator) + validator.errors = make(map[string][]error) + return validator +} + +// QueueItem an item representing a struct field and its path. +type QueueItem struct { + value reflect.Value + path string +} + +func (v *Validator) validateOne(item QueueItem, q *queue.Queue) error { + if item.value.Type().Kind() == reflect.Ptr { + if item.value.IsNil() { + return nil + } + + elem := item.value.Elem() + q.Put(QueueItem{ + value: elem, + path: item.path, + }) + } else if item.value.Kind() == reflect.Struct { + numFields := item.value.Type().NumField() + + validateFn := item.value.Addr().MethodByName("Validate") + + if validateFn.IsValid() { + structValidator := NewStructValidator() + validateFn.Call([]reflect.Value{reflect.ValueOf(structValidator)}) + v.errors[item.path] = structValidator.Errors() + } + + for i := 0; i < numFields; i++ { + field := item.value.Type().Field(i) + value := item.value.Field(i) + + q.Put(QueueItem{ + value: value, + path: item.path + "." + field.Name, + }) + } + } + return nil +} + +// Validate validate a struct +func (v *Validator) Validate(s interface{}) error { + q := queue.New(40) + q.Put(QueueItem{value: reflect.ValueOf(s), path: "root"}) + + for !q.Empty() { + val, err := q.Get(1) + if err != nil { + return err + } + item, ok := val[0].(QueueItem) + if !ok { + return fmt.Errorf("Cannot convert item into QueueItem") + } + v.validateOne(item, q) + } + return nil +} + +// PrintErrors display the errors thrown during validation +func (v *Validator) PrintErrors() { + for path, errs := range v.errors { + fmt.Printf("Errors at %s:\n", path) + for _, err := range errs { + fmt.Printf("--> %s\n", err) + } + } +} + +// Errors return the errors thrown during validation +func (v *Validator) Errors() map[string][]error { + return v.errors +} + +// StructValidator is a validator for structs +type StructValidator struct { + errors []error +} + +// NewStructValidator is a constructor of struct validator +func NewStructValidator() *StructValidator { + val := new(StructValidator) + val.errors = make([]error, 0) + return val +} + +// Push an error in the validator. +func (v *StructValidator) Push(err error) { + v.errors = append(v.errors, err) +} + +// HasErrors checks whether the validator contains errors. +func (v *StructValidator) HasErrors() bool { + return len(v.errors) > 0 +} + +// Errors returns the errors. +func (v *StructValidator) Errors() []error { + return v.errors +} |
