summaryrefslogtreecommitdiff
path: root/internal/configuration/schema/validator.go
diff options
context:
space:
mode:
authorClement Michaud <clement.michaud34@gmail.com>2019-11-17 11:47:07 +0100
committerClément Michaud <clement.michaud34@gmail.com>2019-11-17 16:30:33 +0100
commit3b2d733367c88621e4178301f2bcb4bc03613eee (patch)
tree41ac41fc5b6cece04db85a08bfa7c32a022f7354 /internal/configuration/schema/validator.go
parenta06b69dd458e756f1a3d6867eb5b9f54560e2ee1 (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.go129
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
+}