unintended error redesign to heierarchical
pull/16/head
Dean Karn 10 years ago
parent 4139d8bd75
commit fe79700b95
  1. 66
      validator.go
  2. 57
      validator_test.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 package validator
import ( import (
@ -10,9 +19,10 @@ import (
) )
const ( const (
defaultTagName = "validate" defaultTagName = "validate"
omitempty string = "omitempty" omitempty = "omitempty"
validationErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n" validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n"
validationStructErrMsg = "Struct:%s\n"
) )
// FieldValidationError contains a single fields validation error // 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. // 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 // it also allows FieldValidationError to be used as an Error interface
func (e FieldValidationError) Error() string { 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 ) // StructValidationErrors is hierarchical list of field and struct errors
// 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
type StructValidationErrors struct { type StructValidationErrors struct {
// Name of the Struct
Struct string Struct string
// Struct Field Errors
Errors map[string]*FieldValidationError 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. // 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 // it also allows StructValidationErrors to be used as an Error interface
func (e StructValidationErrors) Error() string { func (e StructValidationErrors) Error() string {
s := "" s := fmt.Sprintf(validationStructErrMsg, e.Struct)
for _, err := range e.Errors { 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) // 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 // 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) return internalValidator.ValidateStruct(s)
} }
// ValidateStruct validates a struct and returns a struct containing the errors // 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) structValue := reflect.ValueOf(s)
structType := reflect.TypeOf(s) structType := reflect.TypeOf(s)
structName := structType.Name() structName := structType.Name()
var currentStructError = &StructValidationErrors{ validationErrors := &StructValidationErrors{
Struct: structName, Struct: structName,
Errors: map[string]*FieldValidationError{}, Errors: map[string]*FieldValidationError{},
StructErrors: map[string]*StructValidationErrors{},
} }
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { 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 { if structErrors := v.ValidateStruct(valueField.Interface()); structErrors != nil {
for key, val := range structErrors { validationErrors.StructErrors[typeField.Name] = structErrors
errorArray[key] = val
}
// free up memory map no longer needed // free up memory map no longer needed
structErrors = nil structErrors = nil
} }
@ -180,24 +194,18 @@ func (v *Validator) ValidateStruct(s interface{}) map[string]*StructValidationEr
default: default:
if fieldError := v.validateStructFieldByTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { 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 // free up memory reference
fieldError = nil fieldError = nil
} }
} }
} }
if len(currentStructError.Errors) > 0 { if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
errorArray[currentStructError.Struct] = currentStructError
// free up memory
currentStructError = nil
}
if len(errorArray) == 0 {
return nil return nil
} }
return errorArray return validationErrors
} }
// ValidateFieldWithTag validates the given field by the given tag arguments // ValidateFieldWithTag validates the given field by the given tag arguments

@ -2,13 +2,24 @@ package validator_test
import ( import (
"fmt" "fmt"
"log"
"testing" "testing"
"github.com/joeybloggs/go-validate-yourself" "github.com/joeybloggs/go-validate-yourself"
) )
// type UserDetails struct {
// Address string `validate:"omitempty,length=6"`
// Sub struct {
// A string `validate:"required"`
// }
// }
type UserDetails struct { type UserDetails struct {
Address string `validate:"omitempty,length=6"` Address string `validate:"omitempty,length=6"`
Sub struct {
A string `validate:"required"`
}
} }
type User struct { type User struct {
@ -16,28 +27,54 @@ type User struct {
Details *UserDetails 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) { func TestValidateStruct(t *testing.T) {
u := &User{ u := &User{
FirstName: "", FirstName: "",
Details: &UserDetails{ Details: &UserDetails{
"", Address: "",
Sub: struct {
A string `validate:"required"`
}{
A: "",
},
}, },
} }
errors := validator.ValidateStruct(u) errors := validator.ValidateStruct(u)
fmt.Println(errors == nil) fmt.Println(errors == nil)
log.Println(errors.Error())
for _, i := range errors { // for _, i := range errors {
fmt.Printf("Error Struct:%s\n", i.Struct) // fmt.Printf("Error Struct:%s\n", i.Struct)
//
for _, j := range i.Errors { // for _, j := range i.Errors {
//
fmt.Printf("Error Field:%s Error Tag:%s\n", j.Field, j.ErrorTag) // fmt.Printf("Error Field:%s Error Tag:%s\n", j.Field, j.ErrorTag)
fmt.Println(j.Error()) // fmt.Println(j.Error())
} // }
} // }
} }

Loading…
Cancel
Save