|
|
@ -8,6 +8,17 @@ |
|
|
|
|
|
|
|
|
|
|
|
package validator |
|
|
|
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
|
|
|
|
// Validate implements the Validate Struct
|
|
|
|
// NOTE: Fields within are not thread safe and that is on purpose
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// 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 |
|
|
|
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
|
|
|
|
// FieldError contains a single field's validation error along
|
|
|
|
// with other properties that may be needed for error message creation
|
|
|
|
// with other properties that may be needed for error message creation
|
|
|
|
type FieldError struct { |
|
|
|
type FieldError struct { |
|
|
|
Field string |
|
|
|
Field string |
|
|
|
// Tag string
|
|
|
|
Tag string |
|
|
|
// Kind reflect.Kind
|
|
|
|
// Kind reflect.Kind
|
|
|
|
// Type reflect.Type
|
|
|
|
// Type reflect.Type
|
|
|
|
// Param string
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// 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.
|
|
|
|
// 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{} |
|
|
|
errs := map[string]*FieldError{} |
|
|
|
// errchan := make(chan *FieldError)
|
|
|
|
sv := reflect.ValueOf(current) |
|
|
|
// done := make(chan bool)
|
|
|
|
|
|
|
|
// wg := &sync.WaitGroup{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
v.structRecursive(s, s, s, 0, errs) |
|
|
|
v.structRecursive(sv, sv, "", errs) |
|
|
|
|
|
|
|
|
|
|
|
// LOOP:
|
|
|
|
if len(errs) == 0 { |
|
|
|
// for {
|
|
|
|
return nil |
|
|
|
// select {
|
|
|
|
} |
|
|
|
// case err := <-errchan:
|
|
|
|
|
|
|
|
// errs[err.Field] = err
|
|
|
|
|
|
|
|
// // fmt.Println(err)
|
|
|
|
|
|
|
|
// case <-done:
|
|
|
|
|
|
|
|
// // fmt.Println("All Done")
|
|
|
|
|
|
|
|
// break LOOP
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return errs |
|
|
|
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) {
|
|
|
|
|
|
|
|
|
|
|
|
errs["Name"] = &FieldError{Field: "Name"} |
|
|
|
func (v *Validate) structRecursive(top reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors) { |
|
|
|
|
|
|
|
|
|
|
|
if depth < 3 { |
|
|
|
if current.Kind() == reflect.Ptr && !current.IsNil() { |
|
|
|
// wg.Add(1)
|
|
|
|
current = current.Elem() |
|
|
|
v.structRecursive(s, s, s, depth+1, errs) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// wg.Wait()
|
|
|
|
if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface { |
|
|
|
|
|
|
|
panic("value passed for validation is not a struct") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// if depth == 0 {
|
|
|
|
// errs[errPrefix+"Name"] = &FieldError{Field: "Name", Tag: "required"}
|
|
|
|
// // wg.Wait()
|
|
|
|
|
|
|
|
// done <- true
|
|
|
|
// if depth < 3 {
|
|
|
|
// // return
|
|
|
|
// v.structRecursive(top, current, errs)
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// // wg.Done()
|
|
|
|
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|