From aad5727a16ed87f286394fd7226656e747553630 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 12 Feb 2015 20:49:57 -0500 Subject: [PATCH] initial working commit --- baked_in.go | 18 ++++ validator.go | 209 ++++++++++++++++++++++++++++++++++++++++++---- validator_test.go | 33 +++++++- 3 files changed, 241 insertions(+), 19 deletions(-) create mode 100644 baked_in.go diff --git a/baked_in.go b/baked_in.go new file mode 100644 index 0000000..728a635 --- /dev/null +++ b/baked_in.go @@ -0,0 +1,18 @@ +package validator + +import ( + "log" + "reflect" +) + +var bakedInValidators = map[string]ValidationFunc{ + "required": isRequired, +} + +func isRequired(field interface{}, param string) bool { + + log.Printf("Required:%s Valid:%t\n", field, field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface()) + return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() + + // return true +} diff --git a/validator.go b/validator.go index 1b1d13f..272a5d9 100644 --- a/validator.go +++ b/validator.go @@ -2,9 +2,10 @@ package validator import ( "errors" - "fmt" "log" "reflect" + "strings" + "unicode" ) // FieldValidationError contains a single fields validation error @@ -17,17 +18,18 @@ type FieldValidationError struct { // NOTE: if a field within a struct is a struct it's errors will not be contained within the current // StructValidationErrors but rather a new ArrayValidationErrors is created for each struct type StructValidationErrors struct { - Errors []FieldValidationError + Struct string + Errors []*FieldValidationError } -// ArrayStructValidationErrors is a struct that contains a 2D flattened list of struct specific StructValidationErrors -type ArrayStructValidationErrors struct { - // Key = Struct Name - Errors map[string][]StructValidationErrors -} +// // ArrayStructValidationErrors is a struct that contains a 2D flattened list of struct specific StructValidationErrors +// type ArrayStructValidationErrors struct { +// // Key = Struct Name +// Errors map[string][]StructValidationErrors +// } // ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed) -type ValidationFunc func(v interface{}, param string) error +type ValidationFunc func(v interface{}, param string) bool // Validator implements the Validator Struct // NOTE: Fields within are not thread safe and that is on purpose @@ -40,7 +42,7 @@ type Validator struct { validationFuncs map[string]ValidationFunc } -var bakedInValidators = map[string]ValidationFunc{} +// var bakedInValidators = map[string]ValidationFunc{} var internalValidator = NewValidator("validate", bakedInValidators) @@ -89,17 +91,23 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error { return nil } -func ValidateStruct(s interface{}) ArrayStructValidationErrors { +// ValidateStruct validates a struct and returns a struct containing the errors +func ValidateStruct(s interface{}) []*StructValidationErrors { return internalValidator.ValidateStruct(s) } -func (v *Validator) ValidateStruct(s interface{}) ArrayStructValidationErrors { - - var errorStruct = ArrayStructValidationErrors{} +// ValidateStruct validates a struct and returns a struct containing the errors +func (v *Validator) ValidateStruct(s interface{}) []*StructValidationErrors { + errorArray := []*StructValidationErrors{} structValue := reflect.ValueOf(s) structType := reflect.TypeOf(s) + structName := structType.Name() + + var currentStructError = &StructValidationErrors{ + Struct: structName, + } if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { return v.ValidateStruct(structValue.Elem().Interface()) @@ -120,8 +128,179 @@ func (v *Validator) ValidateStruct(s interface{}) ArrayStructValidationErrors { valueField = valueField.Elem() } - fmt.Println(typeField.Name) + tag := typeField.Tag.Get(v.tagName) + + if tag == "-" { + continue + } + + // if no validation and not a struct (which may containt fields for validation) + if tag == "" && valueField.Kind() != reflect.Struct { + continue + } + + switch valueField.Kind() { + + case reflect.Struct: + + if !unicode.IsUpper(rune(typeField.Name[0])) { + continue + } + + if structErrors := v.ValidateStruct(valueField.Interface()); structErrors != nil { + errorArray = append(errorArray, structErrors...) + } + + default: + + if fieldError := v.validateStructFieldByTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { + currentStructError.Errors = append(currentStructError.Errors, fieldError) + } + } + } + + if currentStructError.Errors != nil { + errorArray = append(errorArray, currentStructError) + } + + if len(errorArray) == 0 { + return nil + } + + return errorArray +} + +// ValidateFieldWithTag validates the given field by the given tag arguments +func (v *Validator) validateStructFieldByTag(f interface{}, name string, tag string) *FieldValidationError { + + // // This is a double check if coming from ValidateStruct but need to be here in case function is called directly + // if tag == "-" { + // return nil + // } + // + // valueField := reflect.ValueOf(f) + // + // if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + // return v.validateStructFieldByTag(valueField.Elem().Interface(), name, tag) + // } + // + // // fmt.Println(typeField.Name) + // + // switch valueField.Kind() { + // + // case reflect.Struct, reflect.Invalid: + // log.Fatal("Invalid field passed to ValidateFieldWithTag") + // } + // + // // typeField := reflect.TypeOf(f) + // // name := "" + // valTags := strings.Split(tag, ",") + // + // for _, valTag := range valTags { + // + // vals := strings.Split(valTag, "=") + // key := strings.Trim(vals[0], " ") + // + // if len(key) == 0 { + // log.Fatalf("Invalid validation tag on field %s", name) + // } + // + // valFunc := v.validationFuncs[key] + // if valFunc == nil { + // log.Fatalf("Undefined validation function on field %s", name) + // } + // + // param := "" + // if len(vals) > 1 { + // param = strings.Trim(vals[1], " ") + // } + // + // if err := valFunc(f, param); !err { + // + // return &FieldValidationError{ + // Field: name, + // ErrorTag: key, + // } + // } + // + // } + // + // return nil + // + // + + if err := v.validateFieldByNameAndTag(f, name, tag); err != nil { + return &FieldValidationError{ + Field: name, + ErrorTag: err.Error(), + } + } + + return nil +} + +// ValidateFieldByTag allows validation of a single field with the internal validator, still using tag style validation to check multiple errors +func ValidateFieldByTag(f interface{}, tag string) error { + + return internalValidator.validateFieldByNameAndTag(f, "", tag) +} + +// ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors +func (v *Validator) ValidateFieldByTag(f interface{}, tag string) error { + + return v.validateFieldByNameAndTag(f, "", tag) +} + +func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag string) error { + + // This is a double check if coming from ValidateStruct but need to be here in case function is called directly + if tag == "-" { + return nil + } + + valueField := reflect.ValueOf(f) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + return v.ValidateFieldByTag(valueField.Elem().Interface(), tag) + } + + // fmt.Println(typeField.Name) + + switch valueField.Kind() { + + case reflect.Struct, reflect.Invalid: + log.Fatal("Invalid field passed to ValidateFieldWithTag") + } + + // typeField := reflect.TypeOf(f) + // name := "" + valTags := strings.Split(tag, ",") + + for _, valTag := range valTags { + + vals := strings.Split(valTag, "=") + key := strings.Trim(vals[0], " ") + + if len(key) == 0 { + log.Fatalf("Invalid validation tag on field %s", name) + } + + valFunc := v.validationFuncs[key] + if valFunc == nil { + log.Fatalf("Undefined validation function on field %s", name) + } + + param := "" + if len(vals) > 1 { + param = strings.Trim(vals[1], " ") + } + + if err := valFunc(f, param); !err { + + return errors.New(key) + } + } - return errorStruct + return nil } diff --git a/validator_test.go b/validator_test.go index 5f32192..097477e 100644 --- a/validator_test.go +++ b/validator_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "gopkg.in/joeybloggs/go-validate-yourself.v0" + "github.com/joeybloggs/go-validate-yourself" ) type UserDetails struct { @@ -19,13 +19,38 @@ type User struct { func TestValidateStruct(t *testing.T) { u := &User{ - FirstName: "Dean Karn", + FirstName: "", Details: &UserDetails{ - "26 Here Blvd.", + "", }, } errors := validator.ValidateStruct(u) - fmt.Println(errors) + fmt.Println(errors == nil) + + for _, i := range errors { + fmt.Printf("Error Struct:%s\n", i.Struct) + + for _, j := range i.Errors { + + fmt.Printf("Error Field:%s Error Tag:%s\n", j.Field, j.ErrorTag) + } + } + } + +// func TestValidateField(t *testing.T) { +// +// u := &User{ +// FirstName: "Dean Karn", +// Details: &UserDetails{ +// "26 Here Blvd.", +// }, +// } +// +// err := validator.ValidateFieldByTag(u.FirstName, "required") +// +// fmt.Println(err == nil) +// fmt.Println(err) +// }