diff --git a/validator.go b/validator.go index 1607d28..96039d8 100644 --- a/validator.go +++ b/validator.go @@ -8,6 +8,17 @@ package validator +import ( + "bytes" + "fmt" + "reflect" + "strings" +) + +const ( + fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" +) + // Validate implements the Validate Struct // NOTE: Fields within are not thread safe and that is on purpose // Functions and Tags should all be predifined before use, so subscribe to the philosiphy @@ -30,11 +41,31 @@ type Config struct { // param = parameter used in validation i.e. gt=0 param would be 0 type Func func(top interface{}, current interface{}, f interface{}, param string) bool +// ValidationErrors is a type of map[string]*FieldError +// it exists to allow for multiple errors passed from this library +// and yet still comply to the error interface +type ValidationErrors map[string]*FieldError + +// This is intended for use in development + debugging and not intended to be a production error message. +// It allows ValidationErrors to subscribe to the Error interface. +// All information to create an error message specific to your application is contained within +// the FieldError found in the ValidationErrors +func (ve ValidationErrors) Error() string { + + buff := bytes.NewBufferString("") + + for key, err := range ve { + buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag)) + } + + return strings.TrimSpace(buff.String()) +} + // FieldError contains a single field's validation error along // with other properties that may be needed for error message creation type FieldError struct { Field string - // Tag string + Tag string // Kind reflect.Kind // Type reflect.Type // Param string @@ -58,48 +89,36 @@ func New(config Config) *Validate { // NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good // way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to // the Array or Map. -func (v *Validate) Struct(s interface{}) map[string]*FieldError { +func (v *Validate) Struct(current interface{}) ValidationErrors { - // var err *FieldError errs := map[string]*FieldError{} - // errchan := make(chan *FieldError) - // done := make(chan bool) - // wg := &sync.WaitGroup{} - - v.structRecursive(s, s, s, 0, errs) - - // LOOP: - // for { - // select { - // case err := <-errchan: - // errs[err.Field] = err - // // fmt.Println(err) - // case <-done: - // // fmt.Println("All Done") - // break LOOP - // } - // } + sv := reflect.ValueOf(current) + + v.structRecursive(sv, sv, "", errs) + + if len(errs) == 0 { + return nil + } return errs } -func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}, depth int, errs map[string]*FieldError) { +// func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}, errs map[string]*FieldError) { + +func (v *Validate) structRecursive(top reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors) { - errs["Name"] = &FieldError{Field: "Name"} + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } - if depth < 3 { - // wg.Add(1) - v.structRecursive(s, s, s, depth+1, errs) + if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface { + panic("value passed for validation is not a struct") } - // wg.Wait() + // errs[errPrefix+"Name"] = &FieldError{Field: "Name", Tag: "required"} - // if depth == 0 { - // // wg.Wait() - // done <- true - // // return - // } else { - // // wg.Done() + // if depth < 3 { + // v.structRecursive(top, current, errs) // } } diff --git a/validator_test.go b/validator_test.go index 63a11c7..b31c29d 100644 --- a/validator_test.go +++ b/validator_test.go @@ -213,6 +213,7 @@ func TestValidation(t *testing.T) { errs := validate.Struct(tst) fmt.Println(errs) + fmt.Println(errs == nil) } // func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors {