From fe79700b958c9d409ad38ce21b0a42aba8bd98bd Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Fri, 13 Feb 2015 10:40:48 -0500 Subject: [PATCH] issue-#3 unintended error redesign to heierarchical --- validator.go | 66 ++++++++++++++++++++++++++--------------------- validator_test.go | 57 +++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 39 deletions(-) diff --git a/validator.go b/validator.go index adbfeca..c85584d 100644 --- a/validator.go +++ b/validator.go @@ -1,3 +1,12 @@ +/** + * Package validator + * + * NOTES: + * + * anonymous structs - they don't have names so expect the Struct name within StructValidationErrors to be blank + * + */ + package validator import ( @@ -10,9 +19,10 @@ import ( ) const ( - defaultTagName = "validate" - omitempty string = "omitempty" - validationErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n" + defaultTagName = "validate" + omitempty = "omitempty" + validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n" + validationStructErrMsg = "Struct:%s\n" ) // FieldValidationError contains a single fields validation error @@ -24,29 +34,35 @@ type FieldValidationError struct { // This is intended for use in development + debugging and not intended to be a production error message. // it also allows FieldValidationError to be used as an Error interface func (e FieldValidationError) Error() string { - return fmt.Sprintf(validationErrMsg, e.Field, e.ErrorTag) + return fmt.Sprintf(validationFieldErrMsg, e.Field, e.ErrorTag) } -// StructValidationErrors is a struct of errors for struct fields ( Excluding fields of type 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 StructValidationErrors is created for each struct resulting in -// a neat & tidy 2D flattened list of structs validation errors +// StructValidationErrors is hierarchical list of field and struct errors type StructValidationErrors struct { + // Name of the Struct Struct string + // Struct Field Errors Errors map[string]*FieldValidationError + // Struct Fields of type struct and their errors + // key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank + StructErrors map[string]*StructValidationErrors } // This is intended for use in development + debugging and not intended to be a production error message. // it also allows StructValidationErrors to be used as an Error interface func (e StructValidationErrors) Error() string { - s := "" + s := fmt.Sprintf(validationStructErrMsg, e.Struct) for _, err := range e.Errors { - s += fmt.Sprintf(validationErrMsg, err.Field, err.ErrorTag) + s += err.Error() + } + + for _, sErr := range e.StructErrors { + s += sErr.Error() } - return s + return fmt.Sprintf("%s\n\n", s) } // ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed) @@ -113,22 +129,22 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error { } // ValidateStruct validates a struct and returns a struct containing the errors -func ValidateStruct(s interface{}) map[string]*StructValidationErrors { +func ValidateStruct(s interface{}) *StructValidationErrors { return internalValidator.ValidateStruct(s) } // ValidateStruct validates a struct and returns a struct containing the errors -func (v *Validator) ValidateStruct(s interface{}) map[string]*StructValidationErrors { +func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors { - errorArray := map[string]*StructValidationErrors{} structValue := reflect.ValueOf(s) structType := reflect.TypeOf(s) structName := structType.Name() - var currentStructError = &StructValidationErrors{ - Struct: structName, - Errors: map[string]*FieldValidationError{}, + validationErrors := &StructValidationErrors{ + Struct: structName, + Errors: map[string]*FieldValidationError{}, + StructErrors: map[string]*StructValidationErrors{}, } if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { @@ -170,9 +186,7 @@ func (v *Validator) ValidateStruct(s interface{}) map[string]*StructValidationEr } if structErrors := v.ValidateStruct(valueField.Interface()); structErrors != nil { - for key, val := range structErrors { - errorArray[key] = val - } + validationErrors.StructErrors[typeField.Name] = structErrors // free up memory map no longer needed structErrors = nil } @@ -180,24 +194,18 @@ func (v *Validator) ValidateStruct(s interface{}) map[string]*StructValidationEr default: if fieldError := v.validateStructFieldByTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { - currentStructError.Errors[fieldError.Field] = fieldError + validationErrors.Errors[fieldError.Field] = fieldError // free up memory reference fieldError = nil } } } - if len(currentStructError.Errors) > 0 { - errorArray[currentStructError.Struct] = currentStructError - // free up memory - currentStructError = nil - } - - if len(errorArray) == 0 { + if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { return nil } - return errorArray + return validationErrors } // ValidateFieldWithTag validates the given field by the given tag arguments diff --git a/validator_test.go b/validator_test.go index 1010506..ed12593 100644 --- a/validator_test.go +++ b/validator_test.go @@ -2,13 +2,24 @@ package validator_test import ( "fmt" + "log" "testing" "github.com/joeybloggs/go-validate-yourself" ) +// type UserDetails struct { +// Address string `validate:"omitempty,length=6"` +// Sub struct { +// A string `validate:"required"` +// } +// } + type UserDetails struct { Address string `validate:"omitempty,length=6"` + Sub struct { + A string `validate:"required"` + } } type User struct { @@ -16,28 +27,54 @@ type User struct { Details *UserDetails } +// func Test(t *testing.T) { TestingT(t) } +// +// type MySuite struct{} +// +// var _ = Suite(&MySuite{}) +// +// func (s *MySuite) SetUpTest(c *C) { +// s.dir = c.MkDir() +// // Use s.dir to prepare some data. +// } + +// func RecursiveErrorReporter(e *validator.StructValidationErrors) { +// +// // log.Printf("Error within Struct:%s\n", e.Struct) +// +// // for _, f := range e.Errors { +// // log.Println(f.Error()) +// // } +// } + func TestValidateStruct(t *testing.T) { u := &User{ FirstName: "", Details: &UserDetails{ - "", + Address: "", + Sub: struct { + A string `validate:"required"` + }{ + A: "", + }, }, } errors := validator.ValidateStruct(u) fmt.Println(errors == nil) + log.Println(errors.Error()) - 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) - fmt.Println(j.Error()) - } - } + // 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) + // fmt.Println(j.Error()) + // } + // } }