From c62550c414c4885e1a6dfc3d93f2a4538b66ae4b Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Fri, 10 Jul 2015 11:31:53 -0400 Subject: [PATCH 01/20] Investigating difference speed architectures investigation if goroutines in a consumer producer pattern vs passing a map Spoiler: the map wins out big time! --- benchmarks_test.go | 248 +- examples_test.go | 180 +- old/validator_old.go | 989 ++++++ validator.go | 1038 +------ validator_test.go | 6832 +++++++++++++++++++++--------------------- 5 files changed, 4723 insertions(+), 4564 deletions(-) create mode 100644 old/validator_old.go diff --git a/benchmarks_test.go b/benchmarks_test.go index ee836c2..8f09908 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -2,11 +2,11 @@ package validator import "testing" -func BenchmarkValidateField(b *testing.B) { - for n := 0; n < b.N; n++ { - validate.Field("1", "len=1") - } -} +// func BenchmarkValidateField(b *testing.B) { +// for n := 0; n < b.N; n++ { +// validate.Field("1", "len=1") +// } +// } func BenchmarkValidateStructSimple(b *testing.B) { @@ -42,122 +42,122 @@ func BenchmarkTemplateParallelSimple(b *testing.B) { }) } -func BenchmarkValidateStructLarge(b *testing.B) { - - tFail := &TestString{ - Required: "", - Len: "", - Min: "", - Max: "12345678901", - MinMax: "", - Lt: "0123456789", - Lte: "01234567890", - Gt: "1", - Gte: "1", - OmitEmpty: "12345678901", - Sub: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "", - }, - Iface: &Impl{ - F: "12", - }, - } - - tSuccess := &TestString{ - Required: "Required", - Len: "length==10", - Min: "min=1", - Max: "1234567890", - MinMax: "12345", - Lt: "012345678", - Lte: "0123456789", - Gt: "01234567890", - Gte: "0123456789", - OmitEmpty: "", - Sub: &SubTest{ - Test: "1", - }, - SubIgnore: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "1", - }, - Iface: &Impl{ - F: "123", - }, - } - - for n := 0; n < b.N; n++ { - validate.Struct(tSuccess) - validate.Struct(tFail) - } -} - -func BenchmarkTemplateParallelLarge(b *testing.B) { - - tFail := &TestString{ - Required: "", - Len: "", - Min: "", - Max: "12345678901", - MinMax: "", - Lt: "0123456789", - Lte: "01234567890", - Gt: "1", - Gte: "1", - OmitEmpty: "12345678901", - Sub: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "", - }, - Iface: &Impl{ - F: "12", - }, - } - - tSuccess := &TestString{ - Required: "Required", - Len: "length==10", - Min: "min=1", - Max: "1234567890", - MinMax: "12345", - Lt: "012345678", - Lte: "0123456789", - Gt: "01234567890", - Gte: "0123456789", - OmitEmpty: "", - Sub: &SubTest{ - Test: "1", - }, - SubIgnore: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "1", - }, - Iface: &Impl{ - F: "123", - }, - } - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - validate.Struct(tSuccess) - validate.Struct(tFail) - } - }) -} +// func BenchmarkValidateStructLarge(b *testing.B) { + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// Lt: "0123456789", +// Lte: "01234567890", +// Gt: "1", +// Gte: "1", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// for n := 0; n < b.N; n++ { +// validate.Struct(tSuccess) +// validate.Struct(tFail) +// } +// } + +// func BenchmarkTemplateParallelLarge(b *testing.B) { + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// Lt: "0123456789", +// Lte: "01234567890", +// Gt: "1", +// Gte: "1", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// b.RunParallel(func(pb *testing.PB) { +// for pb.Next() { +// validate.Struct(tSuccess) +// validate.Struct(tFail) +// } +// }) +// } diff --git a/examples_test.go b/examples_test.go index c5dd351..a2aca52 100644 --- a/examples_test.go +++ b/examples_test.go @@ -1,95 +1,89 @@ package validator_test -import ( - "fmt" - - "../validator" -) - -func ExampleValidate_new() { - validator.New("validate", validator.BakedInValidators) -} - -func ExampleValidate_addFunction() { - // This should be stored somewhere globally - var validate *validator.Validate - - validate = validator.New("validate", validator.BakedInValidators) - - fn := func(top interface{}, current interface{}, field interface{}, param string) bool { - return field.(string) == "hello" - } - - validate.AddFunction("valueishello", fn) - - message := "hello" - err := validate.Field(message, "valueishello") - fmt.Println(err) - //Output: - // -} - -func ExampleValidate_field() { - // This should be stored somewhere globally - var validate *validator.Validate - - validate = validator.New("validate", validator.BakedInValidators) - - i := 0 - err := validate.Field(i, "gt=1,lte=10") - fmt.Println(err.Field) - fmt.Println(err.Tag) - fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time - fmt.Println(err.Type) - fmt.Println(err.Param) - fmt.Println(err.Value) - //Output: - // - //gt - //int - //int - //1 - //0 -} - -func ExampleValidate_struct() { - // This should be stored somewhere globally - var validate *validator.Validate - - validate = validator.New("validate", validator.BakedInValidators) - - type ContactInformation struct { - Phone string `validate:"required"` - Street string `validate:"required"` - City string `validate:"required"` - } - - type User struct { - Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) - Age int8 `validate:"required,gt=0,lt=150"` - Email string `validate:"email"` - ContactInformation []*ContactInformation - } - - contactInfo := &ContactInformation{ - Street: "26 Here Blvd.", - City: "Paradeso", - } - - user := &User{ - Name: "Joey Bloggs", - Age: 31, - Email: "joeybloggs@gmail.com", - ContactInformation: []*ContactInformation{contactInfo}, - } - - structError := validate.Struct(user) - for _, fieldError := range structError.Errors { - fmt.Println(fieldError.Field) // Phone - fmt.Println(fieldError.Tag) // required - //... and so forth - //Output: - //Phone - //required - } -} +// func ExampleValidate_new() { +// validator.New("validate", validator.BakedInValidators) +// } + +// func ExampleValidate_addFunction() { +// // This should be stored somewhere globally +// var validate *validator.Validate + +// validate = validator.New("validate", validator.BakedInValidators) + +// fn := func(top interface{}, current interface{}, field interface{}, param string) bool { +// return field.(string) == "hello" +// } + +// validate.AddFunction("valueishello", fn) + +// message := "hello" +// err := validate.Field(message, "valueishello") +// fmt.Println(err) +// //Output: +// // +// } + +// func ExampleValidate_field() { +// // This should be stored somewhere globally +// var validate *validator.Validate + +// validate = validator.New("validate", validator.BakedInValidators) + +// i := 0 +// err := validate.Field(i, "gt=1,lte=10") +// fmt.Println(err.Field) +// fmt.Println(err.Tag) +// fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time +// fmt.Println(err.Type) +// fmt.Println(err.Param) +// fmt.Println(err.Value) +// //Output: +// // +// //gt +// //int +// //int +// //1 +// //0 +// } + +// func ExampleValidate_struct() { +// // This should be stored somewhere globally +// var validate *validator.Validate + +// validate = validator.New("validate", validator.BakedInValidators) + +// type ContactInformation struct { +// Phone string `validate:"required"` +// Street string `validate:"required"` +// City string `validate:"required"` +// } + +// type User struct { +// Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) +// Age int8 `validate:"required,gt=0,lt=150"` +// Email string `validate:"email"` +// ContactInformation []*ContactInformation +// } + +// contactInfo := &ContactInformation{ +// Street: "26 Here Blvd.", +// City: "Paradeso", +// } + +// user := &User{ +// Name: "Joey Bloggs", +// Age: 31, +// Email: "joeybloggs@gmail.com", +// ContactInformation: []*ContactInformation{contactInfo}, +// } + +// structError := validate.Struct(user) +// for _, fieldError := range structError.Errors { +// fmt.Println(fieldError.Field) // Phone +// fmt.Println(fieldError.Tag) // required +// //... and so forth +// //Output: +// //Phone +// //required +// } +// } diff --git a/old/validator_old.go b/old/validator_old.go new file mode 100644 index 0000000..ccd2a55 --- /dev/null +++ b/old/validator_old.go @@ -0,0 +1,989 @@ +/** + * Package validator + * + * MISC: + * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank + * + */ + +package validator + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "strings" + "sync" + "time" + "unicode" +) + +const ( + utf8HexComma = "0x2C" + tagSeparator = "," + orSeparator = "|" + noValidationTag = "-" + tagKeySeparator = "=" + structOnlyTag = "structonly" + omitempty = "omitempty" + required = "required" + fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" + sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s" + mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" + structErrMsg = "Struct:%s\n" + diveTag = "dive" + arrayIndexFieldName = "%s[%d]" + mapIndexFieldName = "%s[%v]" +) + +var structPool *sync.Pool + +// returns new *StructErrors to the pool +func newStructErrors() interface{} { + return &StructErrors{ + Errors: map[string]*FieldError{}, + StructErrors: map[string]*StructErrors{}, + } +} + +type cachedTags struct { + keyVals [][]string + isOrVal bool +} + +type cachedField struct { + index int + name string + tags []*cachedTags + tag string + kind reflect.Kind + typ reflect.Type + isTime bool + isSliceOrArray bool + isMap bool + isTimeSubtype bool + sliceSubtype reflect.Type + mapSubtype reflect.Type + sliceSubKind reflect.Kind + mapSubKind reflect.Kind + dive bool + diveTag string +} + +type cachedStruct struct { + children int + name string + kind reflect.Kind + fields []*cachedField +} + +type structsCacheMap struct { + lock sync.RWMutex + m map[reflect.Type]*cachedStruct +} + +func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + +var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}} + +type fieldsCacheMap struct { + lock sync.RWMutex + m map[string][]*cachedTags +} + +func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *fieldsCacheMap) Set(key string, value []*cachedTags) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + +var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}} + +// 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 + Kind reflect.Kind + Type reflect.Type + Param string + Value interface{} + IsPlaceholderErr bool + IsSliceOrArray bool + IsMap bool + SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors + MapErrs map[interface{}]error // counld be FieldError, StructErrors +} + +// This is intended for use in development + debugging and not intended to be a production error message. +// it also allows FieldError to be used as an Error interface +func (e *FieldError) Error() string { + + if e.IsPlaceholderErr { + + buff := bytes.NewBufferString("") + + if e.IsSliceOrArray { + + for j, err := range e.SliceOrArrayErrs { + buff.WriteString("\n") + buff.WriteString(fmt.Sprintf(sliceErrMsg, e.Field, j, "\n"+err.Error())) + } + + } else if e.IsMap { + + for key, err := range e.MapErrs { + buff.WriteString(fmt.Sprintf(mapErrMsg, e.Field, key, "\n"+err.Error())) + } + } + + return strings.TrimSpace(buff.String()) + } + + return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag) +} + +// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name +// for those that want/need it. +// This is now needed because of the new dive functionality +func (e *FieldError) Flatten() map[string]*FieldError { + + errs := map[string]*FieldError{} + + if e.IsPlaceholderErr { + + if e.IsSliceOrArray { + for key, err := range e.SliceOrArrayErrs { + + fe, ok := err.(*FieldError) + + if ok { + + if flat := fe.Flatten(); flat != nil && len(flat) > 0 { + for k, v := range flat { + if fe.IsPlaceholderErr { + errs[fmt.Sprintf("[%#v]%s", key, k)] = v + } else { + errs[fmt.Sprintf("[%#v]", key)] = v + } + + } + } + } else { + + se := err.(*StructErrors) + + if flat := se.Flatten(); flat != nil && len(flat) > 0 { + for k, v := range flat { + errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v + } + } + } + } + } + + if e.IsMap { + for key, err := range e.MapErrs { + + fe, ok := err.(*FieldError) + + if ok { + + if flat := fe.Flatten(); flat != nil && len(flat) > 0 { + for k, v := range flat { + if fe.IsPlaceholderErr { + errs[fmt.Sprintf("[%#v]%s", key, k)] = v + } else { + errs[fmt.Sprintf("[%#v]", key)] = v + } + } + } + } else { + + se := err.(*StructErrors) + + if flat := se.Flatten(); flat != nil && len(flat) > 0 { + for k, v := range flat { + errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v + } + } + } + } + } + + return errs + } + + errs[e.Field] = e + + return errs +} + +// StructErrors is hierarchical list of field and struct validation errors +// for a non hierarchical representation please see the Flatten method for StructErrors +type StructErrors struct { + // Name of the Struct + Struct string + // Struct Field Errors + Errors map[string]*FieldError + // 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]*StructErrors +} + +// This is intended for use in development + debugging and not intended to be a production error message. +// it also allows StructErrors to be used as an Error interface +func (e *StructErrors) Error() string { + buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct)) + + for _, err := range e.Errors { + buff.WriteString(err.Error()) + buff.WriteString("\n") + } + + for _, err := range e.StructErrors { + buff.WriteString(err.Error()) + } + + return strings.TrimSpace(buff.String()) +} + +// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name +// for those that want/need it +func (e *StructErrors) Flatten() map[string]*FieldError { + + if e == nil { + return nil + } + + errs := map[string]*FieldError{} + + for _, f := range e.Errors { + + if flat := f.Flatten(); flat != nil && len(flat) > 0 { + + for k, fe := range flat { + + if f.IsPlaceholderErr { + errs[f.Field+k] = fe + } else { + errs[k] = fe + } + } + } + } + + for key, val := range e.StructErrors { + + otherErrs := val.Flatten() + + for _, f2 := range otherErrs { + + f2.Field = fmt.Sprintf("%s.%s", key, f2.Field) + errs[f2.Field] = f2 + } + } + + return errs +} + +// Func accepts all values needed for file and cross field validation +// top = top level struct when validating by struct otherwise nil +// current = current level struct when validating by struct otherwise optional comparison value +// f = field value for validation +// 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 + +// 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 +// or make it thread safe on your end +type Validate struct { + // tagName being used. + tagName string + // validateFuncs is a map of validation functions and the tag keys + validationFuncs map[string]Func +} + +// New creates a new Validate instance for use. +func New(tagName string, funcs map[string]Func) *Validate { + + structPool = &sync.Pool{New: newStructErrors} + + return &Validate{ + tagName: tagName, + validationFuncs: funcs, + } +} + +// SetTag sets tagName of the Validator to one of your choosing after creation +// perhaps to dodge a tag name conflict in a specific section of code +// NOTE: this method is not thread-safe +func (v *Validate) SetTag(tagName string) { + v.tagName = tagName +} + +// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained +// performance tuning towards your application, however, the default should be fine for +// nearly all cases. only increase if you have a deeply nested struct structure. +// NOTE: this method is not thread-safe +// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed +func (v *Validate) SetMaxStructPoolSize(max int) { + structPool = &sync.Pool{New: newStructErrors} +} + +// AddFunction adds a validation Func to a Validate's map of validators denoted by the key +// NOTE: if the key already exists, it will get replaced. +// NOTE: this method is not thread-safe +func (v *Validate) AddFunction(key string, f Func) error { + + if len(key) == 0 { + return errors.New("Function Key cannot be empty") + } + + if f == nil { + return errors.New("Function cannot be empty") + } + + v.validationFuncs[key] = f + + return nil +} + +// Struct validates a struct, even it's nested structs, and returns a struct containing the errors +// 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{}) *StructErrors { + + return v.structRecursive(s, s, s) +} + +// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors +func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors { + + structValue := reflect.ValueOf(s) + + if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { + return v.structRecursive(top, current, structValue.Elem().Interface()) + } + + if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { + panic("interface passed for validation is not a struct") + } + + structType := reflect.TypeOf(s) + + var structName string + var numFields int + var cs *cachedStruct + var isCached bool + + cs, isCached = structCache.Get(structType) + + if isCached { + structName = cs.name + numFields = cs.children + } else { + structName = structType.Name() + numFields = structValue.NumField() + cs = &cachedStruct{name: structName, children: numFields} + } + + validationErrors := structPool.Get().(*StructErrors) + validationErrors.Struct = structName + + for i := 0; i < numFields; i++ { + + var valueField reflect.Value + var cField *cachedField + var typeField reflect.StructField + + if isCached { + cField = cs.fields[i] + valueField = structValue.Field(cField.index) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + } + } else { + valueField = structValue.Field(i) + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + } + + typeField = structType.Field(i) + + cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))} + + if cField.tag == noValidationTag { + cs.children-- + continue + } + + // if no validation and not a struct (which may containt fields for validation) + if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { + cs.children-- + continue + } + + cField.name = typeField.Name + cField.kind = valueField.Kind() + cField.typ = valueField.Type() + } + + // this can happen if the first cache value was nil + // but the second actually has a value + if cField.kind == reflect.Ptr { + cField.kind = valueField.Kind() + } + + switch cField.kind { + + case reflect.Struct, reflect.Interface: + + if !unicode.IsUpper(rune(cField.name[0])) { + cs.children-- + continue + } + + if cField.isTime { + + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + + } else { + + if strings.Contains(cField.tag, structOnlyTag) { + cs.children-- + continue + } + + if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() { + + if strings.Contains(cField.tag, omitempty) { + goto CACHEFIELD + } + + tags := strings.Split(cField.tag, tagSeparator) + + if len(tags) > 0 { + + var param string + vals := strings.SplitN(tags[0], tagKeySeparator, 2) + + if len(vals) > 1 { + param = vals[1] + } + + validationErrors.Errors[cField.name] = &FieldError{ + Field: cField.name, + Tag: vals[0], + Param: param, + Value: valueField.Interface(), + Kind: valueField.Kind(), + Type: valueField.Type(), + } + + goto CACHEFIELD + } + } + + // if we get here, the field is interface and could be a struct or a field + // and we need to check the inner type and validate + if cField.kind == reflect.Interface { + + valueField = valueField.Elem() + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + } + + if valueField.Kind() == reflect.Struct { + goto VALIDATESTRUCT + } + + // sending nil for cField as it was type interface and could be anything + // each time and so must be calculated each time and can't be cached reliably + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + + goto CACHEFIELD + } + + VALIDATESTRUCT: + if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil { + validationErrors.StructErrors[cField.name] = structErrors + // free up memory map no longer needed + structErrors = nil + } + } + + case reflect.Slice, reflect.Array: + cField.isSliceOrArray = true + cField.sliceSubtype = cField.typ.Elem() + cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) + cField.sliceSubKind = cField.sliceSubtype.Kind() + + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + + case reflect.Map: + cField.isMap = true + cField.mapSubtype = cField.typ.Elem() + cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) + cField.mapSubKind = cField.mapSubtype.Kind() + + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + + default: + if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { + validationErrors.Errors[fieldError.Field] = fieldError + // free up memory reference + fieldError = nil + } + } + + CACHEFIELD: + if !isCached { + cs.fields = append(cs.fields, cField) + } + } + + structCache.Set(structType, cs) + + if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { + structPool.Put(validationErrors) + return nil + } + + return validationErrors +} + +// Field allows validation of a single field, still using tag style validation to check multiple errors +func (v *Validate) Field(f interface{}, tag string) *FieldError { + return v.FieldWithValue(nil, f, tag) +} + +// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors +func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError { + return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil) +} + +func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError { + + var cField *cachedField + var isCached bool + var valueField reflect.Value + + // This is a double check if coming from validate.Struct but need to be here in case function is called directly + if tag == noValidationTag || tag == "" { + return nil + } + + if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") { + return nil + } + + valueField = reflect.ValueOf(f) + + if cacheField == nil { + + if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { + valueField = valueField.Elem() + f = valueField.Interface() + } + + cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()} + + switch cField.kind { + case reflect.Slice, reflect.Array: + isSingleField = false // cached tags mean nothing because it will be split up while diving + cField.isSliceOrArray = true + cField.sliceSubtype = cField.typ.Elem() + cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) + cField.sliceSubKind = cField.sliceSubtype.Kind() + case reflect.Map: + isSingleField = false // cached tags mean nothing because it will be split up while diving + cField.isMap = true + cField.mapSubtype = cField.typ.Elem() + cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) + cField.mapSubKind = cField.mapSubtype.Kind() + } + } else { + cField = cacheField + } + + switch cField.kind { + + case reflect.Struct, reflect.Interface, reflect.Invalid: + + if cField.typ != reflect.TypeOf(time.Time{}) { + panic("Invalid field passed to fieldWithNameAndValue") + } + } + + if len(cField.tags) == 0 { + + if isSingleField { + cField.tags, isCached = fieldsCache.Get(tag) + } + + if !isCached { + + for _, t := range strings.Split(tag, tagSeparator) { + + if t == diveTag { + + cField.dive = true + cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") + break + } + + orVals := strings.Split(t, orSeparator) + cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} + cField.tags = append(cField.tags, cTag) + + for i, val := range orVals { + vals := strings.SplitN(val, tagKeySeparator, 2) + + key := strings.TrimSpace(vals[0]) + + if len(key) == 0 { + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + } + + param := "" + if len(vals) > 1 { + param = strings.Replace(vals[1], utf8HexComma, ",", -1) + } + + cTag.keyVals[i] = []string{key, param} + } + } + + if isSingleField { + fieldsCache.Set(cField.tag, cField.tags) + } + } + } + + var fieldErr *FieldError + var err error + + for _, cTag := range cField.tags { + + if cTag.isOrVal { + + errTag := "" + + for _, val := range cTag.keyVals { + + fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name) + + if err == nil { + return nil + } + + errTag += orSeparator + fieldErr.Tag + } + + errTag = strings.TrimLeft(errTag, orSeparator) + + fieldErr.Tag = errTag + fieldErr.Kind = cField.kind + fieldErr.Type = cField.typ + + return fieldErr + } + + if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil { + + fieldErr.Kind = cField.kind + fieldErr.Type = cField.typ + + return fieldErr + } + } + + if cField.dive { + + if cField.isSliceOrArray { + + if errs := v.traverseSliceOrArray(val, current, valueField, cField); errs != nil && len(errs) > 0 { + + return &FieldError{ + Field: cField.name, + Kind: cField.kind, + Type: cField.typ, + Value: f, + IsPlaceholderErr: true, + IsSliceOrArray: true, + SliceOrArrayErrs: errs, + } + } + + } else if cField.isMap { + if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 { + + return &FieldError{ + Field: cField.name, + Kind: cField.kind, + Type: cField.typ, + Value: f, + IsPlaceholderErr: true, + IsMap: true, + MapErrs: errs, + } + } + } else { + // throw error, if not a slice or map then should not have gotten here + panic("dive error! can't dive on a non slice or map") + } + } + + return nil +} + +func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error { + + errs := map[interface{}]error{} + + for _, key := range valueField.MapKeys() { + + idxField := valueField.MapIndex(key) + + if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + cField.mapSubKind = idxField.Kind() + } + + switch cField.mapSubKind { + case reflect.Struct, reflect.Interface: + + if cField.isTimeSubtype { + + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { + errs[key.Interface()] = fieldError + } + + continue + } + + if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { + + if strings.Contains(cField.diveTag, omitempty) { + continue + } + + tags := strings.Split(cField.diveTag, tagSeparator) + + if len(tags) > 0 { + + var param string + vals := strings.SplitN(tags[0], tagKeySeparator, 2) + + if len(vals) > 1 { + param = vals[1] + } + + errs[key.Interface()] = &FieldError{ + Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), + Tag: vals[0], + Param: param, + Value: idxField.Interface(), + Kind: idxField.Kind(), + Type: cField.mapSubtype, + } + } + + continue + } + + // if we get here, the field is interface and could be a struct or a field + // and we need to check the inner type and validate + if idxField.Kind() == reflect.Interface { + + idxField = idxField.Elem() + + if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + } + + if idxField.Kind() == reflect.Struct { + goto VALIDATESTRUCT + } + + // sending nil for cField as it was type interface and could be anything + // each time and so must be calculated each time and can't be cached reliably + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { + errs[key.Interface()] = fieldError + } + + continue + } + + VALIDATESTRUCT: + if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { + errs[key.Interface()] = structErrors + } + + default: + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { + errs[key.Interface()] = fieldError + } + } + } + + return errs +} + +func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error { + + errs := map[int]error{} + + for i := 0; i < valueField.Len(); i++ { + + idxField := valueField.Index(i) + + if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + cField.sliceSubKind = idxField.Kind() + } + + switch cField.sliceSubKind { + case reflect.Struct, reflect.Interface: + + if cField.isTimeSubtype { + + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { + errs[i] = fieldError + } + + continue + } + + if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { + + if strings.Contains(cField.diveTag, omitempty) { + continue + } + + tags := strings.Split(cField.diveTag, tagSeparator) + + if len(tags) > 0 { + + var param string + vals := strings.SplitN(tags[0], tagKeySeparator, 2) + + if len(vals) > 1 { + param = vals[1] + } + + errs[i] = &FieldError{ + Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i), + Tag: vals[0], + Param: param, + Value: idxField.Interface(), + Kind: idxField.Kind(), + Type: cField.sliceSubtype, + } + } + + continue + } + + // if we get here, the field is interface and could be a struct or a field + // and we need to check the inner type and validate + if idxField.Kind() == reflect.Interface { + + idxField = idxField.Elem() + + if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + } + + if idxField.Kind() == reflect.Struct { + goto VALIDATESTRUCT + } + + // sending nil for cField as it was type interface and could be anything + // each time and so must be calculated each time and can't be cached reliably + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { + errs[i] = fieldError + } + + continue + } + + VALIDATESTRUCT: + if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { + errs[i] = structErrors + } + + default: + if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { + errs[i] = fieldError + } + } + } + + return errs +} + +func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) { + + // OK to continue because we checked it's existance before getting into this loop + if key == omitempty { + return nil, nil + } + + valFunc, ok := v.validationFuncs[key] + if !ok { + panic(fmt.Sprintf("Undefined validation function on field %s", name)) + } + + if err := valFunc(val, current, f, param); err { + return nil, nil + } + + return &FieldError{ + Field: name, + Tag: key, + Value: f, + Param: param, + }, errors.New(key) +} diff --git a/validator.go b/validator.go index ccd2a55..1607d28 100644 --- a/validator.go +++ b/validator.go @@ -8,302 +8,19 @@ package validator -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "sync" - "time" - "unicode" -) - -const ( - utf8HexComma = "0x2C" - tagSeparator = "," - orSeparator = "|" - noValidationTag = "-" - tagKeySeparator = "=" - structOnlyTag = "structonly" - omitempty = "omitempty" - required = "required" - fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" - sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s" - mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" - structErrMsg = "Struct:%s\n" - diveTag = "dive" - arrayIndexFieldName = "%s[%d]" - mapIndexFieldName = "%s[%v]" -) - -var structPool *sync.Pool - -// returns new *StructErrors to the pool -func newStructErrors() interface{} { - return &StructErrors{ - Errors: map[string]*FieldError{}, - StructErrors: map[string]*StructErrors{}, - } -} - -type cachedTags struct { - keyVals [][]string - isOrVal bool -} - -type cachedField struct { - index int - name string - tags []*cachedTags - tag string - kind reflect.Kind - typ reflect.Type - isTime bool - isSliceOrArray bool - isMap bool - isTimeSubtype bool - sliceSubtype reflect.Type - mapSubtype reflect.Type - sliceSubKind reflect.Kind - mapSubKind reflect.Kind - dive bool - diveTag string -} - -type cachedStruct struct { - children int - name string - kind reflect.Kind - fields []*cachedField -} - -type structsCacheMap struct { - lock sync.RWMutex - m map[reflect.Type]*cachedStruct -} - -func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) { - s.lock.RLock() - defer s.lock.RUnlock() - value, ok := s.m[key] - return value, ok -} - -func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) { - s.lock.Lock() - defer s.lock.Unlock() - s.m[key] = value -} - -var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}} - -type fieldsCacheMap struct { - lock sync.RWMutex - m map[string][]*cachedTags -} - -func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) { - s.lock.RLock() - defer s.lock.RUnlock() - value, ok := s.m[key] - return value, ok -} - -func (s *fieldsCacheMap) Set(key string, value []*cachedTags) { - s.lock.Lock() - defer s.lock.Unlock() - s.m[key] = value -} - -var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}} - -// 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 - Kind reflect.Kind - Type reflect.Type - Param string - Value interface{} - IsPlaceholderErr bool - IsSliceOrArray bool - IsMap bool - SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors - MapErrs map[interface{}]error // counld be FieldError, StructErrors -} - -// This is intended for use in development + debugging and not intended to be a production error message. -// it also allows FieldError to be used as an Error interface -func (e *FieldError) Error() string { - - if e.IsPlaceholderErr { - - buff := bytes.NewBufferString("") - - if e.IsSliceOrArray { - - for j, err := range e.SliceOrArrayErrs { - buff.WriteString("\n") - buff.WriteString(fmt.Sprintf(sliceErrMsg, e.Field, j, "\n"+err.Error())) - } - - } else if e.IsMap { - - for key, err := range e.MapErrs { - buff.WriteString(fmt.Sprintf(mapErrMsg, e.Field, key, "\n"+err.Error())) - } - } - - return strings.TrimSpace(buff.String()) - } - - return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag) -} - -// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name -// for those that want/need it. -// This is now needed because of the new dive functionality -func (e *FieldError) Flatten() map[string]*FieldError { - - errs := map[string]*FieldError{} - - if e.IsPlaceholderErr { - - if e.IsSliceOrArray { - for key, err := range e.SliceOrArrayErrs { - - fe, ok := err.(*FieldError) - - if ok { - - if flat := fe.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - if fe.IsPlaceholderErr { - errs[fmt.Sprintf("[%#v]%s", key, k)] = v - } else { - errs[fmt.Sprintf("[%#v]", key)] = v - } - - } - } - } else { - - se := err.(*StructErrors) - - if flat := se.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v - } - } - } - } - } - - if e.IsMap { - for key, err := range e.MapErrs { - - fe, ok := err.(*FieldError) - - if ok { - - if flat := fe.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - if fe.IsPlaceholderErr { - errs[fmt.Sprintf("[%#v]%s", key, k)] = v - } else { - errs[fmt.Sprintf("[%#v]", key)] = v - } - } - } - } else { - - se := err.(*StructErrors) - - if flat := se.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v - } - } - } - } - } - - return errs - } - - errs[e.Field] = e - - return errs -} - -// StructErrors is hierarchical list of field and struct validation errors -// for a non hierarchical representation please see the Flatten method for StructErrors -type StructErrors struct { - // Name of the Struct - Struct string - // Struct Field Errors - Errors map[string]*FieldError - // 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]*StructErrors -} - -// This is intended for use in development + debugging and not intended to be a production error message. -// it also allows StructErrors to be used as an Error interface -func (e *StructErrors) Error() string { - buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct)) - - for _, err := range e.Errors { - buff.WriteString(err.Error()) - buff.WriteString("\n") - } - - for _, err := range e.StructErrors { - buff.WriteString(err.Error()) - } - - return strings.TrimSpace(buff.String()) +// 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 +// or make it thread safe on your end +type Validate struct { + config Config } -// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name -// for those that want/need it -func (e *StructErrors) Flatten() map[string]*FieldError { - - if e == nil { - return nil - } - - errs := map[string]*FieldError{} - - for _, f := range e.Errors { - - if flat := f.Flatten(); flat != nil && len(flat) > 0 { - - for k, fe := range flat { - - if f.IsPlaceholderErr { - errs[f.Field+k] = fe - } else { - errs[k] = fe - } - } - } - } - - for key, val := range e.StructErrors { - - otherErrs := val.Flatten() - - for _, f2 := range otherErrs { - - f2.Field = fmt.Sprintf("%s.%s", key, f2.Field) - errs[f2.Field] = f2 - } - } - - return errs +// Config contains the options that Validator with use +// passed to the New function +type Config struct { + TagName string + ValidationFuncs map[string]Func } // Func accepts all values needed for file and cross field validation @@ -313,677 +30,124 @@ func (e *StructErrors) Flatten() map[string]*FieldError { // 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 -// 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 -// or make it thread safe on your end -type Validate struct { - // tagName being used. - tagName string - // validateFuncs is a map of validation functions and the tag keys - validationFuncs map[string]Func +// 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 + // Kind reflect.Kind + // Type reflect.Type + // Param string + // Value interface{} + // IsPlaceholderErr bool + // IsSliceOrArray bool + // IsMap bool + // SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors + // MapErrs map[interface{}]error // counld be FieldError, StructErrors } // New creates a new Validate instance for use. -func New(tagName string, funcs map[string]Func) *Validate { - - structPool = &sync.Pool{New: newStructErrors} - - return &Validate{ - tagName: tagName, - validationFuncs: funcs, - } -} +func New(config Config) *Validate { -// SetTag sets tagName of the Validator to one of your choosing after creation -// perhaps to dodge a tag name conflict in a specific section of code -// NOTE: this method is not thread-safe -func (v *Validate) SetTag(tagName string) { - v.tagName = tagName -} - -// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained -// performance tuning towards your application, however, the default should be fine for -// nearly all cases. only increase if you have a deeply nested struct structure. -// NOTE: this method is not thread-safe -// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed -func (v *Validate) SetMaxStructPoolSize(max int) { - structPool = &sync.Pool{New: newStructErrors} -} - -// AddFunction adds a validation Func to a Validate's map of validators denoted by the key -// NOTE: if the key already exists, it will get replaced. -// NOTE: this method is not thread-safe -func (v *Validate) AddFunction(key string, f Func) error { - - if len(key) == 0 { - return errors.New("Function Key cannot be empty") - } - - if f == nil { - return errors.New("Function cannot be empty") - } - - v.validationFuncs[key] = f + // structPool = &sync.Pool{New: newStructErrors} - return nil + return &Validate{config: config} } // Struct validates a struct, even it's nested structs, and returns a struct containing the errors // 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{}) *StructErrors { - - return v.structRecursive(s, s, s) -} - -// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors -func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors { - - structValue := reflect.ValueOf(s) - - if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { - return v.structRecursive(top, current, structValue.Elem().Interface()) - } - - if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { - panic("interface passed for validation is not a struct") - } - - structType := reflect.TypeOf(s) - - var structName string - var numFields int - var cs *cachedStruct - var isCached bool - - cs, isCached = structCache.Get(structType) - - if isCached { - structName = cs.name - numFields = cs.children - } else { - structName = structType.Name() - numFields = structValue.NumField() - cs = &cachedStruct{name: structName, children: numFields} - } - - validationErrors := structPool.Get().(*StructErrors) - validationErrors.Struct = structName - - for i := 0; i < numFields; i++ { - - var valueField reflect.Value - var cField *cachedField - var typeField reflect.StructField - - if isCached { - cField = cs.fields[i] - valueField = structValue.Field(cField.index) - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - } else { - valueField = structValue.Field(i) - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - - typeField = structType.Field(i) - - cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))} - - if cField.tag == noValidationTag { - cs.children-- - continue - } - - // if no validation and not a struct (which may containt fields for validation) - if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { - cs.children-- - continue - } - - cField.name = typeField.Name - cField.kind = valueField.Kind() - cField.typ = valueField.Type() - } - - // this can happen if the first cache value was nil - // but the second actually has a value - if cField.kind == reflect.Ptr { - cField.kind = valueField.Kind() - } - - switch cField.kind { +func (v *Validate) Struct(s interface{}) map[string]*FieldError { - case reflect.Struct, reflect.Interface: - - if !unicode.IsUpper(rune(cField.name[0])) { - cs.children-- - continue - } - - if cField.isTime { - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - } else { - - if strings.Contains(cField.tag, structOnlyTag) { - cs.children-- - continue - } - - if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() { - - if strings.Contains(cField.tag, omitempty) { - goto CACHEFIELD - } - - tags := strings.Split(cField.tag, tagSeparator) - - if len(tags) > 0 { - - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) - - if len(vals) > 1 { - param = vals[1] - } - - validationErrors.Errors[cField.name] = &FieldError{ - Field: cField.name, - Tag: vals[0], - Param: param, - Value: valueField.Interface(), - Kind: valueField.Kind(), - Type: valueField.Type(), - } - - goto CACHEFIELD - } - } - - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if cField.kind == reflect.Interface { - - valueField = valueField.Elem() - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - - if valueField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - goto CACHEFIELD - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil { - validationErrors.StructErrors[cField.name] = structErrors - // free up memory map no longer needed - structErrors = nil - } - } - - case reflect.Slice, reflect.Array: - cField.isSliceOrArray = true - cField.sliceSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) - cField.sliceSubKind = cField.sliceSubtype.Kind() - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - case reflect.Map: - cField.isMap = true - cField.mapSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) - cField.mapSubKind = cField.mapSubtype.Kind() - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - default: - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - } - - CACHEFIELD: - if !isCached { - cs.fields = append(cs.fields, cField) - } - } - - structCache.Set(structType, cs) - - if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { - structPool.Put(validationErrors) - return nil - } - - return validationErrors -} - -// Field allows validation of a single field, still using tag style validation to check multiple errors -func (v *Validate) Field(f interface{}, tag string) *FieldError { - return v.FieldWithValue(nil, f, tag) -} + // 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 + // } + // } -// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors -func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError { - return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil) + return errs } -func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError { - - var cField *cachedField - var isCached bool - var valueField reflect.Value - - // This is a double check if coming from validate.Struct but need to be here in case function is called directly - if tag == noValidationTag || tag == "" { - return nil - } - - if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") { - return nil - } - - valueField = reflect.ValueOf(f) - - if cacheField == nil { - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - f = valueField.Interface() - } - - cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()} - - switch cField.kind { - case reflect.Slice, reflect.Array: - isSingleField = false // cached tags mean nothing because it will be split up while diving - cField.isSliceOrArray = true - cField.sliceSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) - cField.sliceSubKind = cField.sliceSubtype.Kind() - case reflect.Map: - isSingleField = false // cached tags mean nothing because it will be split up while diving - cField.isMap = true - cField.mapSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) - cField.mapSubKind = cField.mapSubtype.Kind() - } - } else { - cField = cacheField - } - - switch cField.kind { - - case reflect.Struct, reflect.Interface, reflect.Invalid: - - if cField.typ != reflect.TypeOf(time.Time{}) { - panic("Invalid field passed to fieldWithNameAndValue") - } - } - - if len(cField.tags) == 0 { - - if isSingleField { - cField.tags, isCached = fieldsCache.Get(tag) - } - - if !isCached { - - for _, t := range strings.Split(tag, tagSeparator) { - - if t == diveTag { - - cField.dive = true - cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") - break - } - - orVals := strings.Split(t, orSeparator) - cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} - cField.tags = append(cField.tags, cTag) - - for i, val := range orVals { - vals := strings.SplitN(val, tagKeySeparator, 2) - - key := strings.TrimSpace(vals[0]) - - if len(key) == 0 { - panic(fmt.Sprintf("Invalid validation tag on field %s", name)) - } - - param := "" - if len(vals) > 1 { - param = strings.Replace(vals[1], utf8HexComma, ",", -1) - } - - cTag.keyVals[i] = []string{key, param} - } - } - - if isSingleField { - fieldsCache.Set(cField.tag, cField.tags) - } - } - } - - var fieldErr *FieldError - var err error - - for _, cTag := range cField.tags { - - if cTag.isOrVal { - - errTag := "" - - for _, val := range cTag.keyVals { +func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}, depth int, errs map[string]*FieldError) { - fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name) + errs["Name"] = &FieldError{Field: "Name"} - if err == nil { - return nil - } - - errTag += orSeparator + fieldErr.Tag - } - - errTag = strings.TrimLeft(errTag, orSeparator) - - fieldErr.Tag = errTag - fieldErr.Kind = cField.kind - fieldErr.Type = cField.typ - - return fieldErr - } - - if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil { - - fieldErr.Kind = cField.kind - fieldErr.Type = cField.typ - - return fieldErr - } - } - - if cField.dive { - - if cField.isSliceOrArray { - - if errs := v.traverseSliceOrArray(val, current, valueField, cField); errs != nil && len(errs) > 0 { - - return &FieldError{ - Field: cField.name, - Kind: cField.kind, - Type: cField.typ, - Value: f, - IsPlaceholderErr: true, - IsSliceOrArray: true, - SliceOrArrayErrs: errs, - } - } - - } else if cField.isMap { - if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 { - - return &FieldError{ - Field: cField.name, - Kind: cField.kind, - Type: cField.typ, - Value: f, - IsPlaceholderErr: true, - IsMap: true, - MapErrs: errs, - } - } - } else { - // throw error, if not a slice or map then should not have gotten here - panic("dive error! can't dive on a non slice or map") - } + if depth < 3 { + // wg.Add(1) + v.structRecursive(s, s, s, depth+1, errs) } - return nil -} - -func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error { - - errs := map[interface{}]error{} - - for _, key := range valueField.MapKeys() { - - idxField := valueField.MapIndex(key) - - if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - cField.mapSubKind = idxField.Kind() - } - - switch cField.mapSubKind { - case reflect.Struct, reflect.Interface: - - if cField.isTimeSubtype { - - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - - continue - } - - if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { - - if strings.Contains(cField.diveTag, omitempty) { - continue - } - - tags := strings.Split(cField.diveTag, tagSeparator) - - if len(tags) > 0 { - - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) - - if len(vals) > 1 { - param = vals[1] - } - - errs[key.Interface()] = &FieldError{ - Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), - Tag: vals[0], - Param: param, - Value: idxField.Interface(), - Kind: idxField.Kind(), - Type: cField.mapSubtype, - } - } - - continue - } - - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if idxField.Kind() == reflect.Interface { - - idxField = idxField.Elem() - - if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - } + // wg.Wait() - if idxField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - - continue - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { - errs[key.Interface()] = structErrors - } - - default: - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - } - } - - return errs + // if depth == 0 { + // // wg.Wait() + // done <- true + // // return + // } else { + // // wg.Done() + // } } -func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error { - - errs := map[int]error{} - - for i := 0; i < valueField.Len(); i++ { - - idxField := valueField.Index(i) - - if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - cField.sliceSubKind = idxField.Kind() - } - - switch cField.sliceSubKind { - case reflect.Struct, reflect.Interface: - - if cField.isTimeSubtype { - - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - - continue - } - - if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { - - if strings.Contains(cField.diveTag, omitempty) { - continue - } - - tags := strings.Split(cField.diveTag, tagSeparator) +// // Struct validates a struct, even it's nested structs, and returns a struct containing the errors +// // 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 { - if len(tags) > 0 { +// // var err *FieldError +// errs := map[string]*FieldError{} +// errchan := make(chan *FieldError) +// done := make(chan bool) +// // wg := &sync.WaitGroup{} - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) +// go v.structRecursive(s, s, s, 0, errchan, done) - if len(vals) > 1 { - param = vals[1] - } +// LOOP: +// for { +// select { +// case err := <-errchan: +// errs[err.Field] = err +// // fmt.Println(err) +// case <-done: +// // fmt.Println("All Done") +// break LOOP +// } +// } - errs[i] = &FieldError{ - Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i), - Tag: vals[0], - Param: param, - Value: idxField.Interface(), - Kind: idxField.Kind(), - Type: cField.sliceSubtype, - } - } +// return errs +// } - continue - } +// func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}, depth int, errs chan *FieldError, done chan bool) { - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if idxField.Kind() == reflect.Interface { +// errs <- &FieldError{Field: "Name"} - idxField = idxField.Elem() +// if depth < 1 { +// // wg.Add(1) +// v.structRecursive(s, s, s, depth+1, errs, done) +// } - if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - } +// // wg.Wait() - if idxField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - - continue - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { - errs[i] = structErrors - } - - default: - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - } - } - - return errs -} - -func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) { - - // OK to continue because we checked it's existance before getting into this loop - if key == omitempty { - return nil, nil - } - - valFunc, ok := v.validationFuncs[key] - if !ok { - panic(fmt.Sprintf("Undefined validation function on field %s", name)) - } - - if err := valFunc(val, current, f, param); err { - return nil, nil - } - - return &FieldError{ - Field: name, - Tag: key, - Value: f, - Param: param, - }, errors.New(key) -} +// if depth == 0 { +// // wg.Wait() +// done <- true +// // return +// } else { +// // wg.Done() +// } +// } diff --git a/validator_test.go b/validator_test.go index 8bfff9b..63a11c7 100644 --- a/validator_test.go +++ b/validator_test.go @@ -6,7 +6,6 @@ import ( "reflect" "runtime" "testing" - "time" ) // NOTES: @@ -108,7 +107,7 @@ type TestSlice struct { OmitEmpty []int `validate:"omitempty,min=1,max=10"` } -var validate = New("validate", BakedInValidators) +var validate = New(Config{TagName: "validate", ValidationFuncs: BakedInValidators}) func IsEqual(t *testing.T, val1, val2 interface{}) bool { v1 := reflect.ValueOf(val1) @@ -203,3618 +202,3631 @@ func PanicMatchesSkip(t *testing.T, skip int, fn func(), matches string) { fn() } -func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors { +func TestValidation(t *testing.T) { - val, ok := s.StructErrors[structFieldName] - EqualSkip(t, 2, ok, true) - NotEqualSkip(t, 2, val, nil) - EqualSkip(t, 2, val.Struct, expectedStructName) - - return val -} - -func AssertFieldError(t *testing.T, s *StructErrors, field string, expectedTag string) { - - val, ok := s.Errors[field] - EqualSkip(t, 2, ok, true) - NotEqualSkip(t, 2, val, nil) - EqualSkip(t, 2, val.Field, field) - EqualSkip(t, 2, val.Tag, expectedTag) -} - -func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, expectedTag string) { - - val, ok := s[field] - EqualSkip(t, 2, ok, true) - NotEqualSkip(t, 2, val, nil) - EqualSkip(t, 2, val.Field, field) - EqualSkip(t, 2, val.Tag, expectedTag) -} - -func TestBadKeyValidation(t *testing.T) { type Test struct { - Name string `validate:"required, "` - } - - tst := &Test{ - Name: "test", - } - - PanicMatches(t, func() { validate.Struct(tst) }, "Invalid validation tag on field Name") -} - -func TestFlattenValidation(t *testing.T) { - - type Inner struct { - Name string `validate:"required"` - } - - type TestMultiDimensionalStructsPtr struct { - Errs [][]*Inner `validate:"gt=0,dive,dive,required"` - } - - var errStructPtrArray [][]*Inner - - errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{"ok"}}) - - tmsp := &TestMultiDimensionalStructsPtr{ - Errs: errStructPtrArray, - } - - errs := validate.Struct(tmsp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - // for full test coverage - fmt.Sprint(errs.Error()) - - fieldErr := errs.Errors["Errs"] - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, fieldErr.Field, "Errs") - Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - - innerSlice1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSlice1.IsPlaceholderErr, true) - Equal(t, innerSlice1.Field, "Errs[0]") - - flatFieldErr, ok := fieldErr.Flatten()["[0][1].Inner.Name"] - Equal(t, ok, true) - Equal(t, flatFieldErr.Field, "Name") - Equal(t, flatFieldErr.Tag, "required") - - structErrFlatten, ok := errs.Flatten()["Errs[0][1].Inner.Name"] - Equal(t, ok, true) - Equal(t, structErrFlatten.Field, "Name") - Equal(t, structErrFlatten.Tag, "required") - - errStructPtrArray = [][]*Inner{} - errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, nil, &Inner{"ok"}}) - - tmsp = &TestMultiDimensionalStructsPtr{ - Errs: errStructPtrArray, - } - - errs = validate.Struct(tmsp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - // for full test coverage - fmt.Sprint(errs.Error()) - - fieldErr = errs.Errors["Errs"] - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, fieldErr.Field, "Errs") - Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - - innerSlice1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSlice1.IsPlaceholderErr, true) - Equal(t, innerSlice1.Field, "Errs[0]") - - flatFieldErr, ok = fieldErr.Flatten()["[0][1]"] - Equal(t, ok, true) - Equal(t, flatFieldErr.Field, "Errs[0][1]") - Equal(t, flatFieldErr.Tag, "required") - - type TestMapStructPtr struct { - Errs map[int]*Inner `validate:"gt=0,dive,required"` - } - - mip := map[int]*Inner{0: &Inner{"ok"}, 3: &Inner{""}, 4: &Inner{"ok"}} - - msp := &TestMapStructPtr{ - Errs: mip, - } - - errs = validate.Struct(msp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldError := errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 1) - - innerStructError, ok := fieldError.MapErrs[3].(*StructErrors) - Equal(t, ok, true) - Equal(t, innerStructError.Struct, "Inner") - Equal(t, len(innerStructError.Errors), 1) - - innerInnerFieldError, ok := innerStructError.Errors["Name"] - Equal(t, ok, true) - Equal(t, innerInnerFieldError.IsPlaceholderErr, false) - Equal(t, innerInnerFieldError.IsSliceOrArray, false) - Equal(t, innerInnerFieldError.Field, "Name") - Equal(t, innerInnerFieldError.Tag, "required") - - flatErrs, ok := errs.Flatten()["Errs[3].Inner.Name"] - Equal(t, ok, true) - Equal(t, flatErrs.Field, "Name") - Equal(t, flatErrs.Tag, "required") - - mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - - msp2 := &TestMapStructPtr{ - Errs: mip2, - } - - errs = validate.Struct(msp2) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldError = errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 1) - - innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) - Equal(t, ok, true) - Equal(t, innerFieldError.IsPlaceholderErr, false) - Equal(t, innerFieldError.IsSliceOrArray, false) - Equal(t, innerFieldError.Field, "Errs[3]") - Equal(t, innerFieldError.Tag, "required") - - flatErrs, ok = errs.Flatten()["Errs[3]"] - Equal(t, ok, true) - Equal(t, flatErrs.Field, "Errs[3]") - Equal(t, flatErrs.Tag, "required") - - type TestMapInnerArrayStruct struct { - Errs map[int][]string `validate:"gt=0,dive,dive,required"` + Name string } - mias := map[int][]string{0: []string{"ok"}, 3: []string{"ok", ""}, 4: []string{"ok"}} - - mia := &TestMapInnerArrayStruct{ - Errs: mias, - } + tst := &Test{Name: "Dean"} - errs = validate.Struct(mia) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) + errs := validate.Struct(tst) - flatErrs, ok = errs.Flatten()["Errs[3][1]"] - Equal(t, ok, true) - Equal(t, flatErrs.Field, "Errs[3][1]") - Equal(t, flatErrs.Tag, "required") + fmt.Println(errs) } -func TestInterfaceErrValidation(t *testing.T) { - - var v1 interface{} - var v2 interface{} - - v2 = 1 - v1 = v2 - - err := validate.Field(v1, "len=1") - Equal(t, err, nil) - err = validate.Field(v2, "len=1") - Equal(t, err, nil) - - type ExternalCMD struct { - Userid string `json:"userid"` - Action uint32 `json:"action"` - Data interface{} `json:"data,omitempty" validate:"required"` - } - - s := &ExternalCMD{ - Userid: "123456", - Action: 10000, - // Data: 1, - } - - errs := validate.Struct(s) - NotEqual(t, errs, nil) - Equal(t, errs.Errors["Data"].Field, "Data") - Equal(t, errs.Errors["Data"].Tag, "required") - - type ExternalCMD2 struct { - Userid string `json:"userid"` - Action uint32 `json:"action"` - Data interface{} `json:"data,omitempty" validate:"len=1"` - } - - s2 := &ExternalCMD2{ - Userid: "123456", - Action: 10000, - // Data: 1, - } - - errs = validate.Struct(s2) - NotEqual(t, errs, nil) - Equal(t, errs.Errors["Data"].Field, "Data") - Equal(t, errs.Errors["Data"].Tag, "len") - Equal(t, errs.Errors["Data"].Param, "1") - - s3 := &ExternalCMD2{ - Userid: "123456", - Action: 10000, - Data: 2, - } - - errs = validate.Struct(s3) - NotEqual(t, errs, nil) - Equal(t, errs.Errors["Data"].Field, "Data") - Equal(t, errs.Errors["Data"].Tag, "len") - Equal(t, errs.Errors["Data"].Param, "1") - - type Inner struct { - Name string `validate:"required"` - } - - inner := &Inner{ - Name: "", - } - - s4 := &ExternalCMD{ - Userid: "123456", - Action: 10000, - Data: inner, - } - - errs = validate.Struct(s4) - NotEqual(t, errs, nil) - Equal(t, errs.StructErrors["Data"].Struct, "Inner") - Equal(t, errs.StructErrors["Data"].Errors["Name"].Field, "Name") - Equal(t, errs.StructErrors["Data"].Errors["Name"].Tag, "required") +// func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors { - type TestMapStructPtr struct { - Errs map[int]interface{} `validate:"gt=0,dive,len=2"` - } - - mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - - msp := &TestMapStructPtr{ - Errs: mip, - } - - errs = validate.Struct(msp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldError := errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 1) - - innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) - Equal(t, ok, true) - Equal(t, innerFieldError.IsPlaceholderErr, false) - Equal(t, innerFieldError.IsMap, false) - Equal(t, len(innerFieldError.MapErrs), 0) - Equal(t, innerFieldError.Field, "Errs[3]") - Equal(t, innerFieldError.Tag, "len") - - type TestMultiDimensionalStructs struct { - Errs [][]interface{} `validate:"gt=0,dive,dive,len=2"` - } - - var errStructArray [][]interface{} - - errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// val, ok := s.StructErrors[structFieldName] +// EqualSkip(t, 2, ok, true) +// NotEqualSkip(t, 2, val, nil) +// EqualSkip(t, 2, val.Struct, expectedStructName) - tms := &TestMultiDimensionalStructs{ - Errs: errStructArray, - } +// return val +// } - errs = validate.Struct(tms) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok := errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - - sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerInnersliceError1 := innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - type TestMultiDimensionalStructsPtr2 struct { - Errs [][]*Inner `validate:"gt=0,dive,dive,len=2"` - } +// func AssertFieldError(t *testing.T, s *StructErrors, field string, expectedTag string) { - var errStructPtr2Array [][]*Inner +// val, ok := s.Errors[field] +// EqualSkip(t, 2, ok, true) +// NotEqualSkip(t, 2, val, nil) +// EqualSkip(t, 2, val.Field, field) +// EqualSkip(t, 2, val.Tag, expectedTag) +// } - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) +// func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, expectedTag string) { - tmsp2 := &TestMultiDimensionalStructsPtr2{ - Errs: errStructPtr2Array, - } +// val, ok := s[field] +// EqualSkip(t, 2, ok, true) +// NotEqualSkip(t, 2, val, nil) +// EqualSkip(t, 2, val.Field, field) +// EqualSkip(t, 2, val.Tag, expectedTag) +// } - errs = validate.Struct(tmsp2) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSliceStructError2.IsPlaceholderErr, false) - Equal(t, innerSliceStructError2.IsSliceOrArray, false) - Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) - Equal(t, innerSliceStructError2.Field, "Errs[2][2]") - - innerInnersliceError1 = innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - m := map[int]interface{}{0: "ok", 3: "", 4: "ok"} - - err = validate.Field(m, "len=3,dive,len=2") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, true) - Equal(t, err.IsMap, true) - Equal(t, len(err.MapErrs), 1) - - err = validate.Field(m, "len=2,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, false) - Equal(t, err.IsMap, false) - Equal(t, len(err.MapErrs), 0) - - arr := []interface{}{"ok", "", "ok"} - - err = validate.Field(arr, "len=3,dive,len=2") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, true) - Equal(t, err.IsSliceOrArray, true) - Equal(t, len(err.SliceOrArrayErrs), 1) - - err = validate.Field(arr, "len=2,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, false) - Equal(t, err.IsSliceOrArray, false) - Equal(t, len(err.SliceOrArrayErrs), 0) - - type MyStruct struct { - A, B string - C interface{} - } +// func TestBadKeyValidation(t *testing.T) { +// type Test struct { +// Name string `validate:"required, "` +// } - var a MyStruct +// tst := &Test{ +// Name: "test", +// } - a.A = "value" - a.C = "nu" +// PanicMatches(t, func() { validate.Struct(tst) }, "Invalid validation tag on field Name") +// } - errs = validate.Struct(a) - Equal(t, errs, nil) -} +// func TestFlattenValidation(t *testing.T) { -func TestMapDiveValidation(t *testing.T) { +// type Inner struct { +// Name string `validate:"required"` +// } - n := map[int]interface{}{0: nil} - err := validate.Field(n, "omitempty,required") +// type TestMultiDimensionalStructsPtr struct { +// Errs [][]*Inner `validate:"gt=0,dive,dive,required"` +// } - m := map[int]string{0: "ok", 3: "", 4: "ok"} +// var errStructPtrArray [][]*Inner - err = validate.Field(m, "len=3,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, true) - Equal(t, err.IsMap, true) - Equal(t, len(err.MapErrs), 1) +// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{"ok"}}) - err = validate.Field(m, "len=2,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, false) - Equal(t, err.IsMap, false) - Equal(t, len(err.MapErrs), 0) +// tmsp := &TestMultiDimensionalStructsPtr{ +// Errs: errStructPtrArray, +// } - type Inner struct { - Name string `validate:"required"` - } +// errs := validate.Struct(tmsp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) +// // for full test coverage +// fmt.Sprint(errs.Error()) - type TestMapStruct struct { - Errs map[int]Inner `validate:"gt=0,dive"` - } +// fieldErr := errs.Errors["Errs"] +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, fieldErr.Field, "Errs") +// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - mi := map[int]Inner{0: Inner{"ok"}, 3: Inner{""}, 4: Inner{"ok"}} +// innerSlice1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSlice1.IsPlaceholderErr, true) +// Equal(t, innerSlice1.Field, "Errs[0]") - ms := &TestMapStruct{ - Errs: mi, - } +// flatFieldErr, ok := fieldErr.Flatten()["[0][1].Inner.Name"] +// Equal(t, ok, true) +// Equal(t, flatFieldErr.Field, "Name") +// Equal(t, flatFieldErr.Tag, "required") - errs := validate.Struct(ms) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - // for full test coverage - fmt.Sprint(errs.Error()) - - fieldError := errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 1) - - structErr, ok := fieldError.MapErrs[3].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(structErr.Errors), 1) - - innerErr := structErr.Errors["Name"] - Equal(t, innerErr.IsPlaceholderErr, false) - Equal(t, innerErr.IsMap, false) - Equal(t, len(innerErr.MapErrs), 0) - Equal(t, innerErr.Field, "Name") - Equal(t, innerErr.Tag, "required") - - type TestMapTimeStruct struct { - Errs map[int]*time.Time `validate:"gt=0,dive,required"` - } +// structErrFlatten, ok := errs.Flatten()["Errs[0][1].Inner.Name"] +// Equal(t, ok, true) +// Equal(t, structErrFlatten.Field, "Name") +// Equal(t, structErrFlatten.Tag, "required") - t1 := time.Now().UTC() +// errStructPtrArray = [][]*Inner{} +// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, nil, &Inner{"ok"}}) - mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil} +// tmsp = &TestMultiDimensionalStructsPtr{ +// Errs: errStructPtrArray, +// } - mt := &TestMapTimeStruct{ - Errs: mta, - } +// errs = validate.Struct(tmsp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) +// // for full test coverage +// fmt.Sprint(errs.Error()) + +// fieldErr = errs.Errors["Errs"] +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, fieldErr.Field, "Errs") +// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) + +// innerSlice1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSlice1.IsPlaceholderErr, true) +// Equal(t, innerSlice1.Field, "Errs[0]") + +// flatFieldErr, ok = fieldErr.Flatten()["[0][1]"] +// Equal(t, ok, true) +// Equal(t, flatFieldErr.Field, "Errs[0][1]") +// Equal(t, flatFieldErr.Tag, "required") + +// type TestMapStructPtr struct { +// Errs map[int]*Inner `validate:"gt=0,dive,required"` +// } + +// mip := map[int]*Inner{0: &Inner{"ok"}, 3: &Inner{""}, 4: &Inner{"ok"}} + +// msp := &TestMapStructPtr{ +// Errs: mip, +// } + +// errs = validate.Struct(msp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldError := errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 1) + +// innerStructError, ok := fieldError.MapErrs[3].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, innerStructError.Struct, "Inner") +// Equal(t, len(innerStructError.Errors), 1) + +// innerInnerFieldError, ok := innerStructError.Errors["Name"] +// Equal(t, ok, true) +// Equal(t, innerInnerFieldError.IsPlaceholderErr, false) +// Equal(t, innerInnerFieldError.IsSliceOrArray, false) +// Equal(t, innerInnerFieldError.Field, "Name") +// Equal(t, innerInnerFieldError.Tag, "required") + +// flatErrs, ok := errs.Flatten()["Errs[3].Inner.Name"] +// Equal(t, ok, true) +// Equal(t, flatErrs.Field, "Name") +// Equal(t, flatErrs.Tag, "required") + +// mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + +// msp2 := &TestMapStructPtr{ +// Errs: mip2, +// } + +// errs = validate.Struct(msp2) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldError = errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 1) + +// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerFieldError.IsPlaceholderErr, false) +// Equal(t, innerFieldError.IsSliceOrArray, false) +// Equal(t, innerFieldError.Field, "Errs[3]") +// Equal(t, innerFieldError.Tag, "required") + +// flatErrs, ok = errs.Flatten()["Errs[3]"] +// Equal(t, ok, true) +// Equal(t, flatErrs.Field, "Errs[3]") +// Equal(t, flatErrs.Tag, "required") + +// type TestMapInnerArrayStruct struct { +// Errs map[int][]string `validate:"gt=0,dive,dive,required"` +// } + +// mias := map[int][]string{0: []string{"ok"}, 3: []string{"ok", ""}, 4: []string{"ok"}} + +// mia := &TestMapInnerArrayStruct{ +// Errs: mias, +// } + +// errs = validate.Struct(mia) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// flatErrs, ok = errs.Flatten()["Errs[3][1]"] +// Equal(t, ok, true) +// Equal(t, flatErrs.Field, "Errs[3][1]") +// Equal(t, flatErrs.Tag, "required") +// } + +// func TestInterfaceErrValidation(t *testing.T) { + +// var v1 interface{} +// var v2 interface{} + +// v2 = 1 +// v1 = v2 + +// err := validate.Field(v1, "len=1") +// Equal(t, err, nil) +// err = validate.Field(v2, "len=1") +// Equal(t, err, nil) + +// type ExternalCMD struct { +// Userid string `json:"userid"` +// Action uint32 `json:"action"` +// Data interface{} `json:"data,omitempty" validate:"required"` +// } + +// s := &ExternalCMD{ +// Userid: "123456", +// Action: 10000, +// // Data: 1, +// } + +// errs := validate.Struct(s) +// NotEqual(t, errs, nil) +// Equal(t, errs.Errors["Data"].Field, "Data") +// Equal(t, errs.Errors["Data"].Tag, "required") + +// type ExternalCMD2 struct { +// Userid string `json:"userid"` +// Action uint32 `json:"action"` +// Data interface{} `json:"data,omitempty" validate:"len=1"` +// } + +// s2 := &ExternalCMD2{ +// Userid: "123456", +// Action: 10000, +// // Data: 1, +// } + +// errs = validate.Struct(s2) +// NotEqual(t, errs, nil) +// Equal(t, errs.Errors["Data"].Field, "Data") +// Equal(t, errs.Errors["Data"].Tag, "len") +// Equal(t, errs.Errors["Data"].Param, "1") + +// s3 := &ExternalCMD2{ +// Userid: "123456", +// Action: 10000, +// Data: 2, +// } + +// errs = validate.Struct(s3) +// NotEqual(t, errs, nil) +// Equal(t, errs.Errors["Data"].Field, "Data") +// Equal(t, errs.Errors["Data"].Tag, "len") +// Equal(t, errs.Errors["Data"].Param, "1") + +// type Inner struct { +// Name string `validate:"required"` +// } + +// inner := &Inner{ +// Name: "", +// } + +// s4 := &ExternalCMD{ +// Userid: "123456", +// Action: 10000, +// Data: inner, +// } + +// errs = validate.Struct(s4) +// NotEqual(t, errs, nil) +// Equal(t, errs.StructErrors["Data"].Struct, "Inner") +// Equal(t, errs.StructErrors["Data"].Errors["Name"].Field, "Name") +// Equal(t, errs.StructErrors["Data"].Errors["Name"].Tag, "required") + +// type TestMapStructPtr struct { +// Errs map[int]interface{} `validate:"gt=0,dive,len=2"` +// } + +// mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + +// msp := &TestMapStructPtr{ +// Errs: mip, +// } + +// errs = validate.Struct(msp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldError := errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 1) + +// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerFieldError.IsPlaceholderErr, false) +// Equal(t, innerFieldError.IsMap, false) +// Equal(t, len(innerFieldError.MapErrs), 0) +// Equal(t, innerFieldError.Field, "Errs[3]") +// Equal(t, innerFieldError.Tag, "len") + +// type TestMultiDimensionalStructs struct { +// Errs [][]interface{} `validate:"gt=0,dive,dive,len=2"` +// } + +// var errStructArray [][]interface{} + +// errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + +// tms := &TestMultiDimensionalStructs{ +// Errs: errStructArray, +// } + +// errs = validate.Struct(tms) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok := errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) + +// sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerInnersliceError1 := innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// type TestMultiDimensionalStructsPtr2 struct { +// Errs [][]*Inner `validate:"gt=0,dive,dive,len=2"` +// } + +// var errStructPtr2Array [][]*Inner + +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + +// tmsp2 := &TestMultiDimensionalStructsPtr2{ +// Errs: errStructPtr2Array, +// } + +// errs = validate.Struct(tmsp2) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSliceStructError2.IsPlaceholderErr, false) +// Equal(t, innerSliceStructError2.IsSliceOrArray, false) +// Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) +// Equal(t, innerSliceStructError2.Field, "Errs[2][2]") + +// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// m := map[int]interface{}{0: "ok", 3: "", 4: "ok"} + +// err = validate.Field(m, "len=3,dive,len=2") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, true) +// Equal(t, err.IsMap, true) +// Equal(t, len(err.MapErrs), 1) + +// err = validate.Field(m, "len=2,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, false) +// Equal(t, err.IsMap, false) +// Equal(t, len(err.MapErrs), 0) + +// arr := []interface{}{"ok", "", "ok"} + +// err = validate.Field(arr, "len=3,dive,len=2") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, true) +// Equal(t, err.IsSliceOrArray, true) +// Equal(t, len(err.SliceOrArrayErrs), 1) + +// err = validate.Field(arr, "len=2,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, false) +// Equal(t, err.IsSliceOrArray, false) +// Equal(t, len(err.SliceOrArrayErrs), 0) + +// type MyStruct struct { +// A, B string +// C interface{} +// } + +// var a MyStruct + +// a.A = "value" +// a.C = "nu" + +// errs = validate.Struct(a) +// Equal(t, errs, nil) +// } + +// func TestMapDiveValidation(t *testing.T) { + +// n := map[int]interface{}{0: nil} +// err := validate.Field(n, "omitempty,required") + +// m := map[int]string{0: "ok", 3: "", 4: "ok"} + +// err = validate.Field(m, "len=3,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, true) +// Equal(t, err.IsMap, true) +// Equal(t, len(err.MapErrs), 1) + +// err = validate.Field(m, "len=2,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, false) +// Equal(t, err.IsMap, false) +// Equal(t, len(err.MapErrs), 0) + +// type Inner struct { +// Name string `validate:"required"` +// } + +// type TestMapStruct struct { +// Errs map[int]Inner `validate:"gt=0,dive"` +// } + +// mi := map[int]Inner{0: Inner{"ok"}, 3: Inner{""}, 4: Inner{"ok"}} + +// ms := &TestMapStruct{ +// Errs: mi, +// } + +// errs := validate.Struct(ms) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) +// // for full test coverage +// fmt.Sprint(errs.Error()) + +// fieldError := errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 1) + +// structErr, ok := fieldError.MapErrs[3].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(structErr.Errors), 1) + +// innerErr := structErr.Errors["Name"] +// Equal(t, innerErr.IsPlaceholderErr, false) +// Equal(t, innerErr.IsMap, false) +// Equal(t, len(innerErr.MapErrs), 0) +// Equal(t, innerErr.Field, "Name") +// Equal(t, innerErr.Tag, "required") + +// type TestMapTimeStruct struct { +// Errs map[int]*time.Time `validate:"gt=0,dive,required"` +// } + +// t1 := time.Now().UTC() + +// mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil} + +// mt := &TestMapTimeStruct{ +// Errs: mta, +// } + +// errs = validate.Struct(mt) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldError = errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 2) + +// innerErr, ok = fieldError.MapErrs[3].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerErr.IsPlaceholderErr, false) +// Equal(t, innerErr.IsMap, false) +// Equal(t, len(innerErr.MapErrs), 0) +// Equal(t, innerErr.Field, "Errs[3]") +// Equal(t, innerErr.Tag, "required") + +// type TestMapStructPtr struct { +// Errs map[int]*Inner `validate:"gt=0,dive,required"` +// } + +// mip := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + +// msp := &TestMapStructPtr{ +// Errs: mip, +// } + +// errs = validate.Struct(msp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldError = errs.Errors["Errs"] +// Equal(t, fieldError.IsPlaceholderErr, true) +// Equal(t, fieldError.IsMap, true) +// Equal(t, len(fieldError.MapErrs), 1) + +// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerFieldError.IsPlaceholderErr, false) +// Equal(t, innerFieldError.IsMap, false) +// Equal(t, len(innerFieldError.MapErrs), 0) +// Equal(t, innerFieldError.Field, "Errs[3]") +// Equal(t, innerFieldError.Tag, "required") + +// type TestMapStructPtr2 struct { +// Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"` +// } + +// mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + +// msp2 := &TestMapStructPtr2{ +// Errs: mip2, +// } + +// errs = validate.Struct(msp2) +// Equal(t, errs, nil) +// } + +// func TestArrayDiveValidation(t *testing.T) { + +// arr := []string{"ok", "", "ok"} + +// err := validate.Field(arr, "len=3,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, true) +// Equal(t, err.IsSliceOrArray, true) +// Equal(t, len(err.SliceOrArrayErrs), 1) + +// // flat := err.Flatten() +// // fe, ok := flat["[1]"] +// // Equal(t, ok, true) +// // Equal(t, fe.Tag, "required") + +// err = validate.Field(arr, "len=2,dive,required") +// NotEqual(t, err, nil) +// Equal(t, err.IsPlaceholderErr, false) +// Equal(t, err.IsSliceOrArray, false) +// Equal(t, len(err.SliceOrArrayErrs), 0) + +// type BadDive struct { +// Name string `validate:"dive"` +// } + +// bd := &BadDive{ +// Name: "TEST", +// } + +// PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") + +// type Test struct { +// Errs []string `validate:"gt=0,dive,required"` +// } + +// test := &Test{ +// Errs: []string{"ok", "", "ok"}, +// } + +// errs := validate.Struct(test) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// // flat = errs.Flatten() +// // me, ok := flat["Errs[1]"] +// // Equal(t, ok, true) +// // Equal(t, me.Field, "Errs[1]") +// // Equal(t, me.Tag, "required") + +// fieldErr, ok := errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) + +// innerErr, ok := fieldErr.SliceOrArrayErrs[1].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerErr.Tag, required) +// Equal(t, innerErr.IsPlaceholderErr, false) +// Equal(t, innerErr.Field, "Errs[1]") + +// test = &Test{ +// Errs: []string{"ok", "ok", ""}, +// } + +// errs = validate.Struct(test) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) + +// innerErr, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerErr.Tag, required) +// Equal(t, innerErr.IsPlaceholderErr, false) +// Equal(t, innerErr.Field, "Errs[2]") + +// type TestMultiDimensional struct { +// Errs [][]string `validate:"gt=0,dive,dive,required"` +// } + +// var errArray [][]string + +// errArray = append(errArray, []string{"ok", "", ""}) +// errArray = append(errArray, []string{"ok", "", ""}) + +// tm := &TestMultiDimensional{ +// Errs: errArray, +// } + +// errs = validate.Struct(tm) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) + +// sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) +// Equal(t, sliceError1.Field, "Errs[0]") + +// innerSliceError1, ok := sliceError1.SliceOrArrayErrs[1].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSliceError1.IsPlaceholderErr, false) +// Equal(t, innerSliceError1.Tag, required) +// Equal(t, innerSliceError1.IsSliceOrArray, false) +// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) +// Equal(t, innerSliceError1.Field, "Errs[0][1]") + +// type Inner struct { +// Name string `validate:"required"` +// } + +// type TestMultiDimensionalStructs struct { +// Errs [][]Inner `validate:"gt=0,dive,dive"` +// } + +// var errStructArray [][]Inner + +// errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) +// errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) + +// tms := &TestMultiDimensionalStructs{ +// Errs: errStructArray, +// } + +// errs = validate.Struct(tms) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerInnersliceError1 := innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// type TestMultiDimensionalStructsPtr struct { +// Errs [][]*Inner `validate:"gt=0,dive,dive"` +// } + +// var errStructPtrArray [][]*Inner + +// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + +// tmsp := &TestMultiDimensionalStructsPtr{ +// Errs: errStructPtrArray, +// } + +// errs = validate.Struct(tmsp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) +// // for full test coverage +// fmt.Sprint(errs.Error()) + +// // flat := errs.Flatten() +// // // fmt.Println(errs) +// // fmt.Println(flat) +// // expect Errs[0][1].Inner.Name +// // me, ok := flat["Errs[1]"] +// // Equal(t, ok, true) +// // Equal(t, me.Field, "Errs[1]") +// // Equal(t, me.Tag, "required") + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) + +// // flat := fieldErr.Flatten() +// // fmt.Println(errs) +// // fmt.Println(flat) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// type TestMultiDimensionalStructsPtr2 struct { +// Errs [][]*Inner `validate:"gt=0,dive,dive,required"` +// } + +// var errStructPtr2Array [][]*Inner + +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + +// tmsp2 := &TestMultiDimensionalStructsPtr2{ +// Errs: errStructPtr2Array, +// } + +// errs = validate.Struct(tmsp2) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSliceStructError2.IsPlaceholderErr, false) +// Equal(t, innerSliceStructError2.IsSliceOrArray, false) +// Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) +// Equal(t, innerSliceStructError2.Field, "Errs[2][2]") + +// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// type TestMultiDimensionalStructsPtr3 struct { +// Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"` +// } + +// var errStructPtr3Array [][]*Inner + +// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) +// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + +// tmsp3 := &TestMultiDimensionalStructsPtr3{ +// Errs: errStructPtr3Array, +// } + +// errs = validate.Struct(tmsp3) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) +// Equal(t, ok, true) +// Equal(t, len(innerSliceStructError1.Errors), 1) + +// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] +// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) +// Equal(t, innerInnersliceError1.IsSliceOrArray, false) +// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) + +// type TestMultiDimensionalTimeTime struct { +// Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` +// } + +// var errTimePtr3Array [][]*time.Time + +// t1 := time.Now().UTC() +// t2 := time.Now().UTC() +// t3 := time.Now().UTC().Add(time.Hour * 24) + +// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3}) +// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil}) +// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil}) + +// tmtp3 := &TestMultiDimensionalTimeTime{ +// Errs: errTimePtr3Array, +// } + +// errs = validate.Struct(tmtp3) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSliceError1.IsPlaceholderErr, false) +// Equal(t, innerSliceError1.IsSliceOrArray, false) +// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) +// Equal(t, innerSliceError1.Field, "Errs[2][1]") +// Equal(t, innerSliceError1.Tag, required) + +// type TestMultiDimensionalTimeTime2 struct { +// Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` +// } + +// var errTimeArray [][]*time.Time + +// t1 = time.Now().UTC() +// t2 = time.Now().UTC() +// t3 = time.Now().UTC().Add(time.Hour * 24) + +// errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3}) +// errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil}) +// errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil}) + +// tmtp := &TestMultiDimensionalTimeTime2{ +// Errs: errTimeArray, +// } + +// errs = validate.Struct(tmtp) +// NotEqual(t, errs, nil) +// Equal(t, len(errs.Errors), 1) + +// fieldErr, ok = errs.Errors["Errs"] +// Equal(t, ok, true) +// Equal(t, fieldErr.IsPlaceholderErr, true) +// Equal(t, fieldErr.IsSliceOrArray, true) +// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) + +// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) +// Equal(t, ok, true) +// Equal(t, sliceError1.IsPlaceholderErr, true) +// Equal(t, sliceError1.IsSliceOrArray, true) +// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) + +// innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) +// Equal(t, ok, true) +// Equal(t, innerSliceError1.IsPlaceholderErr, false) +// Equal(t, innerSliceError1.IsSliceOrArray, false) +// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) +// Equal(t, innerSliceError1.Field, "Errs[2][1]") +// Equal(t, innerSliceError1.Tag, required) +// } + +// func TestNilStructPointerValidation(t *testing.T) { +// type Inner struct { +// Data string +// } + +// type Outer struct { +// Inner *Inner `validate:"omitempty"` +// } + +// inner := &Inner{ +// Data: "test", +// } + +// outer := &Outer{ +// Inner: inner, +// } + +// errs := validate.Struct(outer) +// Equal(t, errs, nil) + +// outer = &Outer{ +// Inner: nil, +// } + +// errs = validate.Struct(outer) +// Equal(t, errs, nil) + +// type Inner2 struct { +// Data string +// } + +// type Outer2 struct { +// Inner2 *Inner2 `validate:"required"` +// } + +// inner2 := &Inner2{ +// Data: "test", +// } + +// outer2 := &Outer2{ +// Inner2: inner2, +// } + +// errs = validate.Struct(outer2) +// Equal(t, errs, nil) + +// outer2 = &Outer2{ +// Inner2: nil, +// } + +// errs = validate.Struct(outer2) +// NotEqual(t, errs, nil) + +// type Inner3 struct { +// Data string +// } + +// type Outer3 struct { +// Inner3 *Inner3 +// } + +// inner3 := &Inner3{ +// Data: "test", +// } + +// outer3 := &Outer3{ +// Inner3: inner3, +// } + +// errs = validate.Struct(outer3) +// Equal(t, errs, nil) + +// type Inner4 struct { +// Data string +// } + +// type Outer4 struct { +// Inner4 *Inner4 `validate:"-"` +// } + +// inner4 := &Inner4{ +// Data: "test", +// } + +// outer4 := &Outer4{ +// Inner4: inner4, +// } + +// errs = validate.Struct(outer4) +// Equal(t, errs, nil) +// } + +// func TestSSNValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"00-90-8787", false}, +// {"66690-76", false}, +// {"191 60 2869", true}, +// {"191-60-2869", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "ssn") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d SSN failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ssn") { +// t.Fatalf("Index: %d SSN failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestLongitudeValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"-180.000", true}, +// {"180.1", false}, +// {"+73.234", true}, +// {"+382.3811", false}, +// {"23.11111111", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "longitude") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d Longitude failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "longitude") { +// t.Fatalf("Index: %d Longitude failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestLatitudeValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"-90.000", true}, +// {"+90", true}, +// {"47.1231231", true}, +// {"+99.9", false}, +// {"108", false}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "latitude") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d Latitude failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "latitude") { +// t.Fatalf("Index: %d Latitude failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestDataURIValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, +// {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, +// {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, +// {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + +// "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + +// "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + +// "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + +// "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + +// "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, +// {"data:image/png;base64,12345", false}, +// {"", false}, +// {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "datauri") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d DataURI failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "datauri") { +// t.Fatalf("Index: %d DataURI failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestMultibyteValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", true}, +// {"abc", false}, +// {"123", false}, +// {"<>@;.-=", false}, +// {"ひらがな・カタカナ、.漢字", true}, +// {"あいうえお foobar", true}, +// {"test@example.com", true}, +// {"test@example.com", true}, +// {"1234abcDExyz", true}, +// {"カタカナ", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "multibyte") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "multibyte") { +// t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestPrintableASCIIValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", true}, +// {"foobar", false}, +// {"xyz098", false}, +// {"123456", false}, +// {"カタカナ", false}, +// {"foobar", true}, +// {"0987654321", true}, +// {"test@example.com", true}, +// {"1234abcDEF", true}, +// {"newline\n", false}, +// {"\x19test\x7F", false}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "printascii") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "printascii") { +// t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestASCIIValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", true}, +// {"foobar", false}, +// {"xyz098", false}, +// {"123456", false}, +// {"カタカナ", false}, +// {"foobar", true}, +// {"0987654321", true}, +// {"test@example.com", true}, +// {"1234abcDEF", true}, +// {"", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "ascii") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d ASCII failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ascii") { +// t.Fatalf("Index: %d ASCII failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestUUID5Validation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ + +// {"", false}, +// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, +// {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, +// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, +// {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, +// {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "uuid5") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid5") { +// t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestUUID4Validation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, +// {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, +// {"934859", false}, +// {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, +// {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "uuid4") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid4") { +// t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestUUID3Validation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"412452646", false}, +// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, +// {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, +// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "uuid3") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid3") { +// t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestUUIDValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, +// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, +// {"a987fbc94bed3078cf079141ba07c9f3", false}, +// {"934859", false}, +// {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, +// {"aaaaaaaa-1111-1111-aaag-111111111111", false}, +// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "uuid") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d UUID failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid") { +// t.Fatalf("Index: %d UUID failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestISBNValidation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"foo", false}, +// {"3836221195", true}, +// {"1-61729-085-8", true}, +// {"3 423 21412 0", true}, +// {"3 401 01319 X", true}, +// {"9784873113685", true}, +// {"978-4-87311-368-5", true}, +// {"978 3401013190", true}, +// {"978-3-8362-2119-1", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "isbn") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d ISBN failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") { +// t.Fatalf("Index: %d ISBN failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestISBN13Validation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"foo", false}, +// {"3-8362-2119-5", false}, +// {"01234567890ab", false}, +// {"978 3 8362 2119 0", false}, +// {"9784873113685", true}, +// {"978-4-87311-368-5", true}, +// {"978 3401013190", true}, +// {"978-3-8362-2119-1", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "isbn13") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") { +// t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestISBN10Validation(t *testing.T) { +// tests := []struct { +// param string +// expected bool +// }{ +// {"", false}, +// {"foo", false}, +// {"3423214121", false}, +// {"978-3836221191", false}, +// {"3-423-21412-1", false}, +// {"3 423 21412 1", false}, +// {"3836221195", true}, +// {"1-61729-085-8", true}, +// {"3 423 21412 0", true}, +// {"3 401 01319 X", true}, +// } + +// for i, test := range tests { + +// err := validate.Field(test.param, "isbn10") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") { +// t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) +// } +// } +// } +// } + +// func TestExcludesRuneValidation(t *testing.T) { + +// tests := []struct { +// Value string `validate:"excludesrune=☻"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, +// {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, +// } + +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) + +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } + +// errs := validate.Struct(s) + +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } +// } + +// func TestExcludesAllValidation(t *testing.T) { + +// tests := []struct { +// Value string `validate:"excludesall=@!{}[]"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, +// {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, +// } + +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) + +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } + +// errs := validate.Struct(s) + +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } + +// username := "joeybloggs " + +// err := validate.Field(username, "excludesall=@ ") +// NotEqual(t, err, nil) + +// excluded := "," + +// err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") +// NotEqual(t, err, nil) + +// excluded = "=" + +// err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") +// NotEqual(t, err, nil) +// } + +// func TestExcludesValidation(t *testing.T) { + +// tests := []struct { +// Value string `validate:"excludes=@"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, +// {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, +// } + +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) + +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } + +// errs := validate.Struct(s) + +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } +// } + +// func TestContainsRuneValidation(t *testing.T) { + +// tests := []struct { +// Value string `validate:"containsrune=☻"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, +// {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, +// } + +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) + +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } - errs = validate.Struct(mt) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldError = errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 2) - - innerErr, ok = fieldError.MapErrs[3].(*FieldError) - Equal(t, ok, true) - Equal(t, innerErr.IsPlaceholderErr, false) - Equal(t, innerErr.IsMap, false) - Equal(t, len(innerErr.MapErrs), 0) - Equal(t, innerErr.Field, "Errs[3]") - Equal(t, innerErr.Tag, "required") - - type TestMapStructPtr struct { - Errs map[int]*Inner `validate:"gt=0,dive,required"` - } +// errs := validate.Struct(s) - mip := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } +// } + +// func TestContainsAnyValidation(t *testing.T) { + +// tests := []struct { +// Value string `validate:"containsany=@!{}[]"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, +// {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, +// } + +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) - msp := &TestMapStructPtr{ - Errs: mip, - } +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } - errs = validate.Struct(msp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldError = errs.Errors["Errs"] - Equal(t, fieldError.IsPlaceholderErr, true) - Equal(t, fieldError.IsMap, true) - Equal(t, len(fieldError.MapErrs), 1) - - innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) - Equal(t, ok, true) - Equal(t, innerFieldError.IsPlaceholderErr, false) - Equal(t, innerFieldError.IsMap, false) - Equal(t, len(innerFieldError.MapErrs), 0) - Equal(t, innerFieldError.Field, "Errs[3]") - Equal(t, innerFieldError.Tag, "required") - - type TestMapStructPtr2 struct { - Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"` - } +// errs := validate.Struct(s) - mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } +// } - msp2 := &TestMapStructPtr2{ - Errs: mip2, - } +// func TestContainsValidation(t *testing.T) { - errs = validate.Struct(msp2) - Equal(t, errs, nil) -} +// tests := []struct { +// Value string `validate:"contains=@"` +// Tag string +// ExpectedNil bool +// }{ +// {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, +// {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, +// } -func TestArrayDiveValidation(t *testing.T) { +// for i, s := range tests { +// err := validate.Field(s.Value, s.Tag) + +// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, err) +// } + +// errs := validate.Struct(s) + +// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { +// t.Fatalf("Index: %d failed Error: %s", i, errs) +// } +// } +// } + +// func TestIsNeFieldValidation(t *testing.T) { + +// var j uint64 +// var k float64 +// s := "abcd" +// i := 1 +// j = 1 +// k = 1.543 +// arr := []string{"test"} +// now := time.Now().UTC() + +// var j2 uint64 +// var k2 float64 +// s2 := "abcdef" +// i2 := 3 +// j2 = 2 +// k2 = 1.5434456 +// arr2 := []string{"test", "test2"} +// arr3 := []string{"test"} +// now2 := now - arr := []string{"ok", "", "ok"} +// err := validate.FieldWithValue(s, s2, "nefield") +// Equal(t, err, nil) + +// err = validate.FieldWithValue(i2, i, "nefield") +// Equal(t, err, nil) + +// err = validate.FieldWithValue(j2, j, "nefield") +// Equal(t, err, nil) + +// err = validate.FieldWithValue(k2, k, "nefield") +// Equal(t, err, nil) + +// err = validate.FieldWithValue(arr2, arr, "nefield") +// Equal(t, err, nil) + +// err = validate.FieldWithValue(now2, now, "nefield") +// NotEqual(t, err, nil) + +// err = validate.FieldWithValue(arr3, arr, "nefield") +// NotEqual(t, err, nil) + +// type Test struct { +// Start *time.Time `validate:"nefield=End"` +// End *time.Time +// } + +// sv := &Test{ +// Start: &now, +// End: &now, +// } + +// errs := validate.Struct(sv) +// NotEqual(t, errs, nil) - err := validate.Field(arr, "len=3,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, true) - Equal(t, err.IsSliceOrArray, true) - Equal(t, len(err.SliceOrArrayErrs), 1) +// now3 := time.Now().UTC() - // flat := err.Flatten() - // fe, ok := flat["[1]"] - // Equal(t, ok, true) - // Equal(t, fe.Tag, "required") +// sv = &Test{ +// Start: &now, +// End: &now3, +// } - err = validate.Field(arr, "len=2,dive,required") - NotEqual(t, err, nil) - Equal(t, err.IsPlaceholderErr, false) - Equal(t, err.IsSliceOrArray, false) - Equal(t, len(err.SliceOrArrayErrs), 0) +// errs = validate.Struct(sv) +// Equal(t, errs, nil) - type BadDive struct { - Name string `validate:"dive"` - } +// channel := make(chan string) + +// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "nefield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(5, channel, "nefield") }, "Bad field type chan string") +// PanicMatches(t, func() { validate.FieldWithValue(5, now, "nefield") }, "Bad Top Level field type") - bd := &BadDive{ - Name: "TEST", - } +// type Test2 struct { +// Start *time.Time `validate:"nefield=NonExistantField"` +// End *time.Time +// } - PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") +// sv2 := &Test2{ +// Start: &now, +// End: &now, +// } - type Test struct { - Errs []string `validate:"gt=0,dive,required"` - } +// PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +// } - test := &Test{ - Errs: []string{"ok", "", "ok"}, - } +// func TestIsNeValidation(t *testing.T) { - errs := validate.Struct(test) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - // flat = errs.Flatten() - // me, ok := flat["Errs[1]"] - // Equal(t, ok, true) - // Equal(t, me.Field, "Errs[1]") - // Equal(t, me.Tag, "required") - - fieldErr, ok := errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - - innerErr, ok := fieldErr.SliceOrArrayErrs[1].(*FieldError) - Equal(t, ok, true) - Equal(t, innerErr.Tag, required) - Equal(t, innerErr.IsPlaceholderErr, false) - Equal(t, innerErr.Field, "Errs[1]") - - test = &Test{ - Errs: []string{"ok", "ok", ""}, - } +// var j uint64 +// var k float64 +// s := "abcdef" +// i := 3 +// j = 2 +// k = 1.5434 +// arr := []string{"test"} +// now := time.Now().UTC() - errs = validate.Struct(test) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) +// err := validate.Field(s, "ne=abcd") +// Equal(t, err, nil) - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 1) +// err = validate.Field(i, "ne=1") +// Equal(t, err, nil) - innerErr, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, innerErr.Tag, required) - Equal(t, innerErr.IsPlaceholderErr, false) - Equal(t, innerErr.Field, "Errs[2]") +// err = validate.Field(j, "ne=1") +// Equal(t, err, nil) - type TestMultiDimensional struct { - Errs [][]string `validate:"gt=0,dive,dive,required"` - } +// err = validate.Field(k, "ne=1.543") +// Equal(t, err, nil) - var errArray [][]string +// err = validate.Field(arr, "ne=2") +// Equal(t, err, nil) - errArray = append(errArray, []string{"ok", "", ""}) - errArray = append(errArray, []string{"ok", "", ""}) +// err = validate.Field(arr, "ne=1") +// NotEqual(t, err, nil) - tm := &TestMultiDimensional{ - Errs: errArray, - } +// PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") +// } - errs = validate.Struct(tm) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - - sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - Equal(t, sliceError1.Field, "Errs[0]") - - innerSliceError1, ok := sliceError1.SliceOrArrayErrs[1].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSliceError1.IsPlaceholderErr, false) - Equal(t, innerSliceError1.Tag, required) - Equal(t, innerSliceError1.IsSliceOrArray, false) - Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) - Equal(t, innerSliceError1.Field, "Errs[0][1]") - - type Inner struct { - Name string `validate:"required"` - } +// func TestIsEqFieldValidation(t *testing.T) { - type TestMultiDimensionalStructs struct { - Errs [][]Inner `validate:"gt=0,dive,dive"` - } +// var j uint64 +// var k float64 +// s := "abcd" +// i := 1 +// j = 1 +// k = 1.543 +// arr := []string{"test"} +// now := time.Now().UTC() - var errStructArray [][]Inner +// var j2 uint64 +// var k2 float64 +// s2 := "abcd" +// i2 := 1 +// j2 = 1 +// k2 = 1.543 +// arr2 := []string{"test"} +// arr3 := []string{"test", "test2"} +// now2 := now - errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) - errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) +// err := validate.FieldWithValue(s, s2, "eqfield") +// Equal(t, err, nil) - tms := &TestMultiDimensionalStructs{ - Errs: errStructArray, - } +// err = validate.FieldWithValue(i2, i, "eqfield") +// Equal(t, err, nil) - errs = validate.Struct(tms) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerInnersliceError1 := innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - type TestMultiDimensionalStructsPtr struct { - Errs [][]*Inner `validate:"gt=0,dive,dive"` - } +// err = validate.FieldWithValue(j2, j, "eqfield") +// Equal(t, err, nil) - var errStructPtrArray [][]*Inner +// err = validate.FieldWithValue(k2, k, "eqfield") +// Equal(t, err, nil) - errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) +// err = validate.FieldWithValue(arr2, arr, "eqfield") +// Equal(t, err, nil) - tmsp := &TestMultiDimensionalStructsPtr{ - Errs: errStructPtrArray, - } +// err = validate.FieldWithValue(now2, now, "eqfield") +// Equal(t, err, nil) - errs = validate.Struct(tmsp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - // for full test coverage - fmt.Sprint(errs.Error()) - - // flat := errs.Flatten() - // // fmt.Println(errs) - // fmt.Println(flat) - // expect Errs[0][1].Inner.Name - // me, ok := flat["Errs[1]"] - // Equal(t, ok, true) - // Equal(t, me.Field, "Errs[1]") - // Equal(t, me.Tag, "required") - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - - // flat := fieldErr.Flatten() - // fmt.Println(errs) - // fmt.Println(flat) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerInnersliceError1 = innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - type TestMultiDimensionalStructsPtr2 struct { - Errs [][]*Inner `validate:"gt=0,dive,dive,required"` - } +// err = validate.FieldWithValue(arr3, arr, "eqfield") +// NotEqual(t, err, nil) - var errStructPtr2Array [][]*Inner +// type Test struct { +// Start *time.Time `validate:"eqfield=End"` +// End *time.Time +// } - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) +// sv := &Test{ +// Start: &now, +// End: &now, +// } - tmsp2 := &TestMultiDimensionalStructsPtr2{ - Errs: errStructPtr2Array, - } +// errs := validate.Struct(sv) +// Equal(t, errs, nil) - errs = validate.Struct(tmsp2) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSliceStructError2.IsPlaceholderErr, false) - Equal(t, innerSliceStructError2.IsSliceOrArray, false) - Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) - Equal(t, innerSliceStructError2.Field, "Errs[2][2]") - - innerInnersliceError1 = innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - type TestMultiDimensionalStructsPtr3 struct { - Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"` - } +// now3 := time.Now().UTC() - var errStructPtr3Array [][]*Inner +// sv = &Test{ +// Start: &now, +// End: &now3, +// } - errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) +// errs = validate.Struct(sv) +// NotEqual(t, errs, nil) - tmsp3 := &TestMultiDimensionalStructsPtr3{ - Errs: errStructPtr3Array, - } +// channel := make(chan string) - errs = validate.Struct(tmsp3) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) - Equal(t, ok, true) - Equal(t, len(innerSliceStructError1.Errors), 1) - - innerInnersliceError1 = innerSliceStructError1.Errors["Name"] - Equal(t, innerInnersliceError1.IsPlaceholderErr, false) - Equal(t, innerInnersliceError1.IsSliceOrArray, false) - Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - - type TestMultiDimensionalTimeTime struct { - Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` - } +// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "eqfield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(5, channel, "eqfield") }, "Bad field type chan string") +// PanicMatches(t, func() { validate.FieldWithValue(5, now, "eqfield") }, "Bad Top Level field type") - var errTimePtr3Array [][]*time.Time +// type Test2 struct { +// Start *time.Time `validate:"eqfield=NonExistantField"` +// End *time.Time +// } - t1 := time.Now().UTC() - t2 := time.Now().UTC() - t3 := time.Now().UTC().Add(time.Hour * 24) +// sv2 := &Test2{ +// Start: &now, +// End: &now, +// } - errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3}) - errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil}) - errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil}) +// PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +// } - tmtp3 := &TestMultiDimensionalTimeTime{ - Errs: errTimePtr3Array, - } +// func TestIsEqValidation(t *testing.T) { - errs = validate.Struct(tmtp3) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSliceError1.IsPlaceholderErr, false) - Equal(t, innerSliceError1.IsSliceOrArray, false) - Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) - Equal(t, innerSliceError1.Field, "Errs[2][1]") - Equal(t, innerSliceError1.Tag, required) - - type TestMultiDimensionalTimeTime2 struct { - Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` - } +// var j uint64 +// var k float64 +// s := "abcd" +// i := 1 +// j = 1 +// k = 1.543 +// arr := []string{"test"} +// now := time.Now().UTC() - var errTimeArray [][]*time.Time +// err := validate.Field(s, "eq=abcd") +// Equal(t, err, nil) - t1 = time.Now().UTC() - t2 = time.Now().UTC() - t3 = time.Now().UTC().Add(time.Hour * 24) +// err = validate.Field(i, "eq=1") +// Equal(t, err, nil) - errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3}) - errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil}) - errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil}) +// err = validate.Field(j, "eq=1") +// Equal(t, err, nil) - tmtp := &TestMultiDimensionalTimeTime2{ - Errs: errTimeArray, - } +// err = validate.Field(k, "eq=1.543") +// Equal(t, err, nil) - errs = validate.Struct(tmtp) - NotEqual(t, errs, nil) - Equal(t, len(errs.Errors), 1) - - fieldErr, ok = errs.Errors["Errs"] - Equal(t, ok, true) - Equal(t, fieldErr.IsPlaceholderErr, true) - Equal(t, fieldErr.IsSliceOrArray, true) - Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - - sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) - Equal(t, ok, true) - Equal(t, sliceError1.IsPlaceholderErr, true) - Equal(t, sliceError1.IsSliceOrArray, true) - Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - - innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) - Equal(t, ok, true) - Equal(t, innerSliceError1.IsPlaceholderErr, false) - Equal(t, innerSliceError1.IsSliceOrArray, false) - Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) - Equal(t, innerSliceError1.Field, "Errs[2][1]") - Equal(t, innerSliceError1.Tag, required) -} +// err = validate.Field(arr, "eq=1") +// Equal(t, err, nil) -func TestNilStructPointerValidation(t *testing.T) { - type Inner struct { - Data string - } +// err = validate.Field(arr, "eq=2") +// NotEqual(t, err, nil) - type Outer struct { - Inner *Inner `validate:"omitempty"` - } +// PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") +// } - inner := &Inner{ - Data: "test", - } +// func TestBase64Validation(t *testing.T) { - outer := &Outer{ - Inner: inner, - } +// s := "dW5pY29ybg==" - errs := validate.Struct(outer) - Equal(t, errs, nil) +// err := validate.Field(s, "base64") +// Equal(t, err, nil) - outer = &Outer{ - Inner: nil, - } +// s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" +// err = validate.Field(s, "base64") +// Equal(t, err, nil) - errs = validate.Struct(outer) - Equal(t, errs, nil) +// s = "" +// err = validate.Field(s, "base64") +// NotEqual(t, err, nil) - type Inner2 struct { - Data string - } +// s = "dW5pY29ybg== foo bar" +// err = validate.Field(s, "base64") +// NotEqual(t, err, nil) +// } - type Outer2 struct { - Inner2 *Inner2 `validate:"required"` - } +// func TestStructOnlyValidation(t *testing.T) { - inner2 := &Inner2{ - Data: "test", - } +// type Inner struct { +// Test string `validate:"len=5"` +// } - outer2 := &Outer2{ - Inner2: inner2, - } +// type Outer struct { +// InnerStruct *Inner `validate:"required,structonly"` +// } - errs = validate.Struct(outer2) - Equal(t, errs, nil) +// outer := &Outer{ +// InnerStruct: nil, +// } - outer2 = &Outer2{ - Inner2: nil, - } +// errs := validate.Struct(outer) +// NotEqual(t, errs, nil) - errs = validate.Struct(outer2) - NotEqual(t, errs, nil) +// inner := &Inner{ +// Test: "1234", +// } - type Inner3 struct { - Data string - } +// outer = &Outer{ +// InnerStruct: inner, +// } - type Outer3 struct { - Inner3 *Inner3 - } +// errs = validate.Struct(outer) +// Equal(t, errs, nil) +// } - inner3 := &Inner3{ - Data: "test", - } +// func TestGtField(t *testing.T) { - outer3 := &Outer3{ - Inner3: inner3, - } +// type TimeTest struct { +// Start *time.Time `validate:"required,gt"` +// End *time.Time `validate:"required,gt,gtfield=Start"` +// } - errs = validate.Struct(outer3) - Equal(t, errs, nil) +// now := time.Now() +// start := now.Add(time.Hour * 24) +// end := start.Add(time.Hour * 24) - type Inner4 struct { - Data string - } +// timeTest := &TimeTest{ +// Start: &start, +// End: &end, +// } - type Outer4 struct { - Inner4 *Inner4 `validate:"-"` - } +// errs := validate.Struct(timeTest) +// Equal(t, errs, nil) - inner4 := &Inner4{ - Data: "test", - } +// timeTest = &TimeTest{ +// Start: &end, +// End: &start, +// } - outer4 := &Outer4{ - Inner4: inner4, - } +// errs2 := validate.Struct(timeTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "End", "gtfield") - errs = validate.Struct(outer4) - Equal(t, errs, nil) -} +// err3 := validate.FieldWithValue(&start, &end, "gtfield") +// Equal(t, err3, nil) -func TestSSNValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"00-90-8787", false}, - {"66690-76", false}, - {"191 60 2869", true}, - {"191-60-2869", true}, - } +// err3 = validate.FieldWithValue(&end, &start, "gtfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtfield") - for i, test := range tests { +// type IntTest struct { +// Val1 int `validate:"required"` +// Val2 int `validate:"required,gtfield=Val1"` +// } - err := validate.Field(test.param, "ssn") +// intTest := &IntTest{ +// Val1: 1, +// Val2: 5, +// } - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d SSN failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ssn") { - t.Fatalf("Index: %d SSN failed Error: %s", i, err) - } - } - } -} +// errs = validate.Struct(intTest) +// Equal(t, errs, nil) -func TestLongitudeValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"-180.000", true}, - {"180.1", false}, - {"+73.234", true}, - {"+382.3811", false}, - {"23.11111111", true}, - } +// intTest = &IntTest{ +// Val1: 5, +// Val2: 1, +// } - for i, test := range tests { +// errs2 = validate.Struct(intTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtfield") - err := validate.Field(test.param, "longitude") +// err3 = validate.FieldWithValue(int(1), int(5), "gtfield") +// Equal(t, err3, nil) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d Longitude failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "longitude") { - t.Fatalf("Index: %d Longitude failed Error: %s", i, err) - } - } - } -} +// err3 = validate.FieldWithValue(int(5), int(1), "gtfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtfield") -func TestLatitudeValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"-90.000", true}, - {"+90", true}, - {"47.1231231", true}, - {"+99.9", false}, - {"108", false}, - } +// type UIntTest struct { +// Val1 uint `validate:"required"` +// Val2 uint `validate:"required,gtfield=Val1"` +// } - for i, test := range tests { +// uIntTest := &UIntTest{ +// Val1: 1, +// Val2: 5, +// } - err := validate.Field(test.param, "latitude") +// errs = validate.Struct(uIntTest) +// Equal(t, errs, nil) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d Latitude failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "latitude") { - t.Fatalf("Index: %d Latitude failed Error: %s", i, err) - } - } - } -} +// uIntTest = &UIntTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs2 = validate.Struct(uIntTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtfield") + +// err3 = validate.FieldWithValue(uint(1), uint(5), "gtfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(uint(5), uint(1), "gtfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtfield") + +// type FloatTest struct { +// Val1 float64 `validate:"required"` +// Val2 float64 `validate:"required,gtfield=Val1"` +// } + +// floatTest := &FloatTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs = validate.Struct(floatTest) +// Equal(t, errs, nil) + +// floatTest = &FloatTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs2 = validate.Struct(floatTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtfield") + +// err3 = validate.FieldWithValue(float32(1), float32(5), "gtfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(float32(5), float32(1), "gtfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtfield") + +// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtfield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtfield") }, "Bad field type string") +// PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtfield") }, "Bad Top Level field type") + +// type TimeTest2 struct { +// Start *time.Time `validate:"required"` +// End *time.Time `validate:"required,gtfield=NonExistantField"` +// } + +// timeTest2 := &TimeTest2{ +// Start: &start, +// End: &end, +// } + +// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +// } + +// func TestLtField(t *testing.T) { + +// type TimeTest struct { +// Start *time.Time `validate:"required,lt,ltfield=End"` +// End *time.Time `validate:"required,lt"` +// } + +// now := time.Now() +// start := now.Add(time.Hour * 24 * -1 * 2) +// end := start.Add(time.Hour * 24) + +// timeTest := &TimeTest{ +// Start: &start, +// End: &end, +// } + +// errs := validate.Struct(timeTest) +// Equal(t, errs, nil) + +// timeTest = &TimeTest{ +// Start: &end, +// End: &start, +// } + +// errs2 := validate.Struct(timeTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Start", "ltfield") + +// err3 := validate.FieldWithValue(&end, &start, "ltfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(&start, &end, "ltfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltfield") + +// type IntTest struct { +// Val1 int `validate:"required"` +// Val2 int `validate:"required,ltfield=Val1"` +// } + +// intTest := &IntTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs = validate.Struct(intTest) +// Equal(t, errs, nil) + +// intTest = &IntTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(intTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltfield") + +// err3 = validate.FieldWithValue(int(5), int(1), "ltfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(int(1), int(5), "ltfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltfield") -func TestDataURIValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, - {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, - {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, - {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + - "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + - "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + - "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + - "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + - "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, - {"data:image/png;base64,12345", false}, - {"", false}, - {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, - } +// type UIntTest struct { +// Val1 uint `validate:"required"` +// Val2 uint `validate:"required,ltfield=Val1"` +// } + +// uIntTest := &UIntTest{ +// Val1: 5, +// Val2: 1, +// } - for i, test := range tests { +// errs = validate.Struct(uIntTest) +// Equal(t, errs, nil) + +// uIntTest = &UIntTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(uIntTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltfield") + +// err3 = validate.FieldWithValue(uint(5), uint(1), "ltfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(uint(1), uint(5), "ltfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltfield") + +// type FloatTest struct { +// Val1 float64 `validate:"required"` +// Val2 float64 `validate:"required,ltfield=Val1"` +// } + +// floatTest := &FloatTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs = validate.Struct(floatTest) +// Equal(t, errs, nil) + +// floatTest = &FloatTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(floatTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltfield") + +// err3 = validate.FieldWithValue(float32(5), float32(1), "ltfield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(float32(1), float32(5), "ltfield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltfield") + +// PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltfield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltfield") }, "Bad field type string") +// PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltfield") }, "Bad Top Level field type") + +// type TimeTest2 struct { +// Start *time.Time `validate:"required"` +// End *time.Time `validate:"required,ltfield=NonExistantField"` +// } + +// timeTest2 := &TimeTest2{ +// Start: &end, +// End: &start, +// } + +// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +// } + +// func TestLteField(t *testing.T) { + +// type TimeTest struct { +// Start *time.Time `validate:"required,lte,ltefield=End"` +// End *time.Time `validate:"required,lte"` +// } + +// now := time.Now() +// start := now.Add(time.Hour * 24 * -1 * 2) +// end := start.Add(time.Hour * 24) + +// timeTest := &TimeTest{ +// Start: &start, +// End: &end, +// } + +// errs := validate.Struct(timeTest) +// Equal(t, errs, nil) + +// timeTest = &TimeTest{ +// Start: &end, +// End: &start, +// } + +// errs2 := validate.Struct(timeTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Start", "ltefield") + +// err3 := validate.FieldWithValue(&end, &start, "ltefield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(&start, &end, "ltefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltefield") + +// type IntTest struct { +// Val1 int `validate:"required"` +// Val2 int `validate:"required,ltefield=Val1"` +// } + +// intTest := &IntTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs = validate.Struct(intTest) +// Equal(t, errs, nil) + +// intTest = &IntTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(intTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltefield") + +// err3 = validate.FieldWithValue(int(5), int(1), "ltefield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(int(1), int(5), "ltefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltefield") - err := validate.Field(test.param, "datauri") +// type UIntTest struct { +// Val1 uint `validate:"required"` +// Val2 uint `validate:"required,ltefield=Val1"` +// } + +// uIntTest := &UIntTest{ +// Val1: 5, +// Val2: 1, +// } - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d DataURI failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "datauri") { - t.Fatalf("Index: %d DataURI failed Error: %s", i, err) - } - } - } -} +// errs = validate.Struct(uIntTest) +// Equal(t, errs, nil) + +// uIntTest = &UIntTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(uIntTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltefield") + +// err3 = validate.FieldWithValue(uint(5), uint(1), "ltefield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(uint(1), uint(5), "ltefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltefield") + +// type FloatTest struct { +// Val1 float64 `validate:"required"` +// Val2 float64 `validate:"required,ltefield=Val1"` +// } + +// floatTest := &FloatTest{ +// Val1: 5, +// Val2: 1, +// } + +// errs = validate.Struct(floatTest) +// Equal(t, errs, nil) + +// floatTest = &FloatTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs2 = validate.Struct(floatTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "ltefield") + +// err3 = validate.FieldWithValue(float32(5), float32(1), "ltefield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(float32(1), float32(5), "ltefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "ltefield") + +// PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltefield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltefield") }, "Bad field type string") +// PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltefield") }, "Bad Top Level field type") + +// type TimeTest2 struct { +// Start *time.Time `validate:"required"` +// End *time.Time `validate:"required,ltefield=NonExistantField"` +// } + +// timeTest2 := &TimeTest2{ +// Start: &end, +// End: &start, +// } + +// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +// } + +// func TestGteField(t *testing.T) { + +// type TimeTest struct { +// Start *time.Time `validate:"required,gte"` +// End *time.Time `validate:"required,gte,gtefield=Start"` +// } + +// now := time.Now() +// start := now.Add(time.Hour * 24) +// end := start.Add(time.Hour * 24) + +// timeTest := &TimeTest{ +// Start: &start, +// End: &end, +// } + +// errs := validate.Struct(timeTest) +// Equal(t, errs, nil) + +// timeTest = &TimeTest{ +// Start: &end, +// End: &start, +// } + +// errs2 := validate.Struct(timeTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "End", "gtefield") + +// err3 := validate.FieldWithValue(&start, &end, "gtefield") +// Equal(t, err3, nil) + +// err3 = validate.FieldWithValue(&end, &start, "gtefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtefield") + +// type IntTest struct { +// Val1 int `validate:"required"` +// Val2 int `validate:"required,gtefield=Val1"` +// } + +// intTest := &IntTest{ +// Val1: 1, +// Val2: 5, +// } + +// errs = validate.Struct(intTest) +// Equal(t, errs, nil) -func TestMultibyteValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", true}, - {"abc", false}, - {"123", false}, - {"<>@;.-=", false}, - {"ひらがな・カタカナ、.漢字", true}, - {"あいうえお foobar", true}, - {"test@example.com", true}, - {"test@example.com", true}, - {"1234abcDExyz", true}, - {"カタカナ", true}, - } +// intTest = &IntTest{ +// Val1: 5, +// Val2: 1, +// } - for i, test := range tests { +// errs2 = validate.Struct(intTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtefield") - err := validate.Field(test.param, "multibyte") +// err3 = validate.FieldWithValue(int(1), int(5), "gtefield") +// Equal(t, err3, nil) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "multibyte") { - t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) - } - } - } -} +// err3 = validate.FieldWithValue(int(5), int(1), "gtefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtefield") -func TestPrintableASCIIValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", true}, - {"foobar", false}, - {"xyz098", false}, - {"123456", false}, - {"カタカナ", false}, - {"foobar", true}, - {"0987654321", true}, - {"test@example.com", true}, - {"1234abcDEF", true}, - {"newline\n", false}, - {"\x19test\x7F", false}, - } +// type UIntTest struct { +// Val1 uint `validate:"required"` +// Val2 uint `validate:"required,gtefield=Val1"` +// } - for i, test := range tests { +// uIntTest := &UIntTest{ +// Val1: 1, +// Val2: 5, +// } - err := validate.Field(test.param, "printascii") +// errs = validate.Struct(uIntTest) +// Equal(t, errs, nil) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "printascii") { - t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) - } - } - } -} +// uIntTest = &UIntTest{ +// Val1: 5, +// Val2: 1, +// } -func TestASCIIValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", true}, - {"foobar", false}, - {"xyz098", false}, - {"123456", false}, - {"カタカナ", false}, - {"foobar", true}, - {"0987654321", true}, - {"test@example.com", true}, - {"1234abcDEF", true}, - {"", true}, - } +// errs2 = validate.Struct(uIntTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtefield") - for i, test := range tests { +// err3 = validate.FieldWithValue(uint(1), uint(5), "gtefield") +// Equal(t, err3, nil) - err := validate.Field(test.param, "ascii") +// err3 = validate.FieldWithValue(uint(5), uint(1), "gtefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtefield") - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d ASCII failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ascii") { - t.Fatalf("Index: %d ASCII failed Error: %s", i, err) - } - } - } -} +// type FloatTest struct { +// Val1 float64 `validate:"required"` +// Val2 float64 `validate:"required,gtefield=Val1"` +// } -func TestUUID5Validation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - - {"", false}, - {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, - {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, - {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, - {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, - {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, - } +// floatTest := &FloatTest{ +// Val1: 1, +// Val2: 5, +// } - for i, test := range tests { +// errs = validate.Struct(floatTest) +// Equal(t, errs, nil) - err := validate.Field(test.param, "uuid5") +// floatTest = &FloatTest{ +// Val1: 5, +// Val2: 1, +// } - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid5") { - t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) - } - } - } -} +// errs2 = validate.Struct(floatTest).Flatten() +// NotEqual(t, errs2, nil) +// AssertMapFieldError(t, errs2, "Val2", "gtefield") -func TestUUID4Validation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, - {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, - {"934859", false}, - {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, - {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, - } +// err3 = validate.FieldWithValue(float32(1), float32(5), "gtefield") +// Equal(t, err3, nil) - for i, test := range tests { +// err3 = validate.FieldWithValue(float32(5), float32(1), "gtefield") +// NotEqual(t, err3, nil) +// Equal(t, err3.Tag, "gtefield") - err := validate.Field(test.param, "uuid4") +// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtefield") }, "struct not passed for cross validation") +// PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtefield") }, "Bad field type string") +// PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtefield") }, "Bad Top Level field type") - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid4") { - t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) - } - } - } -} +// type TimeTest2 struct { +// Start *time.Time `validate:"required"` +// End *time.Time `validate:"required,gtefield=NonExistantField"` +// } -func TestUUID3Validation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"412452646", false}, - {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, - {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, - {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, - } +// timeTest2 := &TimeTest2{ +// Start: &start, +// End: &end, +// } - for i, test := range tests { +// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +// } - err := validate.Field(test.param, "uuid3") +// func TestValidateByTagAndValue(t *testing.T) { - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid3") { - t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) - } - } - } -} +// val := "test" +// field := "test" +// err := validate.FieldWithValue(val, field, "required") +// Equal(t, err, nil) -func TestUUIDValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, - {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, - {"a987fbc94bed3078cf079141ba07c9f3", false}, - {"934859", false}, - {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, - {"aaaaaaaa-1111-1111-aaag-111111111111", false}, - {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, - } +// fn := func(val interface{}, current interface{}, field interface{}, param string) bool { - for i, test := range tests { +// return current.(string) == field.(string) +// } - err := validate.Field(test.param, "uuid") +// validate.AddFunction("isequaltestfunc", fn) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d UUID failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid") { - t.Fatalf("Index: %d UUID failed Error: %s", i, err) - } - } - } -} +// err = validate.FieldWithValue(val, field, "isequaltestfunc") +// Equal(t, err, nil) -func TestISBNValidation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"foo", false}, - {"3836221195", true}, - {"1-61729-085-8", true}, - {"3 423 21412 0", true}, - {"3 401 01319 X", true}, - {"9784873113685", true}, - {"978-4-87311-368-5", true}, - {"978 3401013190", true}, - {"978-3-8362-2119-1", true}, - } +// val = "unequal" - for i, test := range tests { +// err = validate.FieldWithValue(val, field, "isequaltestfunc") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "isequaltestfunc") +// } - err := validate.Field(test.param, "isbn") +// func TestAddFunctions(t *testing.T) { - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d ISBN failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") { - t.Fatalf("Index: %d ISBN failed Error: %s", i, err) - } - } - } -} +// fn := func(val interface{}, current interface{}, field interface{}, param string) bool { -func TestISBN13Validation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"foo", false}, - {"3-8362-2119-5", false}, - {"01234567890ab", false}, - {"978 3 8362 2119 0", false}, - {"9784873113685", true}, - {"978-4-87311-368-5", true}, - {"978 3401013190", true}, - {"978-3-8362-2119-1", true}, - } +// return true +// } - for i, test := range tests { +// validate := New("validateme", BakedInValidators) - err := validate.Field(test.param, "isbn13") +// err := validate.AddFunction("new", fn) +// Equal(t, err, nil) - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") { - t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) - } - } - } -} +// err = validate.AddFunction("", fn) +// NotEqual(t, err, nil) -func TestISBN10Validation(t *testing.T) { - tests := []struct { - param string - expected bool - }{ - {"", false}, - {"foo", false}, - {"3423214121", false}, - {"978-3836221191", false}, - {"3-423-21412-1", false}, - {"3 423 21412 1", false}, - {"3836221195", true}, - {"1-61729-085-8", true}, - {"3 423 21412 0", true}, - {"3 401 01319 X", true}, - } +// validate.AddFunction("new", nil) +// NotEqual(t, err, nil) - for i, test := range tests { +// err = validate.AddFunction("new", fn) +// Equal(t, err, nil) +// } - err := validate.Field(test.param, "isbn10") +// func TestChangeTag(t *testing.T) { - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") { - t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) - } - } - } -} +// validate := New("validateme", BakedInValidators) +// validate.SetTag("val") -func TestExcludesRuneValidation(t *testing.T) { +// type Test struct { +// Name string `val:"len=4"` +// } +// s := &Test{ +// Name: "TEST", +// } - tests := []struct { - Value string `validate:"excludesrune=☻"` - Tag string - ExpectedNil bool - }{ - {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, - {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, - } +// err := validate.Struct(s) +// Equal(t, err, nil) +// } - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// func TestUnexposedStruct(t *testing.T) { - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// type Test struct { +// Name string +// unexposed struct { +// A string `validate:"required"` +// } +// } - errs := validate.Struct(s) +// s := &Test{ +// Name: "TEST", +// } - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } -} +// err := validate.Struct(s) +// Equal(t, err, nil) +// } -func TestExcludesAllValidation(t *testing.T) { +// func TestBadParams(t *testing.T) { - tests := []struct { - Value string `validate:"excludesall=@!{}[]"` - Tag string - ExpectedNil bool - }{ - {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, - {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, - } +// i := 1 +// err := validate.Field(i, "-") +// Equal(t, err, nil) - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") +// PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// var ui uint = 1 +// PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") - errs := validate.Struct(s) +// f := 1.23 +// PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") +// } - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } +// func TestLength(t *testing.T) { - username := "joeybloggs " +// i := true +// PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") +// } - err := validate.Field(username, "excludesall=@ ") - NotEqual(t, err, nil) +// func TestIsGt(t *testing.T) { - excluded := "," +// myMap := map[string]string{} +// err := validate.Field(myMap, "gt=0") +// NotEqual(t, err, nil) - err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") - NotEqual(t, err, nil) +// f := 1.23 +// err = validate.Field(f, "gt=5") +// NotEqual(t, err, nil) - excluded = "=" +// var ui uint = 5 +// err = validate.Field(ui, "gt=10") +// NotEqual(t, err, nil) - err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") - NotEqual(t, err, nil) -} +// i := true +// PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") -func TestExcludesValidation(t *testing.T) { +// tm := time.Now().UTC() +// tm = tm.Add(time.Hour * 24) - tests := []struct { - Value string `validate:"excludes=@"` - Tag string - ExpectedNil bool - }{ - {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, - {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, - } +// err = validate.Field(tm, "gt") +// Equal(t, err, nil) - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// t2 := time.Now().UTC() - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// err = validate.Field(t2, "gt") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "gt") - errs := validate.Struct(s) +// type Test struct { +// Now *time.Time `validate:"gt"` +// } +// s := &Test{ +// Now: &tm, +// } - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } -} +// errs := validate.Struct(s) +// Equal(t, errs, nil) -func TestContainsRuneValidation(t *testing.T) { +// s = &Test{ +// Now: &t2, +// } - tests := []struct { - Value string `validate:"containsrune=☻"` - Tag string - ExpectedNil bool - }{ - {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, - {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, - } +// errs = validate.Struct(s) +// NotEqual(t, errs, nil) +// } - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// func TestIsGte(t *testing.T) { - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// i := true +// PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") - errs := validate.Struct(s) +// t1 := time.Now().UTC() +// t1 = t1.Add(time.Hour * 24) - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } -} +// err := validate.Field(t1, "gte") +// Equal(t, err, nil) -func TestContainsAnyValidation(t *testing.T) { +// t2 := time.Now().UTC() - tests := []struct { - Value string `validate:"containsany=@!{}[]"` - Tag string - ExpectedNil bool - }{ - {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, - {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, - } +// err = validate.Field(t2, "gte") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "gte") +// Equal(t, err.Type, reflect.TypeOf(time.Time{})) - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// type Test struct { +// Now *time.Time `validate:"gte"` +// } +// s := &Test{ +// Now: &t1, +// } - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// errs := validate.Struct(s) +// Equal(t, errs, nil) - errs := validate.Struct(s) +// s = &Test{ +// Now: &t2, +// } - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } -} +// errs = validate.Struct(s) +// NotEqual(t, errs, nil) +// } -func TestContainsValidation(t *testing.T) { +// func TestIsLt(t *testing.T) { - tests := []struct { - Value string `validate:"contains=@"` - Tag string - ExpectedNil bool - }{ - {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, - {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, - } +// myMap := map[string]string{} +// err := validate.Field(myMap, "lt=0") +// NotEqual(t, err, nil) - for i, s := range tests { - err := validate.Field(s.Value, s.Tag) +// f := 1.23 +// err = validate.Field(f, "lt=0") +// NotEqual(t, err, nil) - if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { - t.Fatalf("Index: %d failed Error: %s", i, err) - } +// var ui uint = 5 +// err = validate.Field(ui, "lt=0") +// NotEqual(t, err, nil) - errs := validate.Struct(s) +// i := true +// PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") - if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { - t.Fatalf("Index: %d failed Error: %s", i, errs) - } - } -} +// t1 := time.Now().UTC() + +// err = validate.Field(t1, "lt") +// Equal(t, err, nil) + +// t2 := time.Now().UTC() +// t2 = t2.Add(time.Hour * 24) + +// err = validate.Field(t2, "lt") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "lt") + +// type Test struct { +// Now *time.Time `validate:"lt"` +// } + +// s := &Test{ +// Now: &t1, +// } + +// errs := validate.Struct(s) +// Equal(t, errs, nil) + +// s = &Test{ +// Now: &t2, +// } + +// errs = validate.Struct(s) +// NotEqual(t, errs, nil) +// } + +// func TestIsLte(t *testing.T) { + +// i := true +// PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") + +// t1 := time.Now().UTC() + +// err := validate.Field(t1, "lte") +// Equal(t, err, nil) + +// t2 := time.Now().UTC() +// t2 = t2.Add(time.Hour * 24) + +// err = validate.Field(t2, "lte") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "lte") + +// type Test struct { +// Now *time.Time `validate:"lte"` +// } + +// s := &Test{ +// Now: &t1, +// } + +// errs := validate.Struct(s) +// Equal(t, errs, nil) + +// s = &Test{ +// Now: &t2, +// } + +// errs = validate.Struct(s) +// NotEqual(t, errs, nil) +// } + +// func TestUrl(t *testing.T) { + +// var tests = []struct { +// param string +// expected bool +// }{ +// {"http://foo.bar#com", true}, +// {"http://foobar.com", true}, +// {"https://foobar.com", true}, +// {"foobar.com", false}, +// {"http://foobar.coffee/", true}, +// {"http://foobar.中文网/", true}, +// {"http://foobar.org/", true}, +// {"http://foobar.org:8080/", true}, +// {"ftp://foobar.ru/", true}, +// {"http://user:pass@www.foobar.com/", true}, +// {"http://127.0.0.1/", true}, +// {"http://duckduckgo.com/?q=%2F", true}, +// {"http://localhost:3000/", true}, +// {"http://foobar.com/?foo=bar#baz=qux", true}, +// {"http://foobar.com?foo=bar", true}, +// {"http://www.xn--froschgrn-x9a.net/", true}, +// {"", false}, +// {"xyz://foobar.com", true}, +// {"invalid.", false}, +// {".com", false}, +// {"rtmp://foobar.com", true}, +// {"http://www.foo_bar.com/", true}, +// {"http://localhost:3000/", true}, +// {"http://foobar.com#baz=qux", true}, +// {"http://foobar.com/t$-_.+!*\\'(),", true}, +// {"http://www.foobar.com/~foobar", true}, +// {"http://www.-foobar.com/", true}, +// {"http://www.foo---bar.com/", true}, +// {"mailto:someone@example.com", true}, +// {"irc://irc.server.org/channel", true}, +// {"irc://#channel@network", true}, +// {"/abs/test/dir", false}, +// {"./rel/test/dir", false}, +// } +// for i, test := range tests { + +// err := validate.Field(test.param, "url") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d URL failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "url") { +// t.Fatalf("Index: %d URL failed Error: %s", i, err) +// } +// } +// } + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") +// } + +// func TestUri(t *testing.T) { + +// var tests = []struct { +// param string +// expected bool +// }{ +// {"http://foo.bar#com", true}, +// {"http://foobar.com", true}, +// {"https://foobar.com", true}, +// {"foobar.com", false}, +// {"http://foobar.coffee/", true}, +// {"http://foobar.中文网/", true}, +// {"http://foobar.org/", true}, +// {"http://foobar.org:8080/", true}, +// {"ftp://foobar.ru/", true}, +// {"http://user:pass@www.foobar.com/", true}, +// {"http://127.0.0.1/", true}, +// {"http://duckduckgo.com/?q=%2F", true}, +// {"http://localhost:3000/", true}, +// {"http://foobar.com/?foo=bar#baz=qux", true}, +// {"http://foobar.com?foo=bar", true}, +// {"http://www.xn--froschgrn-x9a.net/", true}, +// {"", false}, +// {"xyz://foobar.com", true}, +// {"invalid.", false}, +// {".com", false}, +// {"rtmp://foobar.com", true}, +// {"http://www.foo_bar.com/", true}, +// {"http://localhost:3000/", true}, +// {"http://foobar.com#baz=qux", true}, +// {"http://foobar.com/t$-_.+!*\\'(),", true}, +// {"http://www.foobar.com/~foobar", true}, +// {"http://www.-foobar.com/", true}, +// {"http://www.foo---bar.com/", true}, +// {"mailto:someone@example.com", true}, +// {"irc://irc.server.org/channel", true}, +// {"irc://#channel@network", true}, +// {"/abs/test/dir", true}, +// {"./rel/test/dir", false}, +// } +// for i, test := range tests { + +// err := validate.Field(test.param, "uri") + +// if test.expected == true { +// if !IsEqual(t, err, nil) { +// t.Fatalf("Index: %d URI failed Error: %s", i, err) +// } +// } else { +// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uri") { +// t.Fatalf("Index: %d URI failed Error: %s", i, err) +// } +// } +// } + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") +// } + +// func TestOrTag(t *testing.T) { +// s := "rgba(0,31,255,0.5)" +// err := validate.Field(s, "rgb|rgba") +// Equal(t, err, nil) + +// s = "rgba(0,31,255,0.5)" +// err = validate.Field(s, "rgb|rgba|len=18") +// Equal(t, err, nil) + +// s = "this ain't right" +// err = validate.Field(s, "rgb|rgba") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb|rgba") + +// s = "this ain't right" +// err = validate.Field(s, "rgb|rgba|len=10") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb|rgba|len") + +// s = "this is right" +// err = validate.Field(s, "rgb|rgba|len=13") +// Equal(t, err, nil) + +// s = "" +// err = validate.Field(s, "omitempty,rgb|rgba") +// Equal(t, err, nil) +// } + +// func TestHsla(t *testing.T) { + +// s := "hsla(360,100%,100%,1)" +// err := validate.Field(s, "hsla") +// Equal(t, err, nil) + +// s = "hsla(360,100%,100%,0.5)" +// err = validate.Field(s, "hsla") +// Equal(t, err, nil) + +// s = "hsla(0,0%,0%, 0)" +// err = validate.Field(s, "hsla") +// Equal(t, err, nil) + +// s = "hsl(361,100%,50%,1)" +// err = validate.Field(s, "hsla") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsla") + +// s = "hsl(361,100%,50%)" +// err = validate.Field(s, "hsla") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsla") + +// s = "hsla(361,100%,50%)" +// err = validate.Field(s, "hsla") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsla") + +// s = "hsla(360,101%,50%)" +// err = validate.Field(s, "hsla") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsla") + +// s = "hsla(360,100%,101%)" +// err = validate.Field(s, "hsla") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsla") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "hsla") }, "interface conversion: interface is int, not string") +// } + +// func TestHsl(t *testing.T) { + +// s := "hsl(360,100%,50%)" +// err := validate.Field(s, "hsl") +// Equal(t, err, nil) + +// s = "hsl(0,0%,0%)" +// err = validate.Field(s, "hsl") +// Equal(t, err, nil) + +// s = "hsl(361,100%,50%)" +// err = validate.Field(s, "hsl") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsl") + +// s = "hsl(361,101%,50%)" +// err = validate.Field(s, "hsl") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsl") + +// s = "hsl(361,100%,101%)" +// err = validate.Field(s, "hsl") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsl") + +// s = "hsl(-10,100%,100%)" +// err = validate.Field(s, "hsl") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hsl") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "hsl") }, "interface conversion: interface is int, not string") +// } + +// func TestRgba(t *testing.T) { + +// s := "rgba(0,31,255,0.5)" +// err := validate.Field(s, "rgba") +// Equal(t, err, nil) + +// s = "rgba(0,31,255,0.12)" +// err = validate.Field(s, "rgba") +// Equal(t, err, nil) + +// s = "rgba(12%,55%,100%,0.12)" +// err = validate.Field(s, "rgba") +// Equal(t, err, nil) + +// s = "rgba( 0, 31, 255, 0.5)" +// err = validate.Field(s, "rgba") +// Equal(t, err, nil) + +// s = "rgba(12%,55,100%,0.12)" +// err = validate.Field(s, "rgba") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgba") + +// s = "rgb(0, 31, 255)" +// err = validate.Field(s, "rgba") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgba") + +// s = "rgb(1,349,275,0.5)" +// err = validate.Field(s, "rgba") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgba") + +// s = "rgb(01,31,255,0.5)" +// err = validate.Field(s, "rgba") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgba") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "rgba") }, "interface conversion: interface is int, not string") +// } + +// func TestRgb(t *testing.T) { + +// s := "rgb(0,31,255)" +// err := validate.Field(s, "rgb") +// Equal(t, err, nil) + +// s = "rgb(0, 31, 255)" +// err = validate.Field(s, "rgb") +// Equal(t, err, nil) + +// s = "rgb(10%, 50%, 100%)" +// err = validate.Field(s, "rgb") +// Equal(t, err, nil) + +// s = "rgb(10%, 50%, 55)" +// err = validate.Field(s, "rgb") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb") + +// s = "rgb(1,349,275)" +// err = validate.Field(s, "rgb") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb") + +// s = "rgb(01,31,255)" +// err = validate.Field(s, "rgb") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb") + +// s = "rgba(0,31,255)" +// err = validate.Field(s, "rgb") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "rgb") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "rgb") }, "interface conversion: interface is int, not string") +// } + +// func TestEmail(t *testing.T) { + +// s := "test@mail.com" +// err := validate.Field(s, "email") +// Equal(t, err, nil) + +// s = "" +// err = validate.Field(s, "email") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "email") + +// s = "test@email" +// err = validate.Field(s, "email") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "email") + +// s = "test@email." +// err = validate.Field(s, "email") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "email") + +// s = "@email.com" +// err = validate.Field(s, "email") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "email") + +// i := true +// PanicMatches(t, func() { validate.Field(i, "email") }, "interface conversion: interface is bool, not string") +// } + +// func TestHexColor(t *testing.T) { + +// s := "#fff" +// err := validate.Field(s, "hexcolor") +// Equal(t, err, nil) + +// s = "#c2c2c2" +// err = validate.Field(s, "hexcolor") +// Equal(t, err, nil) + +// s = "fff" +// err = validate.Field(s, "hexcolor") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hexcolor") + +// s = "fffFF" +// err = validate.Field(s, "hexcolor") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hexcolor") + +// i := true +// PanicMatches(t, func() { validate.Field(i, "hexcolor") }, "interface conversion: interface is bool, not string") +// } + +// func TestHexadecimal(t *testing.T) { + +// s := "ff0044" +// err := validate.Field(s, "hexadecimal") +// Equal(t, err, nil) + +// s = "abcdefg" +// err = validate.Field(s, "hexadecimal") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "hexadecimal") + +// i := true +// PanicMatches(t, func() { validate.Field(i, "hexadecimal") }, "interface conversion: interface is bool, not string") +// } + +// func TestNumber(t *testing.T) { + +// s := "1" +// err := validate.Field(s, "number") +// Equal(t, err, nil) + +// s = "+1" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// s = "-1" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") -func TestIsNeFieldValidation(t *testing.T) { - - var j uint64 - var k float64 - s := "abcd" - i := 1 - j = 1 - k = 1.543 - arr := []string{"test"} - now := time.Now().UTC() - - var j2 uint64 - var k2 float64 - s2 := "abcdef" - i2 := 3 - j2 = 2 - k2 = 1.5434456 - arr2 := []string{"test", "test2"} - arr3 := []string{"test"} - now2 := now - - err := validate.FieldWithValue(s, s2, "nefield") - Equal(t, err, nil) - - err = validate.FieldWithValue(i2, i, "nefield") - Equal(t, err, nil) - - err = validate.FieldWithValue(j2, j, "nefield") - Equal(t, err, nil) - - err = validate.FieldWithValue(k2, k, "nefield") - Equal(t, err, nil) - - err = validate.FieldWithValue(arr2, arr, "nefield") - Equal(t, err, nil) - - err = validate.FieldWithValue(now2, now, "nefield") - NotEqual(t, err, nil) - - err = validate.FieldWithValue(arr3, arr, "nefield") - NotEqual(t, err, nil) - - type Test struct { - Start *time.Time `validate:"nefield=End"` - End *time.Time - } - - sv := &Test{ - Start: &now, - End: &now, - } - - errs := validate.Struct(sv) - NotEqual(t, errs, nil) - - now3 := time.Now().UTC() - - sv = &Test{ - Start: &now, - End: &now3, - } - - errs = validate.Struct(sv) - Equal(t, errs, nil) - - channel := make(chan string) - - PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "nefield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(5, channel, "nefield") }, "Bad field type chan string") - PanicMatches(t, func() { validate.FieldWithValue(5, now, "nefield") }, "Bad Top Level field type") - - type Test2 struct { - Start *time.Time `validate:"nefield=NonExistantField"` - End *time.Time - } - - sv2 := &Test2{ - Start: &now, - End: &now, - } - - PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestIsNeValidation(t *testing.T) { - - var j uint64 - var k float64 - s := "abcdef" - i := 3 - j = 2 - k = 1.5434 - arr := []string{"test"} - now := time.Now().UTC() - - err := validate.Field(s, "ne=abcd") - Equal(t, err, nil) - - err = validate.Field(i, "ne=1") - Equal(t, err, nil) - - err = validate.Field(j, "ne=1") - Equal(t, err, nil) - - err = validate.Field(k, "ne=1.543") - Equal(t, err, nil) - - err = validate.Field(arr, "ne=2") - Equal(t, err, nil) - - err = validate.Field(arr, "ne=1") - NotEqual(t, err, nil) - - PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") -} - -func TestIsEqFieldValidation(t *testing.T) { - - var j uint64 - var k float64 - s := "abcd" - i := 1 - j = 1 - k = 1.543 - arr := []string{"test"} - now := time.Now().UTC() - - var j2 uint64 - var k2 float64 - s2 := "abcd" - i2 := 1 - j2 = 1 - k2 = 1.543 - arr2 := []string{"test"} - arr3 := []string{"test", "test2"} - now2 := now - - err := validate.FieldWithValue(s, s2, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(i2, i, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(j2, j, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(k2, k, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(arr2, arr, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(now2, now, "eqfield") - Equal(t, err, nil) - - err = validate.FieldWithValue(arr3, arr, "eqfield") - NotEqual(t, err, nil) - - type Test struct { - Start *time.Time `validate:"eqfield=End"` - End *time.Time - } - - sv := &Test{ - Start: &now, - End: &now, - } - - errs := validate.Struct(sv) - Equal(t, errs, nil) - - now3 := time.Now().UTC() - - sv = &Test{ - Start: &now, - End: &now3, - } - - errs = validate.Struct(sv) - NotEqual(t, errs, nil) - - channel := make(chan string) - - PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "eqfield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(5, channel, "eqfield") }, "Bad field type chan string") - PanicMatches(t, func() { validate.FieldWithValue(5, now, "eqfield") }, "Bad Top Level field type") - - type Test2 struct { - Start *time.Time `validate:"eqfield=NonExistantField"` - End *time.Time - } - - sv2 := &Test2{ - Start: &now, - End: &now, - } - - PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestIsEqValidation(t *testing.T) { - - var j uint64 - var k float64 - s := "abcd" - i := 1 - j = 1 - k = 1.543 - arr := []string{"test"} - now := time.Now().UTC() - - err := validate.Field(s, "eq=abcd") - Equal(t, err, nil) - - err = validate.Field(i, "eq=1") - Equal(t, err, nil) - - err = validate.Field(j, "eq=1") - Equal(t, err, nil) - - err = validate.Field(k, "eq=1.543") - Equal(t, err, nil) - - err = validate.Field(arr, "eq=1") - Equal(t, err, nil) - - err = validate.Field(arr, "eq=2") - NotEqual(t, err, nil) - - PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") -} - -func TestBase64Validation(t *testing.T) { - - s := "dW5pY29ybg==" - - err := validate.Field(s, "base64") - Equal(t, err, nil) - - s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" - err = validate.Field(s, "base64") - Equal(t, err, nil) - - s = "" - err = validate.Field(s, "base64") - NotEqual(t, err, nil) - - s = "dW5pY29ybg== foo bar" - err = validate.Field(s, "base64") - NotEqual(t, err, nil) -} - -func TestStructOnlyValidation(t *testing.T) { - - type Inner struct { - Test string `validate:"len=5"` - } - - type Outer struct { - InnerStruct *Inner `validate:"required,structonly"` - } - - outer := &Outer{ - InnerStruct: nil, - } - - errs := validate.Struct(outer) - NotEqual(t, errs, nil) - - inner := &Inner{ - Test: "1234", - } - - outer = &Outer{ - InnerStruct: inner, - } - - errs = validate.Struct(outer) - Equal(t, errs, nil) -} - -func TestGtField(t *testing.T) { - - type TimeTest struct { - Start *time.Time `validate:"required,gt"` - End *time.Time `validate:"required,gt,gtfield=Start"` - } - - now := time.Now() - start := now.Add(time.Hour * 24) - end := start.Add(time.Hour * 24) - - timeTest := &TimeTest{ - Start: &start, - End: &end, - } - - errs := validate.Struct(timeTest) - Equal(t, errs, nil) - - timeTest = &TimeTest{ - Start: &end, - End: &start, - } - - errs2 := validate.Struct(timeTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "End", "gtfield") - - err3 := validate.FieldWithValue(&start, &end, "gtfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(&end, &start, "gtfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtfield") - - type IntTest struct { - Val1 int `validate:"required"` - Val2 int `validate:"required,gtfield=Val1"` - } - - intTest := &IntTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(intTest) - Equal(t, errs, nil) - - intTest = &IntTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(intTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtfield") - - err3 = validate.FieldWithValue(int(1), int(5), "gtfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(int(5), int(1), "gtfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtfield") - - type UIntTest struct { - Val1 uint `validate:"required"` - Val2 uint `validate:"required,gtfield=Val1"` - } - - uIntTest := &UIntTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(uIntTest) - Equal(t, errs, nil) - - uIntTest = &UIntTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(uIntTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtfield") - - err3 = validate.FieldWithValue(uint(1), uint(5), "gtfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(uint(5), uint(1), "gtfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtfield") - - type FloatTest struct { - Val1 float64 `validate:"required"` - Val2 float64 `validate:"required,gtfield=Val1"` - } - - floatTest := &FloatTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(floatTest) - Equal(t, errs, nil) - - floatTest = &FloatTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(floatTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtfield") - - err3 = validate.FieldWithValue(float32(1), float32(5), "gtfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(float32(5), float32(1), "gtfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtfield") - - PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtfield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtfield") }, "Bad field type string") - PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtfield") }, "Bad Top Level field type") - - type TimeTest2 struct { - Start *time.Time `validate:"required"` - End *time.Time `validate:"required,gtfield=NonExistantField"` - } - - timeTest2 := &TimeTest2{ - Start: &start, - End: &end, - } - - PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestLtField(t *testing.T) { - - type TimeTest struct { - Start *time.Time `validate:"required,lt,ltfield=End"` - End *time.Time `validate:"required,lt"` - } - - now := time.Now() - start := now.Add(time.Hour * 24 * -1 * 2) - end := start.Add(time.Hour * 24) - - timeTest := &TimeTest{ - Start: &start, - End: &end, - } - - errs := validate.Struct(timeTest) - Equal(t, errs, nil) - - timeTest = &TimeTest{ - Start: &end, - End: &start, - } - - errs2 := validate.Struct(timeTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Start", "ltfield") - - err3 := validate.FieldWithValue(&end, &start, "ltfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(&start, &end, "ltfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltfield") - - type IntTest struct { - Val1 int `validate:"required"` - Val2 int `validate:"required,ltfield=Val1"` - } - - intTest := &IntTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(intTest) - Equal(t, errs, nil) - - intTest = &IntTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(intTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltfield") - - err3 = validate.FieldWithValue(int(5), int(1), "ltfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(int(1), int(5), "ltfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltfield") - - type UIntTest struct { - Val1 uint `validate:"required"` - Val2 uint `validate:"required,ltfield=Val1"` - } - - uIntTest := &UIntTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(uIntTest) - Equal(t, errs, nil) - - uIntTest = &UIntTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(uIntTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltfield") - - err3 = validate.FieldWithValue(uint(5), uint(1), "ltfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(uint(1), uint(5), "ltfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltfield") - - type FloatTest struct { - Val1 float64 `validate:"required"` - Val2 float64 `validate:"required,ltfield=Val1"` - } - - floatTest := &FloatTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(floatTest) - Equal(t, errs, nil) - - floatTest = &FloatTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(floatTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltfield") - - err3 = validate.FieldWithValue(float32(5), float32(1), "ltfield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(float32(1), float32(5), "ltfield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltfield") - - PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltfield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltfield") }, "Bad field type string") - PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltfield") }, "Bad Top Level field type") - - type TimeTest2 struct { - Start *time.Time `validate:"required"` - End *time.Time `validate:"required,ltfield=NonExistantField"` - } - - timeTest2 := &TimeTest2{ - Start: &end, - End: &start, - } - - PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestLteField(t *testing.T) { - - type TimeTest struct { - Start *time.Time `validate:"required,lte,ltefield=End"` - End *time.Time `validate:"required,lte"` - } - - now := time.Now() - start := now.Add(time.Hour * 24 * -1 * 2) - end := start.Add(time.Hour * 24) - - timeTest := &TimeTest{ - Start: &start, - End: &end, - } - - errs := validate.Struct(timeTest) - Equal(t, errs, nil) - - timeTest = &TimeTest{ - Start: &end, - End: &start, - } - - errs2 := validate.Struct(timeTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Start", "ltefield") - - err3 := validate.FieldWithValue(&end, &start, "ltefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(&start, &end, "ltefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltefield") - - type IntTest struct { - Val1 int `validate:"required"` - Val2 int `validate:"required,ltefield=Val1"` - } - - intTest := &IntTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(intTest) - Equal(t, errs, nil) - - intTest = &IntTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(intTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltefield") - - err3 = validate.FieldWithValue(int(5), int(1), "ltefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(int(1), int(5), "ltefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltefield") - - type UIntTest struct { - Val1 uint `validate:"required"` - Val2 uint `validate:"required,ltefield=Val1"` - } - - uIntTest := &UIntTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(uIntTest) - Equal(t, errs, nil) - - uIntTest = &UIntTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(uIntTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltefield") - - err3 = validate.FieldWithValue(uint(5), uint(1), "ltefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(uint(1), uint(5), "ltefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltefield") - - type FloatTest struct { - Val1 float64 `validate:"required"` - Val2 float64 `validate:"required,ltefield=Val1"` - } - - floatTest := &FloatTest{ - Val1: 5, - Val2: 1, - } - - errs = validate.Struct(floatTest) - Equal(t, errs, nil) - - floatTest = &FloatTest{ - Val1: 1, - Val2: 5, - } - - errs2 = validate.Struct(floatTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "ltefield") - - err3 = validate.FieldWithValue(float32(5), float32(1), "ltefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(float32(1), float32(5), "ltefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "ltefield") - - PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltefield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltefield") }, "Bad field type string") - PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltefield") }, "Bad Top Level field type") - - type TimeTest2 struct { - Start *time.Time `validate:"required"` - End *time.Time `validate:"required,ltefield=NonExistantField"` - } - - timeTest2 := &TimeTest2{ - Start: &end, - End: &start, - } - - PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestGteField(t *testing.T) { - - type TimeTest struct { - Start *time.Time `validate:"required,gte"` - End *time.Time `validate:"required,gte,gtefield=Start"` - } - - now := time.Now() - start := now.Add(time.Hour * 24) - end := start.Add(time.Hour * 24) - - timeTest := &TimeTest{ - Start: &start, - End: &end, - } - - errs := validate.Struct(timeTest) - Equal(t, errs, nil) - - timeTest = &TimeTest{ - Start: &end, - End: &start, - } - - errs2 := validate.Struct(timeTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "End", "gtefield") - - err3 := validate.FieldWithValue(&start, &end, "gtefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(&end, &start, "gtefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtefield") - - type IntTest struct { - Val1 int `validate:"required"` - Val2 int `validate:"required,gtefield=Val1"` - } - - intTest := &IntTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(intTest) - Equal(t, errs, nil) - - intTest = &IntTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(intTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtefield") - - err3 = validate.FieldWithValue(int(1), int(5), "gtefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(int(5), int(1), "gtefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtefield") - - type UIntTest struct { - Val1 uint `validate:"required"` - Val2 uint `validate:"required,gtefield=Val1"` - } - - uIntTest := &UIntTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(uIntTest) - Equal(t, errs, nil) - - uIntTest = &UIntTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(uIntTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtefield") - - err3 = validate.FieldWithValue(uint(1), uint(5), "gtefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(uint(5), uint(1), "gtefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtefield") - - type FloatTest struct { - Val1 float64 `validate:"required"` - Val2 float64 `validate:"required,gtefield=Val1"` - } - - floatTest := &FloatTest{ - Val1: 1, - Val2: 5, - } - - errs = validate.Struct(floatTest) - Equal(t, errs, nil) - - floatTest = &FloatTest{ - Val1: 5, - Val2: 1, - } - - errs2 = validate.Struct(floatTest).Flatten() - NotEqual(t, errs2, nil) - AssertMapFieldError(t, errs2, "Val2", "gtefield") - - err3 = validate.FieldWithValue(float32(1), float32(5), "gtefield") - Equal(t, err3, nil) - - err3 = validate.FieldWithValue(float32(5), float32(1), "gtefield") - NotEqual(t, err3, nil) - Equal(t, err3.Tag, "gtefield") - - PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtefield") }, "struct not passed for cross validation") - PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtefield") }, "Bad field type string") - PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtefield") }, "Bad Top Level field type") - - type TimeTest2 struct { - Start *time.Time `validate:"required"` - End *time.Time `validate:"required,gtefield=NonExistantField"` - } - - timeTest2 := &TimeTest2{ - Start: &start, - End: &end, - } - - PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -} - -func TestValidateByTagAndValue(t *testing.T) { - - val := "test" - field := "test" - err := validate.FieldWithValue(val, field, "required") - Equal(t, err, nil) - - fn := func(val interface{}, current interface{}, field interface{}, param string) bool { - - return current.(string) == field.(string) - } - - validate.AddFunction("isequaltestfunc", fn) - - err = validate.FieldWithValue(val, field, "isequaltestfunc") - Equal(t, err, nil) - - val = "unequal" - - err = validate.FieldWithValue(val, field, "isequaltestfunc") - NotEqual(t, err, nil) - Equal(t, err.Tag, "isequaltestfunc") -} - -func TestAddFunctions(t *testing.T) { - - fn := func(val interface{}, current interface{}, field interface{}, param string) bool { - - return true - } - - validate := New("validateme", BakedInValidators) - - err := validate.AddFunction("new", fn) - Equal(t, err, nil) - - err = validate.AddFunction("", fn) - NotEqual(t, err, nil) - - validate.AddFunction("new", nil) - NotEqual(t, err, nil) - - err = validate.AddFunction("new", fn) - Equal(t, err, nil) -} - -func TestChangeTag(t *testing.T) { - - validate := New("validateme", BakedInValidators) - validate.SetTag("val") - - type Test struct { - Name string `val:"len=4"` - } - s := &Test{ - Name: "TEST", - } - - err := validate.Struct(s) - Equal(t, err, nil) -} - -func TestUnexposedStruct(t *testing.T) { - - type Test struct { - Name string - unexposed struct { - A string `validate:"required"` - } - } - - s := &Test{ - Name: "TEST", - } - - err := validate.Struct(s) - Equal(t, err, nil) -} - -func TestBadParams(t *testing.T) { - - i := 1 - err := validate.Field(i, "-") - Equal(t, err, nil) - - PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") - PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") - - var ui uint = 1 - PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") - - f := 1.23 - PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") -} - -func TestLength(t *testing.T) { - - i := true - PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") -} - -func TestIsGt(t *testing.T) { - - myMap := map[string]string{} - err := validate.Field(myMap, "gt=0") - NotEqual(t, err, nil) - - f := 1.23 - err = validate.Field(f, "gt=5") - NotEqual(t, err, nil) - - var ui uint = 5 - err = validate.Field(ui, "gt=10") - NotEqual(t, err, nil) - - i := true - PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") - - tm := time.Now().UTC() - tm = tm.Add(time.Hour * 24) - - err = validate.Field(tm, "gt") - Equal(t, err, nil) - - t2 := time.Now().UTC() - - err = validate.Field(t2, "gt") - NotEqual(t, err, nil) - Equal(t, err.Tag, "gt") - - type Test struct { - Now *time.Time `validate:"gt"` - } - s := &Test{ - Now: &tm, - } - - errs := validate.Struct(s) - Equal(t, errs, nil) - - s = &Test{ - Now: &t2, - } - - errs = validate.Struct(s) - NotEqual(t, errs, nil) -} - -func TestIsGte(t *testing.T) { - - i := true - PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") - - t1 := time.Now().UTC() - t1 = t1.Add(time.Hour * 24) - - err := validate.Field(t1, "gte") - Equal(t, err, nil) - - t2 := time.Now().UTC() - - err = validate.Field(t2, "gte") - NotEqual(t, err, nil) - Equal(t, err.Tag, "gte") - Equal(t, err.Type, reflect.TypeOf(time.Time{})) - - type Test struct { - Now *time.Time `validate:"gte"` - } - s := &Test{ - Now: &t1, - } - - errs := validate.Struct(s) - Equal(t, errs, nil) - - s = &Test{ - Now: &t2, - } - - errs = validate.Struct(s) - NotEqual(t, errs, nil) -} - -func TestIsLt(t *testing.T) { - - myMap := map[string]string{} - err := validate.Field(myMap, "lt=0") - NotEqual(t, err, nil) - - f := 1.23 - err = validate.Field(f, "lt=0") - NotEqual(t, err, nil) - - var ui uint = 5 - err = validate.Field(ui, "lt=0") - NotEqual(t, err, nil) - - i := true - PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") - - t1 := time.Now().UTC() - - err = validate.Field(t1, "lt") - Equal(t, err, nil) - - t2 := time.Now().UTC() - t2 = t2.Add(time.Hour * 24) - - err = validate.Field(t2, "lt") - NotEqual(t, err, nil) - Equal(t, err.Tag, "lt") - - type Test struct { - Now *time.Time `validate:"lt"` - } - - s := &Test{ - Now: &t1, - } - - errs := validate.Struct(s) - Equal(t, errs, nil) - - s = &Test{ - Now: &t2, - } - - errs = validate.Struct(s) - NotEqual(t, errs, nil) -} - -func TestIsLte(t *testing.T) { - - i := true - PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") - - t1 := time.Now().UTC() - - err := validate.Field(t1, "lte") - Equal(t, err, nil) - - t2 := time.Now().UTC() - t2 = t2.Add(time.Hour * 24) - - err = validate.Field(t2, "lte") - NotEqual(t, err, nil) - Equal(t, err.Tag, "lte") - - type Test struct { - Now *time.Time `validate:"lte"` - } - - s := &Test{ - Now: &t1, - } - - errs := validate.Struct(s) - Equal(t, errs, nil) - - s = &Test{ - Now: &t2, - } - - errs = validate.Struct(s) - NotEqual(t, errs, nil) -} - -func TestUrl(t *testing.T) { - - var tests = []struct { - param string - expected bool - }{ - {"http://foo.bar#com", true}, - {"http://foobar.com", true}, - {"https://foobar.com", true}, - {"foobar.com", false}, - {"http://foobar.coffee/", true}, - {"http://foobar.中文网/", true}, - {"http://foobar.org/", true}, - {"http://foobar.org:8080/", true}, - {"ftp://foobar.ru/", true}, - {"http://user:pass@www.foobar.com/", true}, - {"http://127.0.0.1/", true}, - {"http://duckduckgo.com/?q=%2F", true}, - {"http://localhost:3000/", true}, - {"http://foobar.com/?foo=bar#baz=qux", true}, - {"http://foobar.com?foo=bar", true}, - {"http://www.xn--froschgrn-x9a.net/", true}, - {"", false}, - {"xyz://foobar.com", true}, - {"invalid.", false}, - {".com", false}, - {"rtmp://foobar.com", true}, - {"http://www.foo_bar.com/", true}, - {"http://localhost:3000/", true}, - {"http://foobar.com#baz=qux", true}, - {"http://foobar.com/t$-_.+!*\\'(),", true}, - {"http://www.foobar.com/~foobar", true}, - {"http://www.-foobar.com/", true}, - {"http://www.foo---bar.com/", true}, - {"mailto:someone@example.com", true}, - {"irc://irc.server.org/channel", true}, - {"irc://#channel@network", true}, - {"/abs/test/dir", false}, - {"./rel/test/dir", false}, - } - for i, test := range tests { - - err := validate.Field(test.param, "url") - - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d URL failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "url") { - t.Fatalf("Index: %d URL failed Error: %s", i, err) - } - } - } - - i := 1 - PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") -} - -func TestUri(t *testing.T) { - - var tests = []struct { - param string - expected bool - }{ - {"http://foo.bar#com", true}, - {"http://foobar.com", true}, - {"https://foobar.com", true}, - {"foobar.com", false}, - {"http://foobar.coffee/", true}, - {"http://foobar.中文网/", true}, - {"http://foobar.org/", true}, - {"http://foobar.org:8080/", true}, - {"ftp://foobar.ru/", true}, - {"http://user:pass@www.foobar.com/", true}, - {"http://127.0.0.1/", true}, - {"http://duckduckgo.com/?q=%2F", true}, - {"http://localhost:3000/", true}, - {"http://foobar.com/?foo=bar#baz=qux", true}, - {"http://foobar.com?foo=bar", true}, - {"http://www.xn--froschgrn-x9a.net/", true}, - {"", false}, - {"xyz://foobar.com", true}, - {"invalid.", false}, - {".com", false}, - {"rtmp://foobar.com", true}, - {"http://www.foo_bar.com/", true}, - {"http://localhost:3000/", true}, - {"http://foobar.com#baz=qux", true}, - {"http://foobar.com/t$-_.+!*\\'(),", true}, - {"http://www.foobar.com/~foobar", true}, - {"http://www.-foobar.com/", true}, - {"http://www.foo---bar.com/", true}, - {"mailto:someone@example.com", true}, - {"irc://irc.server.org/channel", true}, - {"irc://#channel@network", true}, - {"/abs/test/dir", true}, - {"./rel/test/dir", false}, - } - for i, test := range tests { - - err := validate.Field(test.param, "uri") - - if test.expected == true { - if !IsEqual(t, err, nil) { - t.Fatalf("Index: %d URI failed Error: %s", i, err) - } - } else { - if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uri") { - t.Fatalf("Index: %d URI failed Error: %s", i, err) - } - } - } - - i := 1 - PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") -} - -func TestOrTag(t *testing.T) { - s := "rgba(0,31,255,0.5)" - err := validate.Field(s, "rgb|rgba") - Equal(t, err, nil) - - s = "rgba(0,31,255,0.5)" - err = validate.Field(s, "rgb|rgba|len=18") - Equal(t, err, nil) - - s = "this ain't right" - err = validate.Field(s, "rgb|rgba") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb|rgba") - - s = "this ain't right" - err = validate.Field(s, "rgb|rgba|len=10") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb|rgba|len") - - s = "this is right" - err = validate.Field(s, "rgb|rgba|len=13") - Equal(t, err, nil) - - s = "" - err = validate.Field(s, "omitempty,rgb|rgba") - Equal(t, err, nil) -} - -func TestHsla(t *testing.T) { - - s := "hsla(360,100%,100%,1)" - err := validate.Field(s, "hsla") - Equal(t, err, nil) - - s = "hsla(360,100%,100%,0.5)" - err = validate.Field(s, "hsla") - Equal(t, err, nil) - - s = "hsla(0,0%,0%, 0)" - err = validate.Field(s, "hsla") - Equal(t, err, nil) - - s = "hsl(361,100%,50%,1)" - err = validate.Field(s, "hsla") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsla") - - s = "hsl(361,100%,50%)" - err = validate.Field(s, "hsla") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsla") - - s = "hsla(361,100%,50%)" - err = validate.Field(s, "hsla") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsla") - - s = "hsla(360,101%,50%)" - err = validate.Field(s, "hsla") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsla") - - s = "hsla(360,100%,101%)" - err = validate.Field(s, "hsla") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsla") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "hsla") }, "interface conversion: interface is int, not string") -} - -func TestHsl(t *testing.T) { - - s := "hsl(360,100%,50%)" - err := validate.Field(s, "hsl") - Equal(t, err, nil) - - s = "hsl(0,0%,0%)" - err = validate.Field(s, "hsl") - Equal(t, err, nil) - - s = "hsl(361,100%,50%)" - err = validate.Field(s, "hsl") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsl") - - s = "hsl(361,101%,50%)" - err = validate.Field(s, "hsl") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsl") - - s = "hsl(361,100%,101%)" - err = validate.Field(s, "hsl") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsl") - - s = "hsl(-10,100%,100%)" - err = validate.Field(s, "hsl") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hsl") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "hsl") }, "interface conversion: interface is int, not string") -} - -func TestRgba(t *testing.T) { - - s := "rgba(0,31,255,0.5)" - err := validate.Field(s, "rgba") - Equal(t, err, nil) - - s = "rgba(0,31,255,0.12)" - err = validate.Field(s, "rgba") - Equal(t, err, nil) - - s = "rgba(12%,55%,100%,0.12)" - err = validate.Field(s, "rgba") - Equal(t, err, nil) - - s = "rgba( 0, 31, 255, 0.5)" - err = validate.Field(s, "rgba") - Equal(t, err, nil) - - s = "rgba(12%,55,100%,0.12)" - err = validate.Field(s, "rgba") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgba") - - s = "rgb(0, 31, 255)" - err = validate.Field(s, "rgba") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgba") - - s = "rgb(1,349,275,0.5)" - err = validate.Field(s, "rgba") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgba") - - s = "rgb(01,31,255,0.5)" - err = validate.Field(s, "rgba") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgba") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "rgba") }, "interface conversion: interface is int, not string") -} - -func TestRgb(t *testing.T) { - - s := "rgb(0,31,255)" - err := validate.Field(s, "rgb") - Equal(t, err, nil) - - s = "rgb(0, 31, 255)" - err = validate.Field(s, "rgb") - Equal(t, err, nil) - - s = "rgb(10%, 50%, 100%)" - err = validate.Field(s, "rgb") - Equal(t, err, nil) - - s = "rgb(10%, 50%, 55)" - err = validate.Field(s, "rgb") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb") - - s = "rgb(1,349,275)" - err = validate.Field(s, "rgb") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb") - - s = "rgb(01,31,255)" - err = validate.Field(s, "rgb") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb") - - s = "rgba(0,31,255)" - err = validate.Field(s, "rgb") - NotEqual(t, err, nil) - Equal(t, err.Tag, "rgb") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "rgb") }, "interface conversion: interface is int, not string") -} - -func TestEmail(t *testing.T) { - - s := "test@mail.com" - err := validate.Field(s, "email") - Equal(t, err, nil) - - s = "" - err = validate.Field(s, "email") - NotEqual(t, err, nil) - Equal(t, err.Tag, "email") - - s = "test@email" - err = validate.Field(s, "email") - NotEqual(t, err, nil) - Equal(t, err.Tag, "email") - - s = "test@email." - err = validate.Field(s, "email") - NotEqual(t, err, nil) - Equal(t, err.Tag, "email") - - s = "@email.com" - err = validate.Field(s, "email") - NotEqual(t, err, nil) - Equal(t, err.Tag, "email") - - i := true - PanicMatches(t, func() { validate.Field(i, "email") }, "interface conversion: interface is bool, not string") -} - -func TestHexColor(t *testing.T) { - - s := "#fff" - err := validate.Field(s, "hexcolor") - Equal(t, err, nil) - - s = "#c2c2c2" - err = validate.Field(s, "hexcolor") - Equal(t, err, nil) - - s = "fff" - err = validate.Field(s, "hexcolor") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hexcolor") - - s = "fffFF" - err = validate.Field(s, "hexcolor") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hexcolor") - - i := true - PanicMatches(t, func() { validate.Field(i, "hexcolor") }, "interface conversion: interface is bool, not string") -} - -func TestHexadecimal(t *testing.T) { - - s := "ff0044" - err := validate.Field(s, "hexadecimal") - Equal(t, err, nil) - - s = "abcdefg" - err = validate.Field(s, "hexadecimal") - NotEqual(t, err, nil) - Equal(t, err.Tag, "hexadecimal") - - i := true - PanicMatches(t, func() { validate.Field(i, "hexadecimal") }, "interface conversion: interface is bool, not string") -} - -func TestNumber(t *testing.T) { - - s := "1" - err := validate.Field(s, "number") - Equal(t, err, nil) - - s = "+1" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "-1" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "1.12" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "+1.12" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "-1.12" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "1." - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - s = "1.o" - err = validate.Field(s, "number") - NotEqual(t, err, nil) - Equal(t, err.Tag, "number") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "number") }, "interface conversion: interface is int, not string") -} - -func TestNumeric(t *testing.T) { - - s := "1" - err := validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "+1" - err = validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "-1" - err = validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "1.12" - err = validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "+1.12" - err = validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "-1.12" - err = validate.Field(s, "numeric") - Equal(t, err, nil) - - s = "1." - err = validate.Field(s, "numeric") - NotEqual(t, err, nil) - Equal(t, err.Tag, "numeric") - - s = "1.o" - err = validate.Field(s, "numeric") - NotEqual(t, err, nil) - Equal(t, err.Tag, "numeric") - - i := 1 - PanicMatches(t, func() { validate.Field(i, "numeric") }, "interface conversion: interface is int, not string") -} - -func TestAlphaNumeric(t *testing.T) { - - s := "abcd123" - err := validate.Field(s, "alphanum") - Equal(t, err, nil) - - s = "abc!23" - err = validate.Field(s, "alphanum") - NotEqual(t, err, nil) - Equal(t, err.Tag, "alphanum") - - PanicMatches(t, func() { validate.Field(1, "alphanum") }, "interface conversion: interface is int, not string") -} - -func TestAlpha(t *testing.T) { - - s := "abcd" - err := validate.Field(s, "alpha") - Equal(t, err, nil) - - s = "abc1" - err = validate.Field(s, "alpha") - NotEqual(t, err, nil) - Equal(t, err.Tag, "alpha") - - PanicMatches(t, func() { validate.Field(1, "alpha") }, "interface conversion: interface is int, not string") -} - -func TestFlattening(t *testing.T) { - - tSuccess := &TestString{ - Required: "Required", - Len: "length==10", - Min: "min=1", - Max: "1234567890", - MinMax: "12345", - Lt: "012345678", - Lte: "0123456789", - Gt: "01234567890", - Gte: "0123456789", - OmitEmpty: "", - Sub: &SubTest{ - Test: "1", - }, - SubIgnore: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "1", - }, - Iface: &Impl{ - F: "123", - }, - } - - err1 := validate.Struct(tSuccess).Flatten() - Equal(t, len(err1), 0) - - tFail := &TestString{ - Required: "", - Len: "", - Min: "", - Max: "12345678901", - MinMax: "", - OmitEmpty: "12345678901", - Sub: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "", - }, - Iface: &Impl{ - F: "12", - }, - } - - err2 := validate.Struct(tFail).Flatten() - - // Assert Top Level - NotEqual(t, err2, nil) - - // Assert Fields - AssertMapFieldError(t, err2, "Len", "len") - AssertMapFieldError(t, err2, "Gt", "gt") - AssertMapFieldError(t, err2, "Gte", "gte") - - // Assert Struct Field - AssertMapFieldError(t, err2, "Sub.Test", "required") - - // Assert Anonymous Struct Field - AssertMapFieldError(t, err2, "Anonymous.A", "required") - - // Assert Interface Field - AssertMapFieldError(t, err2, "Iface.F", "len") -} - -func TestStructStringValidation(t *testing.T) { - - validate.SetMaxStructPoolSize(11) - - tSuccess := &TestString{ - Required: "Required", - Len: "length==10", - Min: "min=1", - Max: "1234567890", - MinMax: "12345", - Lt: "012345678", - Lte: "0123456789", - Gt: "01234567890", - Gte: "0123456789", - OmitEmpty: "", - Sub: &SubTest{ - Test: "1", - }, - SubIgnore: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "1", - }, - Iface: &Impl{ - F: "123", - }, - } - - err := validate.Struct(tSuccess) - Equal(t, err, nil) - - tFail := &TestString{ - Required: "", - Len: "", - Min: "", - Max: "12345678901", - MinMax: "", - Lt: "0123456789", - Lte: "01234567890", - Gt: "1", - Gte: "1", - OmitEmpty: "12345678901", - Sub: &SubTest{ - Test: "", - }, - Anonymous: struct { - A string `validate:"required"` - }{ - A: "", - }, - Iface: &Impl{ - F: "12", - }, - } - - err = validate.Struct(tFail) - - // Assert Top Level - NotEqual(t, err, nil) - Equal(t, err.Struct, "TestString") - Equal(t, len(err.Errors), 10) - Equal(t, len(err.StructErrors), 3) - - // Assert Fields - AssertFieldError(t, err, "Required", "required") - AssertFieldError(t, err, "Len", "len") - AssertFieldError(t, err, "Min", "min") - AssertFieldError(t, err, "Max", "max") - AssertFieldError(t, err, "MinMax", "min") - AssertFieldError(t, err, "Gt", "gt") - AssertFieldError(t, err, "Gte", "gte") - AssertFieldError(t, err, "OmitEmpty", "max") - - // Assert Anonymous embedded struct - AssertStruct(t, err, "Anonymous", "") - - // Assert SubTest embedded struct - val := AssertStruct(t, err, "Sub", "SubTest") - Equal(t, len(val.Errors), 1) - Equal(t, len(val.StructErrors), 0) - - AssertFieldError(t, val, "Test", "required") - - errors := err.Error() - NotEqual(t, errors, nil) -} - -func TestStructInt32Validation(t *testing.T) { - - tSuccess := &TestInt32{ - Required: 1, - Len: 10, - Min: 1, - Max: 10, - MinMax: 5, - Lt: 9, - Lte: 10, - Gt: 11, - Gte: 10, - OmitEmpty: 0, - } - - err := validate.Struct(tSuccess) - Equal(t, err, nil) - - tFail := &TestInt32{ - Required: 0, - Len: 11, - Min: -1, - Max: 11, - MinMax: -1, - Lt: 10, - Lte: 11, - Gt: 10, - Gte: 9, - OmitEmpty: 11, - } - - err = validate.Struct(tFail) - - // Assert Top Level - NotEqual(t, err, nil) - Equal(t, err.Struct, "TestInt32") - Equal(t, len(err.Errors), 10) - Equal(t, len(err.StructErrors), 0) - - // Assert Fields - AssertFieldError(t, err, "Required", "required") - AssertFieldError(t, err, "Len", "len") - AssertFieldError(t, err, "Min", "min") - AssertFieldError(t, err, "Max", "max") - AssertFieldError(t, err, "MinMax", "min") - AssertFieldError(t, err, "Lt", "lt") - AssertFieldError(t, err, "Lte", "lte") - AssertFieldError(t, err, "Gt", "gt") - AssertFieldError(t, err, "Gte", "gte") - AssertFieldError(t, err, "OmitEmpty", "max") -} - -func TestStructUint64Validation(t *testing.T) { - - tSuccess := &TestUint64{ - Required: 1, - Len: 10, - Min: 1, - Max: 10, - MinMax: 5, - OmitEmpty: 0, - } - - err := validate.Struct(tSuccess) - Equal(t, err, nil) - - tFail := &TestUint64{ - Required: 0, - Len: 11, - Min: 0, - Max: 11, - MinMax: 0, - OmitEmpty: 11, - } - - err = validate.Struct(tFail) - - // Assert Top Level - NotEqual(t, err, nil) - Equal(t, err.Struct, "TestUint64") - Equal(t, len(err.Errors), 6) - Equal(t, len(err.StructErrors), 0) - - // Assert Fields - AssertFieldError(t, err, "Required", "required") - AssertFieldError(t, err, "Len", "len") - AssertFieldError(t, err, "Min", "min") - AssertFieldError(t, err, "Max", "max") - AssertFieldError(t, err, "MinMax", "min") - AssertFieldError(t, err, "OmitEmpty", "max") -} - -func TestStructFloat64Validation(t *testing.T) { - - tSuccess := &TestFloat64{ - Required: 1, - Len: 10, - Min: 1, - Max: 10, - MinMax: 5, - OmitEmpty: 0, - } - - err := validate.Struct(tSuccess) - Equal(t, err, nil) - - tFail := &TestFloat64{ - Required: 0, - Len: 11, - Min: 0, - Max: 11, - MinMax: 0, - OmitEmpty: 11, - } - - err = validate.Struct(tFail) - - // Assert Top Level - NotEqual(t, err, nil) - Equal(t, err.Struct, "TestFloat64") - Equal(t, len(err.Errors), 6) - Equal(t, len(err.StructErrors), 0) - - // Assert Fields - AssertFieldError(t, err, "Required", "required") - AssertFieldError(t, err, "Len", "len") - AssertFieldError(t, err, "Min", "min") - AssertFieldError(t, err, "Max", "max") - AssertFieldError(t, err, "MinMax", "min") - AssertFieldError(t, err, "OmitEmpty", "max") -} - -func TestStructSliceValidation(t *testing.T) { - - tSuccess := &TestSlice{ - Required: []int{1}, - Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, - Min: []int{1, 2}, - Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, - MinMax: []int{1, 2, 3, 4, 5}, - OmitEmpty: []int{}, - } - - err := validate.Struct(tSuccess) - Equal(t, err, nil) - - tFail := &TestSlice{ - Required: []int{}, - Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, - Min: []int{}, - Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, - MinMax: []int{}, - OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, - } - - err = validate.Struct(tFail) - - // Assert Top Level - NotEqual(t, err, nil) - Equal(t, err.Struct, "TestSlice") - Equal(t, len(err.Errors), 6) - Equal(t, len(err.StructErrors), 0) - - // Assert Fields - AssertFieldError(t, err, "Required", "required") - AssertFieldError(t, err, "Len", "len") - AssertFieldError(t, err, "Min", "min") - AssertFieldError(t, err, "Max", "max") - AssertFieldError(t, err, "MinMax", "min") - AssertFieldError(t, err, "OmitEmpty", "max") -} - -func TestInvalidStruct(t *testing.T) { - s := &SubTest{ - Test: "1", - } - - PanicMatches(t, func() { validate.Struct(s.Test) }, "interface passed for validation is not a struct") -} - -func TestInvalidField(t *testing.T) { - s := &SubTest{ - Test: "1", - } - - PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to fieldWithNameAndValue") -} - -func TestInvalidTagField(t *testing.T) { - s := &SubTest{ - Test: "1", - } - - PanicMatches(t, func() { validate.Field(s.Test, "") }, fmt.Sprintf("Invalid validation tag on field %s", "")) -} - -func TestInvalidValidatorFunction(t *testing.T) { - s := &SubTest{ - Test: "1", - } - - PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) -} - -func TestPoolObjectMaxSizeValidation(t *testing.T) { - // this will ensure that the pool objects are let go - // when the pool is saturated - validate.SetMaxStructPoolSize(0) - - tSuccess := &TestSlice{ - Required: []int{1}, - Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, - Min: []int{1, 2}, - Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, - MinMax: []int{1, 2, 3, 4, 5}, - OmitEmpty: []int{}, - } - - for i := 0; i < 2; i++ { - err := validate.Struct(tSuccess) - Equal(t, err, nil) - } -} +// s = "1.12" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// s = "+1.12" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// s = "-1.12" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// s = "1." +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// s = "1.o" +// err = validate.Field(s, "number") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "number") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "number") }, "interface conversion: interface is int, not string") +// } + +// func TestNumeric(t *testing.T) { + +// s := "1" +// err := validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "+1" +// err = validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "-1" +// err = validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "1.12" +// err = validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "+1.12" +// err = validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "-1.12" +// err = validate.Field(s, "numeric") +// Equal(t, err, nil) + +// s = "1." +// err = validate.Field(s, "numeric") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "numeric") + +// s = "1.o" +// err = validate.Field(s, "numeric") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "numeric") + +// i := 1 +// PanicMatches(t, func() { validate.Field(i, "numeric") }, "interface conversion: interface is int, not string") +// } + +// func TestAlphaNumeric(t *testing.T) { + +// s := "abcd123" +// err := validate.Field(s, "alphanum") +// Equal(t, err, nil) + +// s = "abc!23" +// err = validate.Field(s, "alphanum") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "alphanum") + +// PanicMatches(t, func() { validate.Field(1, "alphanum") }, "interface conversion: interface is int, not string") +// } + +// func TestAlpha(t *testing.T) { + +// s := "abcd" +// err := validate.Field(s, "alpha") +// Equal(t, err, nil) + +// s = "abc1" +// err = validate.Field(s, "alpha") +// NotEqual(t, err, nil) +// Equal(t, err.Tag, "alpha") + +// PanicMatches(t, func() { validate.Field(1, "alpha") }, "interface conversion: interface is int, not string") +// } + +// func TestFlattening(t *testing.T) { + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// err1 := validate.Struct(tSuccess).Flatten() +// Equal(t, len(err1), 0) + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// err2 := validate.Struct(tFail).Flatten() + +// // Assert Top Level +// NotEqual(t, err2, nil) + +// // Assert Fields +// AssertMapFieldError(t, err2, "Len", "len") +// AssertMapFieldError(t, err2, "Gt", "gt") +// AssertMapFieldError(t, err2, "Gte", "gte") + +// // Assert Struct Field +// AssertMapFieldError(t, err2, "Sub.Test", "required") + +// // Assert Anonymous Struct Field +// AssertMapFieldError(t, err2, "Anonymous.A", "required") + +// // Assert Interface Field +// AssertMapFieldError(t, err2, "Iface.F", "len") +// } + +// func TestStructStringValidation(t *testing.T) { + +// validate.SetMaxStructPoolSize(11) + +// tSuccess := &TestString{ +// Required: "Required", +// Len: "length==10", +// Min: "min=1", +// Max: "1234567890", +// MinMax: "12345", +// Lt: "012345678", +// Lte: "0123456789", +// Gt: "01234567890", +// Gte: "0123456789", +// OmitEmpty: "", +// Sub: &SubTest{ +// Test: "1", +// }, +// SubIgnore: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "1", +// }, +// Iface: &Impl{ +// F: "123", +// }, +// } + +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) + +// tFail := &TestString{ +// Required: "", +// Len: "", +// Min: "", +// Max: "12345678901", +// MinMax: "", +// Lt: "0123456789", +// Lte: "01234567890", +// Gt: "1", +// Gte: "1", +// OmitEmpty: "12345678901", +// Sub: &SubTest{ +// Test: "", +// }, +// Anonymous: struct { +// A string `validate:"required"` +// }{ +// A: "", +// }, +// Iface: &Impl{ +// F: "12", +// }, +// } + +// err = validate.Struct(tFail) + +// // Assert Top Level +// NotEqual(t, err, nil) +// Equal(t, err.Struct, "TestString") +// Equal(t, len(err.Errors), 10) +// Equal(t, len(err.StructErrors), 3) + +// // Assert Fields +// AssertFieldError(t, err, "Required", "required") +// AssertFieldError(t, err, "Len", "len") +// AssertFieldError(t, err, "Min", "min") +// AssertFieldError(t, err, "Max", "max") +// AssertFieldError(t, err, "MinMax", "min") +// AssertFieldError(t, err, "Gt", "gt") +// AssertFieldError(t, err, "Gte", "gte") +// AssertFieldError(t, err, "OmitEmpty", "max") + +// // Assert Anonymous embedded struct +// AssertStruct(t, err, "Anonymous", "") + +// // Assert SubTest embedded struct +// val := AssertStruct(t, err, "Sub", "SubTest") +// Equal(t, len(val.Errors), 1) +// Equal(t, len(val.StructErrors), 0) + +// AssertFieldError(t, val, "Test", "required") + +// errors := err.Error() +// NotEqual(t, errors, nil) +// } + +// func TestStructInt32Validation(t *testing.T) { + +// tSuccess := &TestInt32{ +// Required: 1, +// Len: 10, +// Min: 1, +// Max: 10, +// MinMax: 5, +// Lt: 9, +// Lte: 10, +// Gt: 11, +// Gte: 10, +// OmitEmpty: 0, +// } + +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) + +// tFail := &TestInt32{ +// Required: 0, +// Len: 11, +// Min: -1, +// Max: 11, +// MinMax: -1, +// Lt: 10, +// Lte: 11, +// Gt: 10, +// Gte: 9, +// OmitEmpty: 11, +// } + +// err = validate.Struct(tFail) + +// // Assert Top Level +// NotEqual(t, err, nil) +// Equal(t, err.Struct, "TestInt32") +// Equal(t, len(err.Errors), 10) +// Equal(t, len(err.StructErrors), 0) + +// // Assert Fields +// AssertFieldError(t, err, "Required", "required") +// AssertFieldError(t, err, "Len", "len") +// AssertFieldError(t, err, "Min", "min") +// AssertFieldError(t, err, "Max", "max") +// AssertFieldError(t, err, "MinMax", "min") +// AssertFieldError(t, err, "Lt", "lt") +// AssertFieldError(t, err, "Lte", "lte") +// AssertFieldError(t, err, "Gt", "gt") +// AssertFieldError(t, err, "Gte", "gte") +// AssertFieldError(t, err, "OmitEmpty", "max") +// } + +// func TestStructUint64Validation(t *testing.T) { + +// tSuccess := &TestUint64{ +// Required: 1, +// Len: 10, +// Min: 1, +// Max: 10, +// MinMax: 5, +// OmitEmpty: 0, +// } + +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) + +// tFail := &TestUint64{ +// Required: 0, +// Len: 11, +// Min: 0, +// Max: 11, +// MinMax: 0, +// OmitEmpty: 11, +// } + +// err = validate.Struct(tFail) + +// // Assert Top Level +// NotEqual(t, err, nil) +// Equal(t, err.Struct, "TestUint64") +// Equal(t, len(err.Errors), 6) +// Equal(t, len(err.StructErrors), 0) + +// // Assert Fields +// AssertFieldError(t, err, "Required", "required") +// AssertFieldError(t, err, "Len", "len") +// AssertFieldError(t, err, "Min", "min") +// AssertFieldError(t, err, "Max", "max") +// AssertFieldError(t, err, "MinMax", "min") +// AssertFieldError(t, err, "OmitEmpty", "max") +// } + +// func TestStructFloat64Validation(t *testing.T) { + +// tSuccess := &TestFloat64{ +// Required: 1, +// Len: 10, +// Min: 1, +// Max: 10, +// MinMax: 5, +// OmitEmpty: 0, +// } + +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) + +// tFail := &TestFloat64{ +// Required: 0, +// Len: 11, +// Min: 0, +// Max: 11, +// MinMax: 0, +// OmitEmpty: 11, +// } + +// err = validate.Struct(tFail) + +// // Assert Top Level +// NotEqual(t, err, nil) +// Equal(t, err.Struct, "TestFloat64") +// Equal(t, len(err.Errors), 6) +// Equal(t, len(err.StructErrors), 0) + +// // Assert Fields +// AssertFieldError(t, err, "Required", "required") +// AssertFieldError(t, err, "Len", "len") +// AssertFieldError(t, err, "Min", "min") +// AssertFieldError(t, err, "Max", "max") +// AssertFieldError(t, err, "MinMax", "min") +// AssertFieldError(t, err, "OmitEmpty", "max") +// } + +// func TestStructSliceValidation(t *testing.T) { + +// tSuccess := &TestSlice{ +// Required: []int{1}, +// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, +// Min: []int{1, 2}, +// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, +// MinMax: []int{1, 2, 3, 4, 5}, +// OmitEmpty: []int{}, +// } + +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) + +// tFail := &TestSlice{ +// Required: []int{}, +// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, +// Min: []int{}, +// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, +// MinMax: []int{}, +// OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, +// } + +// err = validate.Struct(tFail) + +// // Assert Top Level +// NotEqual(t, err, nil) +// Equal(t, err.Struct, "TestSlice") +// Equal(t, len(err.Errors), 6) +// Equal(t, len(err.StructErrors), 0) + +// // Assert Fields +// AssertFieldError(t, err, "Required", "required") +// AssertFieldError(t, err, "Len", "len") +// AssertFieldError(t, err, "Min", "min") +// AssertFieldError(t, err, "Max", "max") +// AssertFieldError(t, err, "MinMax", "min") +// AssertFieldError(t, err, "OmitEmpty", "max") +// } + +// func TestInvalidStruct(t *testing.T) { +// s := &SubTest{ +// Test: "1", +// } + +// PanicMatches(t, func() { validate.Struct(s.Test) }, "interface passed for validation is not a struct") +// } + +// func TestInvalidField(t *testing.T) { +// s := &SubTest{ +// Test: "1", +// } + +// PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to fieldWithNameAndValue") +// } + +// func TestInvalidTagField(t *testing.T) { +// s := &SubTest{ +// Test: "1", +// } + +// PanicMatches(t, func() { validate.Field(s.Test, "") }, fmt.Sprintf("Invalid validation tag on field %s", "")) +// } + +// func TestInvalidValidatorFunction(t *testing.T) { +// s := &SubTest{ +// Test: "1", +// } + +// PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) +// } + +// func TestPoolObjectMaxSizeValidation(t *testing.T) { +// // this will ensure that the pool objects are let go +// // when the pool is saturated +// validate.SetMaxStructPoolSize(0) + +// tSuccess := &TestSlice{ +// Required: []int{1}, +// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, +// Min: []int{1, 2}, +// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, +// MinMax: []int{1, 2, 3, 4, 5}, +// OmitEmpty: []int{}, +// } + +// for i := 0; i < 2; i++ { +// err := validate.Struct(tSuccess) +// Equal(t, err, nil) +// } +// } From e42d7b683abbe305a4690e571fa11ac538149d9f Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sat, 11 Jul 2015 20:55:21 -0400 Subject: [PATCH 02/20] determined variable values to be passed for #74 --- validator.go | 83 +++++++++++++++++++++++++++++------------------ validator_test.go | 1 + 2 files changed, 52 insertions(+), 32 deletions(-) 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 { From 0fa8dd2a3edd55274f6a7ef2a7394acb6a92879b Mon Sep 17 00:00:00 2001 From: zhing Date: Sun, 12 Jul 2015 13:22:36 +0800 Subject: [PATCH 03/20] =?UTF-8?q?add=20validate:len=3D=3F=20support=20for?= =?UTF-8?q?=20utf-8?= --- baked_in.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baked_in.go b/baked_in.go index ed69eb3..9ccfa9e 100644 --- a/baked_in.go +++ b/baked_in.go @@ -546,7 +546,7 @@ func hasLengthOf(top interface{}, current interface{}, field interface{}, param case reflect.String: p := asInt(param) - return int64(len(st.String())) == p + return int64(len([]rune(st.String()))) == p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) From 9596b89a26073eefd34294b1ed706e15fc92bf67 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sun, 12 Jul 2015 21:57:16 -0400 Subject: [PATCH 04/20] working on traversing field values whether it's a struct field or just a regular field working on a single function to handle both to reduce checking the same values within struct field recursion or a regular field; this will also help reduce code complexity and keep things DRY. --- validator.go | 212 +++++++++++++++++++++++++++++++++------------- validator_test.go | 14 ++- 2 files changed, 163 insertions(+), 63 deletions(-) diff --git a/validator.go b/validator.go index 96039d8..abb4dda 100644 --- a/validator.go +++ b/validator.go @@ -13,10 +13,24 @@ import ( "fmt" "reflect" "strings" + "time" ) const ( - fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" + utf8HexComma = "0x2C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + omitempty = "omitempty" + skipValidationTag = "-" + fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" + invaldField = "Invalid field passed to traverseField" +) + +var ( + timeType = reflect.TypeOf(time.Time{}) + timePtrType = reflect.TypeOf(&time.Time{}) ) // Validate implements the Validate Struct @@ -66,10 +80,10 @@ func (ve ValidationErrors) Error() string { type FieldError struct { Field string Tag string - // Kind reflect.Kind - // Type reflect.Type - // Param string - // Value interface{} + Kind reflect.Kind + Type reflect.Type + Param string + Value interface{} // IsPlaceholderErr bool // IsSliceOrArray bool // IsMap bool @@ -94,7 +108,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors { errs := map[string]*FieldError{} sv := reflect.ValueOf(current) - v.structRecursive(sv, sv, "", errs) + v.tranverseStruct(sv, sv, sv, "", errs) if len(errs) == 0 { return nil @@ -103,9 +117,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors { return errs } -// 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) { +func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors) { if current.Kind() == reflect.Ptr && !current.IsNil() { current = current.Elem() @@ -115,58 +127,136 @@ func (v *Validate) structRecursive(top reflect.Value, current reflect.Value, err panic("value passed for validation is not a struct") } - // errs[errPrefix+"Name"] = &FieldError{Field: "Name", Tag: "required"} + typ := current.Type() + errPrefix += typ.Name() + "." + numFields := current.NumField() - // if depth < 3 { - // v.structRecursive(top, current, errs) - // } + for i := 0; i < numFields; i++ { + v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, typ.Field(i).Tag.Get(v.config.TagName)) + } } -// // Struct validates a struct, even it's nested structs, and returns a struct containing the errors -// // 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 { - -// // var err *FieldError -// errs := map[string]*FieldError{} -// errchan := make(chan *FieldError) -// done := make(chan bool) -// // wg := &sync.WaitGroup{} - -// go v.structRecursive(s, s, s, 0, errchan, done) - -// LOOP: -// for { -// select { -// case err := <-errchan: -// errs[err.Field] = err -// // fmt.Println(err) -// case <-done: -// // fmt.Println("All Done") -// break LOOP -// } -// } - -// return errs -// } - -// func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}, depth int, errs chan *FieldError, done chan bool) { - -// errs <- &FieldError{Field: "Name"} - -// if depth < 1 { -// // wg.Add(1) -// v.structRecursive(s, s, s, depth+1, errs, done) -// } - -// // wg.Wait() - -// if depth == 0 { -// // wg.Wait() -// done <- true -// // return -// } else { -// // wg.Done() -// } -// } +func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string) { + + if tag == skipValidationTag { + return + } + + kind := current.Kind() + + if kind == reflect.Ptr && !current.IsNil() { + current = current.Elem() + kind = current.Kind() + } + + typ := current.Type() + + // this also allows for tags 'required' and 'omitempty' to be used on + // nested struct fields because when len(tags) > 0 below and the value is nil + // then required failes and we check for omitempty just before that + if (kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil() { + + if strings.Contains(tag, omitempty) { + return + } + + tags := strings.Split(tag, tagSeparator) + + if len(tags) > 0 { + + var param string + vals := strings.SplitN(tags[0], tagKeySeparator, 2) + + if len(vals) > 1 { + param = vals[1] + } + + errs[errPrefix+typ.Name()] = &FieldError{ + Field: typ.Name(), + Tag: vals[0], + Param: param, + Value: current.Interface(), + Kind: kind, + Type: typ, + } + + return + } + } + + switch kind { + + case reflect.Invalid: + panic(invaldField) + case reflect.Struct, reflect.Interface: + + if kind == reflect.Interface { + + current = current.Elem() + kind = current.Kind() + + if kind == reflect.Ptr && !current.IsNil() { + current = current.Elem() + kind = current.Kind() + } + + if kind != reflect.Struct { + goto FALLTHROUGH + } + } + + if typ != timeType && typ != timePtrType { + + if isStructField { + + // required passed validationa above so stop here + // if only validating the structs existance. + if strings.Contains(tag, structOnlyTag) { + return + } + + v.tranverseStruct(topStruct, current, current, errPrefix, errs) + return + } + + panic(invaldField) + } + FALLTHROUGH: + fallthrough + default: + if len(tag) == 0 { + return + } + } + + // for _, t := range strings.Split(tag, tagSeparator) { + + // if t == diveTag { + + // cField.dive = true + // cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") + // break + // } + + // orVals := strings.Split(t, orSeparator) + // cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} + // cField.tags = append(cField.tags, cTag) + + // for i, val := range orVals { + // vals := strings.SplitN(val, tagKeySeparator, 2) + + // key := strings.TrimSpace(vals[0]) + + // if len(key) == 0 { + // panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + // } + + // param := "" + // if len(vals) > 1 { + // param = strings.Replace(vals[1], utf8HexComma, ",", -1) + // } + + // cTag.keyVals[i] = []string{key, param} + // } + // } +} diff --git a/validator_test.go b/validator_test.go index b31c29d..a236e43 100644 --- a/validator_test.go +++ b/validator_test.go @@ -204,11 +204,21 @@ func PanicMatchesSkip(t *testing.T, skip int, fn func(), matches string) { func TestValidation(t *testing.T) { - type Test struct { + type Inner struct { Name string } - tst := &Test{Name: "Dean"} + type Test struct { + // Name string `validate:"required"` + Inner *Inner `validate:"required"` + } + + inner := &Inner{ + Name: "", + } + + // tst := &Test{Name: "Dean"} + tst := &Test{Inner: inner} errs := validate.Struct(tst) From 89e9d9731d58be7eff9934ac79f417ec63b79287 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Mon, 13 Jul 2015 14:21:06 -0400 Subject: [PATCH 05/20] update to use utf8.RuneCountInString for string length comparisons updated baked in functions: hasLengthOf isGt isGte isLt isLte to use utf8.RuneCountInString for string length comparisons, not counting multi-bye characters but runes in string length comparisons. --- baked_in.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/baked_in.go b/baked_in.go index 701757c..0e394bd 100644 --- a/baked_in.go +++ b/baked_in.go @@ -592,7 +592,7 @@ func isGte(top interface{}, current interface{}, field interface{}, param string case reflect.String: p := asInt(param) - return int64(len(st.String())) >= p + return int64(utf8.RuneCountInString(st.String())) >= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) @@ -637,7 +637,7 @@ func isGt(top interface{}, current interface{}, field interface{}, param string) case reflect.String: p := asInt(param) - return int64(len(st.String())) > p + return int64(utf8.RuneCountInString(st.String())) > p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) @@ -681,7 +681,7 @@ func hasLengthOf(top interface{}, current interface{}, field interface{}, param case reflect.String: p := asInt(param) - return int64(len([]rune(st.String()))) == p + return int64(utf8.RuneCountInString(st.String())) == p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) @@ -875,7 +875,7 @@ func isLte(top interface{}, current interface{}, field interface{}, param string case reflect.String: p := asInt(param) - return int64(len(st.String())) <= p + return int64(utf8.RuneCountInString(st.String())) <= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) @@ -920,7 +920,7 @@ func isLt(top interface{}, current interface{}, field interface{}, param string) case reflect.String: p := asInt(param) - return int64(len(st.String())) < p + return int64(utf8.RuneCountInString(st.String())) < p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) From 7af3fb7c1fad5e7bb7430f4f7112286a1ccff50c Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 14 Jul 2015 13:36:55 -0400 Subject: [PATCH 06/20] initial validation logic reworked initial function layouts and validation completed, still need to rework all of the baked in functions, add map and array traversal and add back original test cases. NOTE: a far more pragmatic validation tests will be added, but not until it has been proven stable with at least the old tests. --- baked_in.go | 1196 ++++++++++++++++++++++----------------------- validator.go | 158 ++++-- validator_test.go | 15 +- 3 files changed, 730 insertions(+), 639 deletions(-) diff --git a/baked_in.go b/baked_in.go index 0e394bd..b9d2286 100644 --- a/baked_in.go +++ b/baked_in.go @@ -2,10 +2,8 @@ package validator import ( "fmt" - "net/url" "reflect" "strconv" - "strings" "time" "unicode/utf8" ) @@ -14,612 +12,610 @@ import ( // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. var BakedInValidators = map[string]Func{ - "required": hasValue, - "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, - "eq": isEq, - "ne": isNe, - "lt": isLt, - "lte": isLte, - "gt": isGt, - "gte": isGte, - "eqfield": isEqField, - "nefield": isNeField, - "gtefield": isGteField, - "gtfield": isGtField, - "ltefield": isLteField, - "ltfield": isLtField, - "alpha": isAlpha, - "alphanum": isAlphanum, - "numeric": isNumeric, - "number": isNumber, - "hexadecimal": isHexadecimal, - "hexcolor": isHexcolor, - "rgb": isRgb, - "rgba": isRgba, - "hsl": isHsl, - "hsla": isHsla, - "email": isEmail, - "url": isURL, - "uri": isURI, - "base64": isBase64, - "contains": contains, - "containsany": containsAny, - "containsrune": containsRune, - "excludes": excludes, - "excludesall": excludesAll, - "excludesrune": excludesRune, - "isbn": isISBN, - "isbn10": isISBN10, - "isbn13": isISBN13, - "uuid": isUUID, - "uuid3": isUUID3, - "uuid4": isUUID4, - "uuid5": isUUID5, - "ascii": isASCII, - "printascii": isPrintableASCII, - "multibyte": hasMultiByteCharacter, - "datauri": isDataURI, - "latitude": isLatitude, - "longitude": isLongitude, - "ssn": isSSN, -} + "required": hasValue, + // "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + // "eq": isEq, + // "ne": isNe, + // "lt": isLt, + "lte": isLte, + // "gt": isGt, + "gte": isGte, + // "eqfield": isEqField, + // "nefield": isNeField, + // "gtefield": isGteField, + // "gtfield": isGtField, + // "ltefield": isLteField, + // "ltfield": isLtField, + // "alpha": isAlpha, + // "alphanum": isAlphanum, + // "numeric": isNumeric, + // "number": isNumber, + // "hexadecimal": isHexadecimal, + // "hexcolor": isHexcolor, + // "rgb": isRgb, + // "rgba": isRgba, + // "hsl": isHsl, + // "hsla": isHsla, + // "email": isEmail, + // "url": isURL, + // "uri": isURI, + // "base64": isBase64, + // "contains": contains, + // "containsany": containsAny, + // "containsrune": containsRune, + // "excludes": excludes, + // "excludesall": excludesAll, + // "excludesrune": excludesRune, + // "isbn": isISBN, + // "isbn10": isISBN10, + // "isbn13": isISBN13, + // "uuid": isUUID, + // "uuid3": isUUID3, + // "uuid4": isUUID4, + // "uuid5": isUUID5, + // "ascii": isASCII, + // "printascii": isPrintableASCII, + // "multibyte": hasMultiByteCharacter, + // "datauri": isDataURI, + // "latitude": isLatitude, + // "longitude": isLongitude, + // "ssn": isSSN, +} + +// func isSSN(top interface{}, current interface{}, field interface{}, param string) bool { + +// if len(field.(string)) != 11 { +// return false +// } + +// return matchesRegex(sSNRegex, field) +// } + +// func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(longitudeRegex, field) +// } + +// func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(latitudeRegex, field) +// } + +// func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool { + +// uri := strings.SplitN(field.(string), ",", 2) + +// if len(uri) != 2 { +// return false +// } + +// if !matchesRegex(dataURIRegex, uri[0]) { +// return false +// } + +// return isBase64(top, current, uri[1], param) +// } + +// func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool { + +// if len(field.(string)) == 0 { +// return true +// } + +// return matchesRegex(multibyteRegex, field) +// } + +// func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(printableASCIIRegex, field) +// } + +// func isASCII(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(aSCIIRegex, field) +// } + +// func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(uUID5Regex, field) +// } + +// func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(uUID4Regex, field) +// } + +// func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(uUID3Regex, field) +// } + +// func isUUID(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(uUIDRegex, field) +// } -func isSSN(top interface{}, current interface{}, field interface{}, param string) bool { +// func isISBN(top interface{}, current interface{}, field interface{}, param string) bool { +// return isISBN10(top, current, field, param) || isISBN13(top, current, field, param) +// } - if len(field.(string)) != 11 { - return false - } +// func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(sSNRegex, field) -} +// s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4) -func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(longitudeRegex, field) -} +// if !matchesRegex(iSBN13Regex, s) { +// return false +// } -func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(latitudeRegex, field) -} +// var checksum int32 +// var i int32 -func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool { +// factor := []int32{1, 3} - uri := strings.SplitN(field.(string), ",", 2) +// for i = 0; i < 12; i++ { +// checksum += factor[i%2] * int32(s[i]-'0') +// } - if len(uri) != 2 { - return false - } +// if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 { +// return true +// } - if !matchesRegex(dataURIRegex, uri[0]) { - return false - } +// return false +// } - return isBase64(top, current, uri[1], param) -} +// func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool { -func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool { +// s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3) - if len(field.(string)) == 0 { - return true - } +// if !matchesRegex(iSBN10Regex, s) { +// return false +// } - return matchesRegex(multibyteRegex, field) -} +// var checksum int32 +// var i int32 -func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(printableASCIIRegex, field) -} +// for i = 0; i < 9; i++ { +// checksum += (i + 1) * int32(s[i]-'0') +// } -func isASCII(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(aSCIIRegex, field) -} +// if s[9] == 'X' { +// checksum += 10 * 10 +// } else { +// checksum += 10 * int32(s[9]-'0') +// } -func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(uUID5Regex, field) -} +// if checksum%11 == 0 { +// return true +// } -func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(uUID4Regex, field) -} +// return false +// } -func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(uUID3Regex, field) -} +// func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool { +// return !containsRune(top, current, field, param) +// } -func isUUID(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(uUIDRegex, field) -} +// func excludesAll(top interface{}, current interface{}, field interface{}, param string) bool { +// return !containsAny(top, current, field, param) +// } -func isISBN(top interface{}, current interface{}, field interface{}, param string) bool { - return isISBN10(top, current, field, param) || isISBN13(top, current, field, param) -} +// func excludes(top interface{}, current interface{}, field interface{}, param string) bool { +// return !contains(top, current, field, param) +// } -func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool { +// func containsRune(top interface{}, current interface{}, field interface{}, param string) bool { +// r, _ := utf8.DecodeRuneInString(param) - s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4) +// return strings.ContainsRune(field.(string), r) +// } - if !matchesRegex(iSBN13Regex, s) { - return false - } +// func containsAny(top interface{}, current interface{}, field interface{}, param string) bool { +// return strings.ContainsAny(field.(string), param) +// } - var checksum int32 - var i int32 +// func contains(top interface{}, current interface{}, field interface{}, param string) bool { +// return strings.Contains(field.(string), param) +// } - factor := []int32{1, 3} +// func isNeField(top interface{}, current interface{}, field interface{}, param string) bool { +// return !isEqField(top, current, field, param) +// } - for i = 0; i < 12; i++ { - checksum += factor[i%2] * int32(s[i]-'0') - } +// func isNe(top interface{}, current interface{}, field interface{}, param string) bool { +// return !isEq(top, current, field, param) +// } - if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 { - return true - } +// func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { - return false -} +// if current == nil { +// panic("struct not passed for cross validation") +// } -func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool { +// currentVal := reflect.ValueOf(current) - s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3) +// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { +// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) +// } - if !matchesRegex(iSBN10Regex, s) { - return false - } +// var currentFielVal reflect.Value - var checksum int32 - var i int32 +// switch currentVal.Kind() { - for i = 0; i < 9; i++ { - checksum += (i + 1) * int32(s[i]-'0') - } +// case reflect.Struct: - if s[9] == 'X' { - checksum += 10 * 10 - } else { - checksum += 10 * int32(s[9]-'0') - } +// if currentVal.Type() == reflect.TypeOf(time.Time{}) { +// currentFielVal = currentVal +// break +// } - if checksum%11 == 0 { - return true - } +// f := currentVal.FieldByName(param) - return false -} +// if f.Kind() == reflect.Invalid { +// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) +// } -func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool { - return !containsRune(top, current, field, param) -} +// currentFielVal = f -func excludesAll(top interface{}, current interface{}, field interface{}, param string) bool { - return !containsAny(top, current, field, param) -} +// default: -func excludes(top interface{}, current interface{}, field interface{}, param string) bool { - return !contains(top, current, field, param) -} +// currentFielVal = currentVal +// } -func containsRune(top interface{}, current interface{}, field interface{}, param string) bool { - r, _ := utf8.DecodeRuneInString(param) +// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - return strings.ContainsRune(field.(string), r) -} +// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) +// } -func containsAny(top interface{}, current interface{}, field interface{}, param string) bool { - return strings.ContainsAny(field.(string), param) -} +// fv := reflect.ValueOf(field) -func contains(top interface{}, current interface{}, field interface{}, param string) bool { - return strings.Contains(field.(string), param) -} +// switch fv.Kind() { -func isNeField(top interface{}, current interface{}, field interface{}, param string) bool { - return !isEqField(top, current, field, param) -} +// case reflect.String: +// return fv.String() == currentFielVal.String() +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -func isNe(top interface{}, current interface{}, field interface{}, param string) bool { - return !isEq(top, current, field, param) -} +// return fv.Int() == currentFielVal.Int() -func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - if current == nil { - panic("struct not passed for cross validation") - } +// return fv.Uint() == currentFielVal.Uint() - currentVal := reflect.ValueOf(current) +// case reflect.Float32, reflect.Float64: - if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { - currentVal = reflect.ValueOf(currentVal.Elem().Interface()) - } +// return fv.Float() == currentFielVal.Float() +// case reflect.Slice, reflect.Map, reflect.Array: - var currentFielVal reflect.Value +// return int64(fv.Len()) == int64(currentFielVal.Len()) +// case reflect.Struct: - switch currentVal.Kind() { +// if fv.Type() == reflect.TypeOf(time.Time{}) { - case reflect.Struct: +// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { +// panic("Bad Top Level field type") +// } - if currentVal.Type() == reflect.TypeOf(time.Time{}) { - currentFielVal = currentVal - break - } +// t := currentFielVal.Interface().(time.Time) +// fieldTime := field.(time.Time) - f := currentVal.FieldByName(param) +// return fieldTime.Equal(t) +// } +// } - if f.Kind() == reflect.Invalid { - panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) - } +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - currentFielVal = f +// func isEq(top interface{}, current interface{}, field interface{}, param string) bool { - default: +// st := reflect.ValueOf(field) - currentFielVal = currentVal - } +// switch st.Kind() { - if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { +// case reflect.String: - currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) - } +// return st.String() == param - fv := reflect.ValueOf(field) +// case reflect.Slice, reflect.Map, reflect.Array: +// p := asInt(param) - switch fv.Kind() { +// return int64(st.Len()) == p - case reflect.String: - return fv.String() == currentFielVal.String() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// p := asInt(param) - return fv.Int() == currentFielVal.Int() +// return st.Int() == p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// p := asUint(param) - return fv.Uint() == currentFielVal.Uint() +// return st.Uint() == p - case reflect.Float32, reflect.Float64: +// case reflect.Float32, reflect.Float64: +// p := asFloat(param) - return fv.Float() == currentFielVal.Float() - case reflect.Slice, reflect.Map, reflect.Array: +// return st.Float() == p +// } - return int64(fv.Len()) == int64(currentFielVal.Len()) - case reflect.Struct: +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - if fv.Type() == reflect.TypeOf(time.Time{}) { +// func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(base64Regex, field) +// } - if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { - panic("Bad Top Level field type") - } +// func isURI(top interface{}, current interface{}, field interface{}, param string) bool { - t := currentFielVal.Interface().(time.Time) - fieldTime := field.(time.Time) +// st := reflect.ValueOf(field) - return fieldTime.Equal(t) - } - } +// switch st.Kind() { - panic(fmt.Sprintf("Bad field type %T", field)) -} +// case reflect.String: +// _, err := url.ParseRequestURI(field.(string)) -func isEq(top interface{}, current interface{}, field interface{}, param string) bool { +// return err == nil +// } - st := reflect.ValueOf(field) +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - switch st.Kind() { +// func isURL(top interface{}, current interface{}, field interface{}, param string) bool { +// st := reflect.ValueOf(field) - case reflect.String: +// switch st.Kind() { - return st.String() == param +// case reflect.String: +// url, err := url.ParseRequestURI(field.(string)) - case reflect.Slice, reflect.Map, reflect.Array: - p := asInt(param) +// if err != nil { +// return false +// } - return int64(st.Len()) == p +// if len(url.Scheme) == 0 { +// return false +// } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p := asInt(param) +// return err == nil +// } - return st.Int() == p +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p := asUint(param) +// func isEmail(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(emailRegex, field) +// } - return st.Uint() == p +// func isHsla(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(hslaRegex, field) +// } - case reflect.Float32, reflect.Float64: - p := asFloat(param) +// func isHsl(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(hslRegex, field) +// } - return st.Float() == p - } +// func isRgba(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(rgbaRegex, field) +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} +// func isRgb(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(rgbRegex, field) +// } -func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(base64Regex, field) -} +// func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(hexcolorRegex, field) +// } -func isURI(top interface{}, current interface{}, field interface{}, param string) bool { +// func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(hexadecimalRegex, field) +// } - st := reflect.ValueOf(field) +// func isNumber(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(numberRegex, field) +// } - switch st.Kind() { +// func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(numericRegex, field) +// } - case reflect.String: - _, err := url.ParseRequestURI(field.(string)) +// func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(alphaNumericRegex, field) +// } - return err == nil - } +// func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool { +// return matchesRegex(alphaRegex, field) +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} - -func isURL(top interface{}, current interface{}, field interface{}, param string) bool { - st := reflect.ValueOf(field) +func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { - switch st.Kind() { - - case reflect.String: - url, err := url.ParseRequestURI(field.(string)) + // st := reflect.ValueOf(field) - if err != nil { - return false - } - - if len(url.Scheme) == 0 { - return false - } - - return err == nil - } - - panic(fmt.Sprintf("Bad field type %T", field)) -} - -func isEmail(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(emailRegex, field) -} - -func isHsla(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(hslaRegex, field) -} - -func isHsl(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(hslRegex, field) -} - -func isRgba(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(rgbaRegex, field) -} - -func isRgb(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(rgbRegex, field) -} - -func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(hexcolorRegex, field) -} - -func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(hexadecimalRegex, field) -} - -func isNumber(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(numberRegex, field) -} - -func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(numericRegex, field) -} - -func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(alphaNumericRegex, field) -} - -func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool { - return matchesRegex(alphaRegex, field) -} - -func hasValue(top interface{}, current interface{}, field interface{}, param string) bool { - - st := reflect.ValueOf(field) - - switch st.Kind() { + switch fieldKind { case reflect.Slice, reflect.Map, reflect.Array: - return field != nil && int64(st.Len()) > 0 + return !field.IsNil() && int64(field.Len()) > 0 default: - return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() + return field.IsValid() && field != reflect.Zero(fieldType).Interface() } } -func isGteField(top interface{}, current interface{}, field interface{}, param string) bool { - - if current == nil { - panic("struct not passed for cross validation") - } +// func isGteField(top interface{}, current interface{}, field interface{}, param string) bool { - currentVal := reflect.ValueOf(current) +// if current == nil { +// panic("struct not passed for cross validation") +// } - if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { - currentVal = reflect.ValueOf(currentVal.Elem().Interface()) - } +// currentVal := reflect.ValueOf(current) - var currentFielVal reflect.Value +// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { +// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) +// } - switch currentVal.Kind() { +// var currentFielVal reflect.Value - case reflect.Struct: +// switch currentVal.Kind() { - if currentVal.Type() == reflect.TypeOf(time.Time{}) { - currentFielVal = currentVal - break - } +// case reflect.Struct: - f := currentVal.FieldByName(param) +// if currentVal.Type() == reflect.TypeOf(time.Time{}) { +// currentFielVal = currentVal +// break +// } - if f.Kind() == reflect.Invalid { - panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) - } +// f := currentVal.FieldByName(param) - currentFielVal = f +// if f.Kind() == reflect.Invalid { +// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) +// } - default: +// currentFielVal = f - currentFielVal = currentVal - } +// default: - if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { +// currentFielVal = currentVal +// } - currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) - } +// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - fv := reflect.ValueOf(field) +// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) +// } - switch fv.Kind() { +// fv := reflect.ValueOf(field) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// switch fv.Kind() { - return fv.Int() >= currentFielVal.Int() +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// return fv.Int() >= currentFielVal.Int() - return fv.Uint() >= currentFielVal.Uint() +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - case reflect.Float32, reflect.Float64: +// return fv.Uint() >= currentFielVal.Uint() - return fv.Float() >= currentFielVal.Float() +// case reflect.Float32, reflect.Float64: - case reflect.Struct: +// return fv.Float() >= currentFielVal.Float() - if fv.Type() == reflect.TypeOf(time.Time{}) { +// case reflect.Struct: - if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { - panic("Bad Top Level field type") - } +// if fv.Type() == reflect.TypeOf(time.Time{}) { - t := currentFielVal.Interface().(time.Time) - fieldTime := field.(time.Time) +// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { +// panic("Bad Top Level field type") +// } - return fieldTime.After(t) || fieldTime.Equal(t) - } - } +// t := currentFielVal.Interface().(time.Time) +// fieldTime := field.(time.Time) - panic(fmt.Sprintf("Bad field type %T", field)) -} +// return fieldTime.After(t) || fieldTime.Equal(t) +// } +// } -func isGtField(top interface{}, current interface{}, field interface{}, param string) bool { +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - if current == nil { - panic("struct not passed for cross validation") - } +// func isGtField(top interface{}, current interface{}, field interface{}, param string) bool { - currentVal := reflect.ValueOf(current) +// if current == nil { +// panic("struct not passed for cross validation") +// } - if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { - currentVal = reflect.ValueOf(currentVal.Elem().Interface()) - } +// currentVal := reflect.ValueOf(current) - var currentFielVal reflect.Value +// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { +// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) +// } - switch currentVal.Kind() { +// var currentFielVal reflect.Value - case reflect.Struct: +// switch currentVal.Kind() { - if currentVal.Type() == reflect.TypeOf(time.Time{}) { - currentFielVal = currentVal - break - } +// case reflect.Struct: - f := currentVal.FieldByName(param) +// if currentVal.Type() == reflect.TypeOf(time.Time{}) { +// currentFielVal = currentVal +// break +// } - if f.Kind() == reflect.Invalid { - panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) - } +// f := currentVal.FieldByName(param) - currentFielVal = f +// if f.Kind() == reflect.Invalid { +// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) +// } - default: +// currentFielVal = f - currentFielVal = currentVal - } +// default: - if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { +// currentFielVal = currentVal +// } - currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) - } +// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - fv := reflect.ValueOf(field) +// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) +// } - switch fv.Kind() { +// fv := reflect.ValueOf(field) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// switch fv.Kind() { - return fv.Int() > currentFielVal.Int() +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// return fv.Int() > currentFielVal.Int() - return fv.Uint() > currentFielVal.Uint() +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - case reflect.Float32, reflect.Float64: +// return fv.Uint() > currentFielVal.Uint() - return fv.Float() > currentFielVal.Float() +// case reflect.Float32, reflect.Float64: - case reflect.Struct: +// return fv.Float() > currentFielVal.Float() - if fv.Type() == reflect.TypeOf(time.Time{}) { +// case reflect.Struct: - if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { - panic("Bad Top Level field type") - } +// if fv.Type() == reflect.TypeOf(time.Time{}) { - t := currentFielVal.Interface().(time.Time) - fieldTime := field.(time.Time) +// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { +// panic("Bad Top Level field type") +// } - return fieldTime.After(t) - } - } +// t := currentFielVal.Interface().(time.Time) +// fieldTime := field.(time.Time) - panic(fmt.Sprintf("Bad field type %T", field)) -} +// return fieldTime.After(t) +// } +// } -func isGte(top interface{}, current interface{}, field interface{}, param string) bool { +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - st := reflect.ValueOf(field) +func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { - switch st.Kind() { + switch fieldKind { case reflect.String: p := asInt(param) - return int64(utf8.RuneCountInString(st.String())) >= p + return int64(utf8.RuneCountInString(field.String())) >= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) - return int64(st.Len()) >= p + return int64(field.Len()) >= p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) - return st.Int() >= p + return field.Int() >= p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) - return st.Uint() >= p + return field.Uint() >= p case reflect.Float32, reflect.Float64: p := asFloat(param) - return st.Float() >= p + return field.Float() >= p case reflect.Struct: - if st.Type() == reflect.TypeOf(time.Time{}) { + if fieldType == timeType || fieldType == timePtrType { now := time.Now().UTC() - t := field.(time.Time) + t := field.Interface().(time.Time) return t.After(now) || t.Equal(now) } @@ -628,281 +624,279 @@ func isGte(top interface{}, current interface{}, field interface{}, param string panic(fmt.Sprintf("Bad field type %T", field)) } -func isGt(top interface{}, current interface{}, field interface{}, param string) bool { +// func isGt(top interface{}, current interface{}, field interface{}, param string) bool { - st := reflect.ValueOf(field) +// st := reflect.ValueOf(field) - switch st.Kind() { +// switch st.Kind() { - case reflect.String: - p := asInt(param) +// case reflect.String: +// p := asInt(param) - return int64(utf8.RuneCountInString(st.String())) > p +// return int64(utf8.RuneCountInString(st.String())) > p - case reflect.Slice, reflect.Map, reflect.Array: - p := asInt(param) +// case reflect.Slice, reflect.Map, reflect.Array: +// p := asInt(param) - return int64(st.Len()) > p +// return int64(st.Len()) > p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p := asInt(param) +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// p := asInt(param) - return st.Int() > p +// return st.Int() > p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p := asUint(param) +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// p := asUint(param) - return st.Uint() > p +// return st.Uint() > p - case reflect.Float32, reflect.Float64: - p := asFloat(param) +// case reflect.Float32, reflect.Float64: +// p := asFloat(param) - return st.Float() > p - case reflect.Struct: +// return st.Float() > p +// case reflect.Struct: - if st.Type() == reflect.TypeOf(time.Time{}) { +// if st.Type() == reflect.TypeOf(time.Time{}) { - return field.(time.Time).After(time.Now().UTC()) - } - } +// return field.(time.Time).After(time.Now().UTC()) +// } +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} +// panic(fmt.Sprintf("Bad field type %T", field)) +// } -// length tests whether a variable's length is equal to a given -// value. For strings it tests the number of characters whereas -// for maps and slices it tests the number of items. -func hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool { +// // length tests whether a variable's length is equal to a given +// // value. For strings it tests the number of characters whereas +// // for maps and slices it tests the number of items. +// func hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool { - st := reflect.ValueOf(field) +// st := reflect.ValueOf(field) - switch st.Kind() { +// switch st.Kind() { - case reflect.String: - p := asInt(param) +// case reflect.String: +// p := asInt(param) - return int64(utf8.RuneCountInString(st.String())) == p +// return int64(utf8.RuneCountInString(st.String())) == p - case reflect.Slice, reflect.Map, reflect.Array: - p := asInt(param) +// case reflect.Slice, reflect.Map, reflect.Array: +// p := asInt(param) - return int64(st.Len()) == p +// return int64(st.Len()) == p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p := asInt(param) +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// p := asInt(param) - return st.Int() == p +// return st.Int() == p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p := asUint(param) +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// p := asUint(param) - return st.Uint() == p +// return st.Uint() == p - case reflect.Float32, reflect.Float64: - p := asFloat(param) +// case reflect.Float32, reflect.Float64: +// p := asFloat(param) - return st.Float() == p - } +// return st.Float() == p +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} +// panic(fmt.Sprintf("Bad field type %T", field)) +// } // min tests whether a variable value is larger or equal to a given // number. For number types, it's a simple lesser-than test; for // strings it tests the number of characters whereas for maps // and slices it tests the number of items. -func hasMinOf(top interface{}, current interface{}, field interface{}, param string) bool { +func hasMinOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { - return isGte(top, current, field, param) + return isGte(topStruct, currentStruct, field, fieldType, fieldKind, param) } -func isLteField(top interface{}, current interface{}, field interface{}, param string) bool { +// func isLteField(top interface{}, current interface{}, field interface{}, param string) bool { - if current == nil { - panic("struct not passed for cross validation") - } +// if current == nil { +// panic("struct not passed for cross validation") +// } - currentVal := reflect.ValueOf(current) +// currentVal := reflect.ValueOf(current) - if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { - currentVal = reflect.ValueOf(currentVal.Elem().Interface()) - } +// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { +// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) +// } - var currentFielVal reflect.Value +// var currentFielVal reflect.Value - switch currentVal.Kind() { +// switch currentVal.Kind() { - case reflect.Struct: +// case reflect.Struct: - if currentVal.Type() == reflect.TypeOf(time.Time{}) { - currentFielVal = currentVal - break - } +// if currentVal.Type() == reflect.TypeOf(time.Time{}) { +// currentFielVal = currentVal +// break +// } - f := currentVal.FieldByName(param) +// f := currentVal.FieldByName(param) - if f.Kind() == reflect.Invalid { - panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) - } +// if f.Kind() == reflect.Invalid { +// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) +// } - currentFielVal = f +// currentFielVal = f - default: +// default: - currentFielVal = currentVal - } +// currentFielVal = currentVal +// } - if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { +// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) - } +// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) +// } - fv := reflect.ValueOf(field) +// fv := reflect.ValueOf(field) - switch fv.Kind() { +// switch fv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return fv.Int() <= currentFielVal.Int() +// return fv.Int() <= currentFielVal.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return fv.Uint() <= currentFielVal.Uint() +// return fv.Uint() <= currentFielVal.Uint() - case reflect.Float32, reflect.Float64: +// case reflect.Float32, reflect.Float64: - return fv.Float() <= currentFielVal.Float() +// return fv.Float() <= currentFielVal.Float() - case reflect.Struct: +// case reflect.Struct: - if fv.Type() == reflect.TypeOf(time.Time{}) { +// if fv.Type() == reflect.TypeOf(time.Time{}) { - if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { - panic("Bad Top Level field type") - } +// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { +// panic("Bad Top Level field type") +// } - t := currentFielVal.Interface().(time.Time) - fieldTime := field.(time.Time) +// t := currentFielVal.Interface().(time.Time) +// fieldTime := field.(time.Time) - return fieldTime.Before(t) || fieldTime.Equal(t) - } - } +// return fieldTime.Before(t) || fieldTime.Equal(t) +// } +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} +// panic(fmt.Sprintf("Bad field type %T", field)) +// } -func isLtField(top interface{}, current interface{}, field interface{}, param string) bool { +// func isLtField(top interface{}, current interface{}, field interface{}, param string) bool { - if current == nil { - panic("struct not passed for cross validation") - } +// if current == nil { +// panic("struct not passed for cross validation") +// } - currentVal := reflect.ValueOf(current) +// currentVal := reflect.ValueOf(current) - if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { - currentVal = reflect.ValueOf(currentVal.Elem().Interface()) - } +// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { +// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) +// } - var currentFielVal reflect.Value +// var currentFielVal reflect.Value - switch currentVal.Kind() { +// switch currentVal.Kind() { - case reflect.Struct: +// case reflect.Struct: - if currentVal.Type() == reflect.TypeOf(time.Time{}) { - currentFielVal = currentVal - break - } +// if currentVal.Type() == reflect.TypeOf(time.Time{}) { +// currentFielVal = currentVal +// break +// } - f := currentVal.FieldByName(param) +// f := currentVal.FieldByName(param) - if f.Kind() == reflect.Invalid { - panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) - } +// if f.Kind() == reflect.Invalid { +// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) +// } - currentFielVal = f +// currentFielVal = f - default: +// default: - currentFielVal = currentVal - } +// currentFielVal = currentVal +// } - if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { +// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) - } +// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) +// } - fv := reflect.ValueOf(field) +// fv := reflect.ValueOf(field) - switch fv.Kind() { +// switch fv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return fv.Int() < currentFielVal.Int() +// return fv.Int() < currentFielVal.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return fv.Uint() < currentFielVal.Uint() +// return fv.Uint() < currentFielVal.Uint() - case reflect.Float32, reflect.Float64: +// case reflect.Float32, reflect.Float64: - return fv.Float() < currentFielVal.Float() +// return fv.Float() < currentFielVal.Float() - case reflect.Struct: +// case reflect.Struct: - if fv.Type() == reflect.TypeOf(time.Time{}) { +// if fv.Type() == reflect.TypeOf(time.Time{}) { - if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { - panic("Bad Top Level field type") - } +// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { +// panic("Bad Top Level field type") +// } - t := currentFielVal.Interface().(time.Time) - fieldTime := field.(time.Time) +// t := currentFielVal.Interface().(time.Time) +// fieldTime := field.(time.Time) - return fieldTime.Before(t) - } - } - - panic(fmt.Sprintf("Bad field type %T", field)) -} +// return fieldTime.Before(t) +// } +// } -func isLte(top interface{}, current interface{}, field interface{}, param string) bool { +// panic(fmt.Sprintf("Bad field type %T", field)) +// } - st := reflect.ValueOf(field) +func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { - switch st.Kind() { + switch fieldKind { case reflect.String: p := asInt(param) - return int64(utf8.RuneCountInString(st.String())) <= p + return int64(utf8.RuneCountInString(field.String())) <= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) - return int64(st.Len()) <= p + return int64(field.Len()) <= p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) - return st.Int() <= p + return field.Int() <= p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) - return st.Uint() <= p + return field.Uint() <= p case reflect.Float32, reflect.Float64: p := asFloat(param) - return st.Float() <= p + return field.Float() <= p case reflect.Struct: - if st.Type() == reflect.TypeOf(time.Time{}) { + if fieldType == timeType || fieldType == timePtrType { now := time.Now().UTC() - t := field.(time.Time) + t := field.Interface().(time.Time) return t.Before(now) || t.Equal(now) } @@ -911,55 +905,55 @@ func isLte(top interface{}, current interface{}, field interface{}, param string panic(fmt.Sprintf("Bad field type %T", field)) } -func isLt(top interface{}, current interface{}, field interface{}, param string) bool { +// func isLt(top interface{}, current interface{}, field interface{}, param string) bool { - st := reflect.ValueOf(field) +// st := reflect.ValueOf(field) - switch st.Kind() { +// switch st.Kind() { - case reflect.String: - p := asInt(param) +// case reflect.String: +// p := asInt(param) - return int64(utf8.RuneCountInString(st.String())) < p +// return int64(utf8.RuneCountInString(st.String())) < p - case reflect.Slice, reflect.Map, reflect.Array: - p := asInt(param) +// case reflect.Slice, reflect.Map, reflect.Array: +// p := asInt(param) - return int64(st.Len()) < p +// return int64(st.Len()) < p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p := asInt(param) +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// p := asInt(param) - return st.Int() < p +// return st.Int() < p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p := asUint(param) +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +// p := asUint(param) - return st.Uint() < p +// return st.Uint() < p - case reflect.Float32, reflect.Float64: - p := asFloat(param) +// case reflect.Float32, reflect.Float64: +// p := asFloat(param) - return st.Float() < p +// return st.Float() < p - case reflect.Struct: +// case reflect.Struct: - if st.Type() == reflect.TypeOf(time.Time{}) { +// if st.Type() == reflect.TypeOf(time.Time{}) { - return field.(time.Time).Before(time.Now().UTC()) - } - } +// return field.(time.Time).Before(time.Now().UTC()) +// } +// } - panic(fmt.Sprintf("Bad field type %T", field)) -} +// panic(fmt.Sprintf("Bad field type %T", field)) +// } // max tests whether a variable value is lesser than a given // value. For numbers, it's a simple lesser-than test; for // strings it tests the number of characters whereas for maps // and slices it tests the number of items. -func hasMaxOf(top interface{}, current interface{}, field interface{}, param string) bool { +func hasMaxOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { - return isLte(top, current, field, param) + return isLte(topStruct, currentStruct, field, fieldType, fieldKind, param) } // asInt retuns the parameter as a int64 diff --git a/validator.go b/validator.go index abb4dda..cd05aa2 100644 --- a/validator.go +++ b/validator.go @@ -18,12 +18,14 @@ import ( const ( utf8HexComma = "0x2C" + utf8Pipe = "0x7C" tagSeparator = "," orSeparator = "|" tagKeySeparator = "=" structOnlyTag = "structonly" omitempty = "omitempty" skipValidationTag = "-" + diveTag = "dive" fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" invaldField = "Invalid field passed to traverseField" ) @@ -49,11 +51,11 @@ type Config struct { } // Func accepts all values needed for file and cross field validation -// top = top level struct when validating by struct otherwise nil -// current = current level struct when validating by struct otherwise optional comparison value -// f = field value for validation -// 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 +// topStruct = top level struct when validating by struct otherwise nil +// currentStruct = current level struct when validating by struct otherwise optional comparison value +// field = field value for validation +// param = parameter used in validation i.e. gt=0 param would be 0 +type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool // ValidationErrors is a type of map[string]*FieldError // it exists to allow for multiple errors passed from this library @@ -131,12 +133,15 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec errPrefix += typ.Name() + "." numFields := current.NumField() + var fld reflect.StructField + for i := 0; i < numFields; i++ { - v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, typ.Field(i).Tag.Get(v.config.TagName)) + fld = typ.Field(i) + v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.config.TagName), fld.Name) } } -func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string) { +func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) { if tag == skipValidationTag { return @@ -171,8 +176,8 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. param = vals[1] } - errs[errPrefix+typ.Name()] = &FieldError{ - Field: typ.Name(), + errs[errPrefix+name] = &FieldError{ + Field: name, Tag: vals[0], Param: param, Value: current.Interface(), @@ -200,6 +205,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. kind = current.Kind() } + // changed current, so have to get inner type again + typ = current.Type() + if kind != reflect.Struct { goto FALLTHROUGH } @@ -229,34 +237,122 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. } } - // for _, t := range strings.Split(tag, tagSeparator) { + var dive bool + // var diveSubTag string + + for _, t := range strings.Split(tag, tagSeparator) { + + if t == diveTag { + + dive = true + // diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") + break + } + + // no use in checking tags if it's empty and is ok to be + // omitempty needs to be the first tag if you wish to use it + if t == omitempty && !hasValue(topStruct, currentStruct, current, typ, kind, "") { + return + } + + // if strings.Contains(tag, omitempty) && !hasValue(topStruct, currentStruct, current, "") { + // return + // } + + var key string + var param string + + // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" + if strings.Index(t, orSeparator) == -1 { + vals := strings.SplitN(t, tagKeySeparator, 2) + key = vals[0] + + if len(key) == 0 { + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + } + + if len(vals) > 1 { + param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } + } else { + key = t + } + + if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, key, param, name) { + return + } + } + + if dive { + // traverse slice or map here + // or panic ;) + } +} + +// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok +func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool { + + // check if key is orVals, it could be! + orVals := strings.Split(key, orSeparator) + + if len(orVals) > 1 { + + var errTag string + + for _, val := range orVals { + vals := strings.SplitN(val, tagKeySeparator, 2) + + if len(vals[0]) == 0 { + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + } + + param := "" + if len(vals) > 1 { + param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } - // if t == diveTag { + // validate and keep track! + valFunc, ok := v.config.ValidationFuncs[vals[0]] + if !ok { + panic(fmt.Sprintf("Undefined validation function on field %s", name)) + } - // cField.dive = true - // cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") - // break - // } + if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { + return false + } - // orVals := strings.Split(t, orSeparator) - // cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} - // cField.tags = append(cField.tags, cTag) + errTag += orSeparator + vals[0] + } - // for i, val := range orVals { - // vals := strings.SplitN(val, tagKeySeparator, 2) + errs[errPrefix+name] = &FieldError{ + Field: name, + Tag: errTag[1:], + Value: current.Interface(), + Param: param, + Type: currentType, + Kind: currentKind, + } - // key := strings.TrimSpace(vals[0]) + return true + } - // if len(key) == 0 { - // panic(fmt.Sprintf("Invalid validation tag on field %s", name)) - // } + valFunc, ok := v.config.ValidationFuncs[key] + if !ok { + panic(fmt.Sprintf("Undefined validation function on field %s", name)) + } - // param := "" - // if len(vals) > 1 { - // param = strings.Replace(vals[1], utf8HexComma, ",", -1) - // } + if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { + return false + } + + errs[errPrefix+name] = &FieldError{ + Field: name, + Tag: key, + Value: current.Interface(), + Param: param, + Type: currentType, + Kind: currentKind, + } - // cTag.keyVals[i] = []string{key, param} - // } - // } + return true } diff --git a/validator_test.go b/validator_test.go index a236e43..90ead12 100644 --- a/validator_test.go +++ b/validator_test.go @@ -209,16 +209,17 @@ func TestValidation(t *testing.T) { } type Test struct { - // Name string `validate:"required"` - Inner *Inner `validate:"required"` + Name string `validate:"required"` + Arr []string `validate:"required"` + // Inner *Inner `validate:"required"` } - inner := &Inner{ - Name: "", - } + // inner := &Inner{ + // Name: "", + // } - // tst := &Test{Name: "Dean"} - tst := &Test{Inner: inner} + tst := &Test{Name: "Dean"} + // tst := &Test{Inner: inner} errs := validate.Struct(tst) From 68ba87af24fcd80167f3377993986f6e9fca6d1a Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 14 Jul 2015 21:10:25 -0400 Subject: [PATCH 07/20] More Progess added single field validation function. updated a few more baked in functions. added back single field validation benchmark. --- baked_in.go | 58 ++++++++++++++++++++++------------------------ benchmarks_test.go | 10 ++++---- validator.go | 24 +++++++++++++++++++ 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/baked_in.go b/baked_in.go index b9d2286..92b4611 100644 --- a/baked_in.go +++ b/baked_in.go @@ -13,9 +13,9 @@ import ( // or even disregard and use your own map if so desired. var BakedInValidators = map[string]Func{ "required": hasValue, - // "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, // "eq": isEq, // "ne": isNe, // "lt": isLt, @@ -621,7 +621,7 @@ func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V } } - panic(fmt.Sprintf("Bad field type %T", field)) + panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // func isGt(top interface{}, current interface{}, field interface{}, param string) bool { @@ -665,43 +665,41 @@ func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V // panic(fmt.Sprintf("Bad field type %T", field)) // } -// // length tests whether a variable's length is equal to a given -// // value. For strings it tests the number of characters whereas -// // for maps and slices it tests the number of items. -// func hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool { +// length tests whether a variable's length is equal to a given +// value. For strings it tests the number of characters whereas +// for maps and slices it tests the number of items. +func hasLengthOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// st := reflect.ValueOf(field) + switch fieldKind { -// switch st.Kind() { + case reflect.String: + p := asInt(param) -// case reflect.String: -// p := asInt(param) + return int64(utf8.RuneCountInString(field.String())) == p -// return int64(utf8.RuneCountInString(st.String())) == p + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) -// case reflect.Slice, reflect.Map, reflect.Array: -// p := asInt(param) + return int64(field.Len()) == p -// return int64(st.Len()) == p + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// p := asInt(param) + return field.Int() == p -// return st.Int() == p - -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// p := asUint(param) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) -// return st.Uint() == p + return field.Uint() == p -// case reflect.Float32, reflect.Float64: -// p := asFloat(param) + case reflect.Float32, reflect.Float64: + p := asFloat(param) -// return st.Float() == p -// } + return field.Float() == p + } -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} // min tests whether a variable value is larger or equal to a given // number. For number types, it's a simple lesser-than test; for @@ -902,7 +900,7 @@ func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V } } - panic(fmt.Sprintf("Bad field type %T", field)) + panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // func isLt(top interface{}, current interface{}, field interface{}, param string) bool { diff --git a/benchmarks_test.go b/benchmarks_test.go index 8f09908..b7f22d7 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -2,11 +2,11 @@ package validator import "testing" -// func BenchmarkValidateField(b *testing.B) { -// for n := 0; n < b.N; n++ { -// validate.Field("1", "len=1") -// } -// } +func BenchmarkValidateField(b *testing.B) { + for n := 0; n < b.N; n++ { + validate.Field("1", "len=1") + } +} func BenchmarkValidateStructSimple(b *testing.B) { diff --git a/validator.go b/validator.go index cd05aa2..a71e652 100644 --- a/validator.go +++ b/validator.go @@ -141,6 +141,30 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec } } +// Field allows validation of a single field, still using tag style validation to check multiple errors +func (v *Validate) Field(field interface{}, tag string) ValidationErrors { + + errs := map[string]*FieldError{} + fieldVal := reflect.ValueOf(field) + + v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "") + + return errs + + // return v.FieldWithValue(nil, field, tag) +} + +// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors +func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors { + + errs := map[string]*FieldError{} + topVal := reflect.ValueOf(val) + + v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "") + + return errs +} + func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) { if tag == skipValidationTag { From 019c5fc4ac92258e9aead6e29812658d227d08ae Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 14 Jul 2015 22:45:37 -0400 Subject: [PATCH 08/20] Finished converting baked in functions & some updates completed all validations function conversion. updated omitempty check to continue if all ok instead of passing tag to be validated. --- baked_in.go | 1071 ++++++++++++++++++++------------------------ benchmarks_test.go | 238 +++++----- regexes.go | 6 +- validator.go | 58 ++- 4 files changed, 646 insertions(+), 727 deletions(-) diff --git a/baked_in.go b/baked_in.go index 92b4611..392de19 100644 --- a/baked_in.go +++ b/baked_in.go @@ -2,8 +2,10 @@ package validator import ( "fmt" + "net/url" "reflect" "strconv" + "strings" "time" "unicode/utf8" ) @@ -12,574 +14,525 @@ import ( // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. var BakedInValidators = map[string]Func{ - "required": hasValue, - "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, - // "eq": isEq, - // "ne": isNe, - // "lt": isLt, - "lte": isLte, - // "gt": isGt, - "gte": isGte, - // "eqfield": isEqField, - // "nefield": isNeField, - // "gtefield": isGteField, - // "gtfield": isGtField, - // "ltefield": isLteField, - // "ltfield": isLtField, - // "alpha": isAlpha, - // "alphanum": isAlphanum, - // "numeric": isNumeric, - // "number": isNumber, - // "hexadecimal": isHexadecimal, - // "hexcolor": isHexcolor, - // "rgb": isRgb, - // "rgba": isRgba, - // "hsl": isHsl, - // "hsla": isHsla, - // "email": isEmail, - // "url": isURL, - // "uri": isURI, - // "base64": isBase64, - // "contains": contains, - // "containsany": containsAny, - // "containsrune": containsRune, - // "excludes": excludes, - // "excludesall": excludesAll, - // "excludesrune": excludesRune, - // "isbn": isISBN, - // "isbn10": isISBN10, - // "isbn13": isISBN13, - // "uuid": isUUID, - // "uuid3": isUUID3, - // "uuid4": isUUID4, - // "uuid5": isUUID5, - // "ascii": isASCII, - // "printascii": isPrintableASCII, - // "multibyte": hasMultiByteCharacter, - // "datauri": isDataURI, - // "latitude": isLatitude, - // "longitude": isLongitude, - // "ssn": isSSN, -} - -// func isSSN(top interface{}, current interface{}, field interface{}, param string) bool { - -// if len(field.(string)) != 11 { -// return false -// } - -// return matchesRegex(sSNRegex, field) -// } - -// func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(longitudeRegex, field) -// } - -// func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(latitudeRegex, field) -// } - -// func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool { - -// uri := strings.SplitN(field.(string), ",", 2) - -// if len(uri) != 2 { -// return false -// } - -// if !matchesRegex(dataURIRegex, uri[0]) { -// return false -// } - -// return isBase64(top, current, uri[1], param) -// } - -// func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool { - -// if len(field.(string)) == 0 { -// return true -// } - -// return matchesRegex(multibyteRegex, field) -// } - -// func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(printableASCIIRegex, field) -// } - -// func isASCII(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(aSCIIRegex, field) -// } - -// func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(uUID5Regex, field) -// } - -// func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(uUID4Regex, field) -// } - -// func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(uUID3Regex, field) -// } - -// func isUUID(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(uUIDRegex, field) -// } - -// func isISBN(top interface{}, current interface{}, field interface{}, param string) bool { -// return isISBN10(top, current, field, param) || isISBN13(top, current, field, param) -// } - -// func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool { - -// s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4) - -// if !matchesRegex(iSBN13Regex, s) { -// return false -// } - -// var checksum int32 -// var i int32 - -// factor := []int32{1, 3} - -// for i = 0; i < 12; i++ { -// checksum += factor[i%2] * int32(s[i]-'0') -// } + "required": hasValue, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "alpha": isAlpha, + "alphanum": isAlphanum, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHexcolor, + "rgb": isRgb, + "rgba": isRgba, + "hsl": isHsl, + "hsla": isHsla, + "email": isEmail, + "url": isURL, + "uri": isURI, + "base64": isBase64, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, +} -// if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 { -// return true -// } +func isSSN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// return false -// } + if field.Len() != 11 { + return false + } -// func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool { + return matchesRegex(sSNRegex, field.String()) +} -// s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3) +func isLongitude(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(longitudeRegex, field.String()) +} -// if !matchesRegex(iSBN10Regex, s) { -// return false -// } +func isLatitude(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(latitudeRegex, field.String()) +} -// var checksum int32 -// var i int32 +func isDataURI(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// for i = 0; i < 9; i++ { -// checksum += (i + 1) * int32(s[i]-'0') -// } + uri := strings.SplitN(field.String(), ",", 2) -// if s[9] == 'X' { -// checksum += 10 * 10 -// } else { -// checksum += 10 * int32(s[9]-'0') -// } + if len(uri) != 2 { + return false + } -// if checksum%11 == 0 { -// return true -// } + if !matchesRegex(dataURIRegex, uri[0]) { + return false + } -// return false -// } + fld := reflect.ValueOf(uri[1]) -// func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool { -// return !containsRune(top, current, field, param) -// } + return isBase64(topStruct, currentStruct, fld, fld.Type(), fld.Kind(), param) +} -// func excludesAll(top interface{}, current interface{}, field interface{}, param string) bool { -// return !containsAny(top, current, field, param) -// } +func hasMultiByteCharacter(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// func excludes(top interface{}, current interface{}, field interface{}, param string) bool { -// return !contains(top, current, field, param) -// } + if field.Len() == 0 { + return true + } -// func containsRune(top interface{}, current interface{}, field interface{}, param string) bool { -// r, _ := utf8.DecodeRuneInString(param) + return matchesRegex(multibyteRegex, field.String()) +} -// return strings.ContainsRune(field.(string), r) -// } +func isPrintableASCII(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(printableASCIIRegex, field.String()) +} -// func containsAny(top interface{}, current interface{}, field interface{}, param string) bool { -// return strings.ContainsAny(field.(string), param) -// } +func isASCII(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(aSCIIRegex, field.String()) +} -// func contains(top interface{}, current interface{}, field interface{}, param string) bool { -// return strings.Contains(field.(string), param) -// } +func isUUID5(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(uUID5Regex, field.String()) +} -// func isNeField(top interface{}, current interface{}, field interface{}, param string) bool { -// return !isEqField(top, current, field, param) -// } +func isUUID4(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(uUID4Regex, field.String()) +} -// func isNe(top interface{}, current interface{}, field interface{}, param string) bool { -// return !isEq(top, current, field, param) -// } +func isUUID3(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(uUID3Regex, field.String()) +} -// func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { +func isUUID(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(uUIDRegex, field.String()) +} -// if current == nil { -// panic("struct not passed for cross validation") -// } +func isISBN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return isISBN10(topStruct, currentStruct, field, fieldType, fieldKind, param) || isISBN13(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// currentVal := reflect.ValueOf(current) +func isISBN13(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { -// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) -// } + s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4) -// var currentFielVal reflect.Value + if !matchesRegex(iSBN13Regex, s) { + return false + } -// switch currentVal.Kind() { + var checksum int32 + var i int32 -// case reflect.Struct: + factor := []int32{1, 3} -// if currentVal.Type() == reflect.TypeOf(time.Time{}) { -// currentFielVal = currentVal -// break -// } + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(s[i]-'0') + } -// f := currentVal.FieldByName(param) + if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 { + return true + } -// if f.Kind() == reflect.Invalid { -// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) -// } + return false +} -// currentFielVal = f +func isISBN10(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// default: + s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3) -// currentFielVal = currentVal -// } + if !matchesRegex(iSBN10Regex, s) { + return false + } -// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + var checksum int32 + var i int32 -// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) -// } + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(s[i]-'0') + } -// fv := reflect.ValueOf(field) + if s[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(s[9]-'0') + } -// switch fv.Kind() { + if checksum%11 == 0 { + return true + } -// case reflect.String: -// return fv.String() == currentFielVal.String() -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return false +} -// return fv.Int() == currentFielVal.Int() +func excludesRune(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !containsRune(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +func excludesAll(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !containsAny(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// return fv.Uint() == currentFielVal.Uint() +func excludes(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !contains(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// case reflect.Float32, reflect.Float64: +func containsRune(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + r, _ := utf8.DecodeRuneInString(param) -// return fv.Float() == currentFielVal.Float() -// case reflect.Slice, reflect.Map, reflect.Array: + return strings.ContainsRune(field.String(), r) +} -// return int64(fv.Len()) == int64(currentFielVal.Len()) -// case reflect.Struct: +func containsAny(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return strings.ContainsAny(field.String(), param) +} -// if fv.Type() == reflect.TypeOf(time.Time{}) { +func contains(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return strings.Contains(field.String(), param) +} -// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { -// panic("Bad Top Level field type") -// } +func isNeField(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !isEqField(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// t := currentFielVal.Interface().(time.Time) -// fieldTime := field.(time.Time) +func isNe(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !isEq(topStruct, currentStruct, field, fieldType, fieldKind, param) +} -// return fieldTime.Equal(t) -// } -// } +func isEqField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + // if current == nil { + if !current.IsValid() { + panic("struct or field value not passed for cross validation") + } -// func isEq(top interface{}, current interface{}, field interface{}, param string) bool { + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// st := reflect.ValueOf(field) + switch current.Kind() { -// switch st.Kind() { + case reflect.Struct: -// case reflect.String: + if current.Type() == timeType || current.Type() == timePtrType { + break + } -// return st.String() == param + current = current.FieldByName(param) -// case reflect.Slice, reflect.Map, reflect.Array: -// p := asInt(param) + if current.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + } -// return int64(st.Len()) == p + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// p := asInt(param) + switch fieldKind { -// return st.Int() == p + case reflect.String: + return field.String() == current.String() -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// p := asUint(param) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() == current.Int() -// return st.Uint() == p + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() == current.Uint() -// case reflect.Float32, reflect.Float64: -// p := asFloat(param) + case reflect.Float32, reflect.Float64: + return field.Float() == current.Float() -// return st.Float() == p -// } + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) == int64(current.Len()) -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + case reflect.Struct: + if fieldType == timeType || fieldType == timePtrType { -// func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(base64Regex, field) -// } + if current.Type() != timeType && current.Type() != timePtrType { + panic("Bad Top Level field type") + } -// func isURI(top interface{}, current interface{}, field interface{}, param string) bool { + t := current.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) -// st := reflect.ValueOf(field) + return fieldTime.Equal(t) + } + } -// switch st.Kind() { + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} -// case reflect.String: -// _, err := url.ParseRequestURI(field.(string)) +func isEq(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// return err == nil -// } + switch fieldKind { -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + case reflect.String: + return field.String() == param -// func isURL(top interface{}, current interface{}, field interface{}, param string) bool { -// st := reflect.ValueOf(field) + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) -// switch st.Kind() { + return int64(field.Len()) == p -// case reflect.String: -// url, err := url.ParseRequestURI(field.(string)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) -// if err != nil { -// return false -// } + return field.Int() == p -// if len(url.Scheme) == 0 { -// return false -// } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) -// return err == nil -// } + return field.Uint() == p -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + case reflect.Float32, reflect.Float64: + p := asFloat(param) -// func isEmail(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(emailRegex, field) -// } + return field.Float() == p + } -// func isHsla(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(hslaRegex, field) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} -// func isHsl(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(hslRegex, field) -// } +func isBase64(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(base64Regex, field.String()) +} -// func isRgba(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(rgbaRegex, field) -// } +func isURI(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// func isRgb(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(rgbRegex, field) -// } + switch fieldKind { -// func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(hexcolorRegex, field) -// } + case reflect.String: + _, err := url.ParseRequestURI(field.String()) -// func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(hexadecimalRegex, field) -// } + return err == nil + } -// func isNumber(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(numberRegex, field) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} -// func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(numericRegex, field) -// } +func isURL(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(alphaNumericRegex, field) -// } + switch fieldKind { -// func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool { -// return matchesRegex(alphaRegex, field) -// } + case reflect.String: + url, err := url.ParseRequestURI(field.String()) -func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + if err != nil { + return false + } - // st := reflect.ValueOf(field) + if len(url.Scheme) == 0 { + return false + } - switch fieldKind { + return err == nil + } - case reflect.Slice, reflect.Map, reflect.Array: - return !field.IsNil() && int64(field.Len()) > 0 + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} - default: - return field.IsValid() && field != reflect.Zero(fieldType).Interface() - } +func isEmail(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(emailRegex, field.String()) } -// func isGteField(top interface{}, current interface{}, field interface{}, param string) bool { +func isHsla(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(hslaRegex, field.String()) +} -// if current == nil { -// panic("struct not passed for cross validation") -// } +func isHsl(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(hslRegex, field.String()) +} -// currentVal := reflect.ValueOf(current) +func isRgba(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(rgbaRegex, field.String()) +} -// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { -// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) -// } +func isRgb(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(rgbRegex, field.String()) +} -// var currentFielVal reflect.Value +func isHexcolor(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(hexcolorRegex, field.String()) +} -// switch currentVal.Kind() { +func isHexadecimal(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(hexadecimalRegex, field.String()) +} -// case reflect.Struct: +func isNumber(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(numberRegex, field.String()) +} -// if currentVal.Type() == reflect.TypeOf(time.Time{}) { -// currentFielVal = currentVal -// break -// } +func isNumeric(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(numericRegex, field.String()) +} -// f := currentVal.FieldByName(param) +func isAlphanum(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(alphaNumericRegex, field.String()) +} -// if f.Kind() == reflect.Invalid { -// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) -// } +func isAlpha(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return matchesRegex(alphaRegex, field.String()) +} -// currentFielVal = f +func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// default: + switch fieldKind { -// currentFielVal = currentVal -// } + case reflect.Slice, reflect.Map, reflect.Array: + return !field.IsNil() && int64(field.Len()) > 0 -// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + default: + return field.IsValid() && field != reflect.Zero(fieldType).Interface() + } +} -// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) -// } +func isGteField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// fv := reflect.ValueOf(field) + if !current.IsValid() { + panic("struct not passed for cross validation") + } -// switch fv.Kind() { + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch current.Kind() { -// return fv.Int() >= currentFielVal.Int() + case reflect.Struct: -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if current.Type() == timeType || current.Type() == timePtrType { + break + } -// return fv.Uint() >= currentFielVal.Uint() + current = current.FieldByName(param) -// case reflect.Float32, reflect.Float64: + if current.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + } -// return fv.Float() >= currentFielVal.Float() + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// case reflect.Struct: + switch fieldKind { -// if fv.Type() == reflect.TypeOf(time.Time{}) { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { -// panic("Bad Top Level field type") -// } + return field.Int() >= current.Int() -// t := currentFielVal.Interface().(time.Time) -// fieldTime := field.(time.Time) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// return fieldTime.After(t) || fieldTime.Equal(t) -// } -// } + return field.Uint() >= current.Uint() -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + case reflect.Float32, reflect.Float64: -// func isGtField(top interface{}, current interface{}, field interface{}, param string) bool { + return field.Float() >= current.Float() -// if current == nil { -// panic("struct not passed for cross validation") -// } + case reflect.Struct: -// currentVal := reflect.ValueOf(current) + if field.Type() == timeType || field.Type() == timePtrType { -// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { -// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) -// } + if current.Type() != timeType && current.Type() != timePtrType { + panic("Bad Top Level field type") + } -// var currentFielVal reflect.Value + t := current.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) -// switch currentVal.Kind() { + return fieldTime.After(t) || fieldTime.Equal(t) + } + } -// case reflect.Struct: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} -// if currentVal.Type() == reflect.TypeOf(time.Time{}) { -// currentFielVal = currentVal -// break -// } +func isGtField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// f := currentVal.FieldByName(param) + if !current.IsValid() { + panic("struct not passed for cross validation") + } -// if f.Kind() == reflect.Invalid { -// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) -// } + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// currentFielVal = f + switch current.Kind() { -// default: + case reflect.Struct: -// currentFielVal = currentVal -// } + if current.Type() == timeType || current.Type() == timePtrType { + break + } -// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + current = current.FieldByName(param) -// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) -// } + if current.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + } -// fv := reflect.ValueOf(field) + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// switch fv.Kind() { + switch fieldKind { -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// return fv.Int() > currentFielVal.Int() + return field.Int() > current.Int() -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// return fv.Uint() > currentFielVal.Uint() + return field.Uint() > current.Uint() -// case reflect.Float32, reflect.Float64: + case reflect.Float32, reflect.Float64: -// return fv.Float() > currentFielVal.Float() + return field.Float() > current.Float() -// case reflect.Struct: + case reflect.Struct: -// if fv.Type() == reflect.TypeOf(time.Time{}) { + if field.Type() == timeType || field.Type() == timePtrType { -// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { -// panic("Bad Top Level field type") -// } + if current.Type() != timeType && current.Type() != timePtrType { + panic("Bad Top Level field type") + } -// t := currentFielVal.Interface().(time.Time) -// fieldTime := field.(time.Time) + t := current.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) -// return fieldTime.After(t) -// } -// } + return fieldTime.After(t) + } + } -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { @@ -624,46 +577,44 @@ func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V panic(fmt.Sprintf("Bad field type %T", field.Interface())) } -// func isGt(top interface{}, current interface{}, field interface{}, param string) bool { +func isGt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// st := reflect.ValueOf(field) - -// switch st.Kind() { + switch fieldKind { -// case reflect.String: -// p := asInt(param) + case reflect.String: + p := asInt(param) -// return int64(utf8.RuneCountInString(st.String())) > p + return int64(utf8.RuneCountInString(field.String())) > p -// case reflect.Slice, reflect.Map, reflect.Array: -// p := asInt(param) + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) -// return int64(st.Len()) > p + return int64(field.Len()) > p -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// p := asInt(param) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) -// return st.Int() > p + return field.Int() > p -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// p := asUint(param) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) -// return st.Uint() > p + return field.Uint() > p -// case reflect.Float32, reflect.Float64: -// p := asFloat(param) + case reflect.Float32, reflect.Float64: + p := asFloat(param) -// return st.Float() > p -// case reflect.Struct: + return field.Float() > p + case reflect.Struct: -// if st.Type() == reflect.TypeOf(time.Time{}) { + if field.Type() == timeType || field.Type() == timePtrType { -// return field.(time.Time).After(time.Now().UTC()) -// } -// } + return field.Interface().(time.Time).After(time.Now().UTC()) + } + } -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} // length tests whether a variable's length is equal to a given // value. For strings it tests the number of characters whereas @@ -710,155 +661,127 @@ func hasMinOf(topStruct reflect.Value, currentStruct reflect.Value, field reflec return isGte(topStruct, currentStruct, field, fieldType, fieldKind, param) } -// func isLteField(top interface{}, current interface{}, field interface{}, param string) bool { - -// if current == nil { -// panic("struct not passed for cross validation") -// } - -// currentVal := reflect.ValueOf(current) - -// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { -// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) -// } - -// var currentFielVal reflect.Value - -// switch currentVal.Kind() { - -// case reflect.Struct: - -// if currentVal.Type() == reflect.TypeOf(time.Time{}) { -// currentFielVal = currentVal -// break -// } +func isLteField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// f := currentVal.FieldByName(param) - -// if f.Kind() == reflect.Invalid { -// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) -// } - -// currentFielVal = f - -// default: - -// currentFielVal = currentVal -// } - -// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { - -// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) -// } - -// fv := reflect.ValueOf(field) + if !current.IsValid() { + panic("struct not passed for cross validation") + } -// switch fv.Kind() { + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch current.Kind() { -// return fv.Int() <= currentFielVal.Int() + case reflect.Struct: -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if current.Type() == timeType || current.Type() == timePtrType { + break + } -// return fv.Uint() <= currentFielVal.Uint() + current = current.FieldByName(param) -// case reflect.Float32, reflect.Float64: + if current.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + } -// return fv.Float() <= currentFielVal.Float() + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// case reflect.Struct: + switch fieldKind { -// if fv.Type() == reflect.TypeOf(time.Time{}) { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { -// panic("Bad Top Level field type") -// } + return field.Int() <= current.Int() -// t := currentFielVal.Interface().(time.Time) -// fieldTime := field.(time.Time) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// return fieldTime.Before(t) || fieldTime.Equal(t) -// } -// } + return field.Uint() <= current.Uint() -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + case reflect.Float32, reflect.Float64: -// func isLtField(top interface{}, current interface{}, field interface{}, param string) bool { + return field.Float() <= current.Float() -// if current == nil { -// panic("struct not passed for cross validation") -// } + case reflect.Struct: -// currentVal := reflect.ValueOf(current) + if field.Type() == timeType || field.Type() == timePtrType { -// if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { -// currentVal = reflect.ValueOf(currentVal.Elem().Interface()) -// } + if current.Type() != timeType && current.Type() != timePtrType { + panic("Bad Top Level field type") + } -// var currentFielVal reflect.Value + t := current.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) -// switch currentVal.Kind() { + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } -// case reflect.Struct: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} -// if currentVal.Type() == reflect.TypeOf(time.Time{}) { -// currentFielVal = currentVal -// break -// } +func isLtField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// f := currentVal.FieldByName(param) + if !current.IsValid() { + panic("struct not passed for cross validation") + } -// if f.Kind() == reflect.Invalid { -// panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) -// } + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// currentFielVal = f + switch current.Kind() { -// default: + case reflect.Struct: -// currentFielVal = currentVal -// } + if current.Type() == timeType || current.Type() == timePtrType { + break + } -// if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + current = current.FieldByName(param) -// currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) -// } + if current.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + } -// fv := reflect.ValueOf(field) + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } -// switch fv.Kind() { + switch fieldKind { -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// return fv.Int() < currentFielVal.Int() + return field.Int() < current.Int() -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// return fv.Uint() < currentFielVal.Uint() + return field.Uint() < current.Uint() -// case reflect.Float32, reflect.Float64: + case reflect.Float32, reflect.Float64: -// return fv.Float() < currentFielVal.Float() + return field.Float() < current.Float() -// case reflect.Struct: + case reflect.Struct: -// if fv.Type() == reflect.TypeOf(time.Time{}) { + if field.Type() == timeType || field.Type() == timePtrType { -// if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { -// panic("Bad Top Level field type") -// } + if current.Type() != timeType && current.Type() != timePtrType { + panic("Bad Top Level field type") + } -// t := currentFielVal.Interface().(time.Time) -// fieldTime := field.(time.Time) + t := current.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) -// return fieldTime.Before(t) -// } -// } + return fieldTime.Before(t) + } + } -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { @@ -903,47 +826,45 @@ func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V panic(fmt.Sprintf("Bad field type %T", field.Interface())) } -// func isLt(top interface{}, current interface{}, field interface{}, param string) bool { +func isLt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// st := reflect.ValueOf(field) - -// switch st.Kind() { + switch fieldKind { -// case reflect.String: -// p := asInt(param) + case reflect.String: + p := asInt(param) -// return int64(utf8.RuneCountInString(st.String())) < p + return int64(utf8.RuneCountInString(field.String())) < p -// case reflect.Slice, reflect.Map, reflect.Array: -// p := asInt(param) + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) -// return int64(st.Len()) < p + return int64(field.Len()) < p -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// p := asInt(param) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) -// return st.Int() < p + return field.Int() < p -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: -// p := asUint(param) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) -// return st.Uint() < p + return field.Uint() < p -// case reflect.Float32, reflect.Float64: -// p := asFloat(param) + case reflect.Float32, reflect.Float64: + p := asFloat(param) -// return st.Float() < p + return field.Float() < p -// case reflect.Struct: + case reflect.Struct: -// if st.Type() == reflect.TypeOf(time.Time{}) { + if field.Type() == timeType || field.Type() == timePtrType { -// return field.(time.Time).Before(time.Now().UTC()) -// } -// } + return field.Interface().(time.Time).Before(time.Now().UTC()) + } + } -// panic(fmt.Sprintf("Bad field type %T", field)) -// } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} // max tests whether a variable value is lesser than a given // value. For numbers, it's a simple lesser-than test; for diff --git a/benchmarks_test.go b/benchmarks_test.go index b7f22d7..ee836c2 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -42,122 +42,122 @@ func BenchmarkTemplateParallelSimple(b *testing.B) { }) } -// func BenchmarkValidateStructLarge(b *testing.B) { - -// tFail := &TestString{ -// Required: "", -// Len: "", -// Min: "", -// Max: "12345678901", -// MinMax: "", -// Lt: "0123456789", -// Lte: "01234567890", -// Gt: "1", -// Gte: "1", -// OmitEmpty: "12345678901", -// Sub: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "", -// }, -// Iface: &Impl{ -// F: "12", -// }, -// } - -// tSuccess := &TestString{ -// Required: "Required", -// Len: "length==10", -// Min: "min=1", -// Max: "1234567890", -// MinMax: "12345", -// Lt: "012345678", -// Lte: "0123456789", -// Gt: "01234567890", -// Gte: "0123456789", -// OmitEmpty: "", -// Sub: &SubTest{ -// Test: "1", -// }, -// SubIgnore: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "1", -// }, -// Iface: &Impl{ -// F: "123", -// }, -// } - -// for n := 0; n < b.N; n++ { -// validate.Struct(tSuccess) -// validate.Struct(tFail) -// } -// } - -// func BenchmarkTemplateParallelLarge(b *testing.B) { - -// tFail := &TestString{ -// Required: "", -// Len: "", -// Min: "", -// Max: "12345678901", -// MinMax: "", -// Lt: "0123456789", -// Lte: "01234567890", -// Gt: "1", -// Gte: "1", -// OmitEmpty: "12345678901", -// Sub: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "", -// }, -// Iface: &Impl{ -// F: "12", -// }, -// } - -// tSuccess := &TestString{ -// Required: "Required", -// Len: "length==10", -// Min: "min=1", -// Max: "1234567890", -// MinMax: "12345", -// Lt: "012345678", -// Lte: "0123456789", -// Gt: "01234567890", -// Gte: "0123456789", -// OmitEmpty: "", -// Sub: &SubTest{ -// Test: "1", -// }, -// SubIgnore: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "1", -// }, -// Iface: &Impl{ -// F: "123", -// }, -// } - -// b.RunParallel(func(pb *testing.PB) { -// for pb.Next() { -// validate.Struct(tSuccess) -// validate.Struct(tFail) -// } -// }) -// } +func BenchmarkValidateStructLarge(b *testing.B) { + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } + + for n := 0; n < b.N; n++ { + validate.Struct(tSuccess) + validate.Struct(tFail) + } +} + +func BenchmarkTemplateParallelLarge(b *testing.B) { + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + validate.Struct(tSuccess) + validate.Struct(tFail) + } + }) +} diff --git a/regexes.go b/regexes.go index d3e8d80..e3f420e 100644 --- a/regexes.go +++ b/regexes.go @@ -58,7 +58,7 @@ var ( sSNRegex = regexp.MustCompile(sSNRegexString) ) -func matchesRegex(regex *regexp.Regexp, field interface{}) bool { - fieldAsString := field.(string) //this will panic inherently - return regex.MatchString(fieldAsString) +func matchesRegex(regex *regexp.Regexp, value string) bool { + // fieldAsString := field.(string) //this will panic inherently + return regex.MatchString(value) } diff --git a/validator.go b/validator.go index a71e652..28249c1 100644 --- a/validator.go +++ b/validator.go @@ -101,6 +101,28 @@ func New(config Config) *Validate { return &Validate{config: config} } +// Field allows validation of a single field, still using tag style validation to check multiple errors +func (v *Validate) Field(field interface{}, tag string) ValidationErrors { + + errs := map[string]*FieldError{} + fieldVal := reflect.ValueOf(field) + + v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "") + + return errs +} + +// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors +func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors { + + errs := map[string]*FieldError{} + topVal := reflect.ValueOf(val) + + v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "") + + return errs +} + // Struct validates a struct, even it's nested structs, and returns a struct containing the errors // 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 @@ -141,30 +163,6 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec } } -// Field allows validation of a single field, still using tag style validation to check multiple errors -func (v *Validate) Field(field interface{}, tag string) ValidationErrors { - - errs := map[string]*FieldError{} - fieldVal := reflect.ValueOf(field) - - v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "") - - return errs - - // return v.FieldWithValue(nil, field, tag) -} - -// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors -func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors { - - errs := map[string]*FieldError{} - topVal := reflect.ValueOf(val) - - v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "") - - return errs -} - func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) { if tag == skipValidationTag { @@ -275,13 +273,13 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. // no use in checking tags if it's empty and is ok to be // omitempty needs to be the first tag if you wish to use it - if t == omitempty && !hasValue(topStruct, currentStruct, current, typ, kind, "") { - return - } + if t == omitempty { - // if strings.Contains(tag, omitempty) && !hasValue(topStruct, currentStruct, current, "") { - // return - // } + if !hasValue(topStruct, currentStruct, current, typ, kind, "") { + return + } + continue + } var key string var param string From 4d2ffbedfc8bf3e5c8fc5815e2a692e681d80511 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 14 Jul 2015 23:11:59 -0400 Subject: [PATCH 09/20] Added traverseSlice function --- validator.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/validator.go b/validator.go index 28249c1..0fe2eac 100644 --- a/validator.go +++ b/validator.go @@ -12,6 +12,7 @@ import ( "bytes" "fmt" "reflect" + "strconv" "strings" "time" ) @@ -260,14 +261,14 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. } var dive bool - // var diveSubTag string + var diveSubTag string for _, t := range strings.Split(tag, tagSeparator) { if t == diveTag { dive = true - // diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") + diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") break } @@ -308,6 +309,27 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. if dive { // traverse slice or map here // or panic ;) + switch kind { + case reflect.Slice, reflect.Array: + + case reflect.Map: + v.traverseSlice(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name) + } + } +} + +// func (v *Validate) traverseSlice(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error { +func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) { + + for i := 0; i < current.Len(); i++ { + + idxField := current.Index(i) + + if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + } + + v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, name+"["+strconv.Itoa(i)+"]") } } From 88cc19b9e127703cb6ffd10f41f29262b1b79bbb Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Wed, 15 Jul 2015 07:45:28 -0400 Subject: [PATCH 10/20] Add traverseMap function --- validator.go | 62 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/validator.go b/validator.go index 0fe2eac..18ce53a 100644 --- a/validator.go +++ b/validator.go @@ -12,23 +12,24 @@ import ( "bytes" "fmt" "reflect" - "strconv" "strings" "time" ) const ( - utf8HexComma = "0x2C" - utf8Pipe = "0x7C" - tagSeparator = "," - orSeparator = "|" - tagKeySeparator = "=" - structOnlyTag = "structonly" - omitempty = "omitempty" - skipValidationTag = "-" - diveTag = "dive" - fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" - invaldField = "Invalid field passed to traverseField" + utf8HexComma = "0x2C" + utf8Pipe = "0x7C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + omitempty = "omitempty" + skipValidationTag = "-" + diveTag = "dive" + fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" + invaldField = "Invalid field passed to traverseField" + arrayIndexFieldName = "%s[%d]" + mapIndexFieldName = "%s[%v]" ) var ( @@ -133,7 +134,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors { errs := map[string]*FieldError{} sv := reflect.ValueOf(current) - v.tranverseStruct(sv, sv, sv, "", errs) + v.tranverseStruct(sv, sv, sv, "", errs, true) if len(errs) == 0 { return nil @@ -142,7 +143,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors { return errs } -func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors) { +func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool) { if current.Kind() == reflect.Ptr && !current.IsNil() { current = current.Elem() @@ -153,7 +154,11 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec } typ := current.Type() - errPrefix += typ.Name() + "." + + if useStructName { + errPrefix += typ.Name() + "." + } + numFields := current.NumField() var fld reflect.StructField @@ -246,7 +251,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. return } - v.tranverseStruct(topStruct, current, current, errPrefix, errs) + v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false) return } @@ -311,14 +316,17 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. // or panic ;) switch kind { case reflect.Slice, reflect.Array: - - case reflect.Map: v.traverseSlice(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name) + case reflect.Map: + v.traverseMap(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name) + default: + // throw error, if not a slice or map then should not have gotten here + // bad dive tag usage + panic("dive error! can't dive on a non slice or map") } } } -// func (v *Validate) traverseSlice(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error { func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) { for i := 0; i < current.Len(); i++ { @@ -329,7 +337,21 @@ func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect. idxField = idxField.Elem() } - v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, name+"["+strconv.Itoa(i)+"]") + v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i)) + } +} + +func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) { + + for _, key := range current.MapKeys() { + + idxField := current.MapIndex(key) + + if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { + idxField = idxField.Elem() + } + + v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface())) } } From 24d46304a873555a331bcb61bde8dfe34f1dd433 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Wed, 15 Jul 2015 09:30:07 -0400 Subject: [PATCH 11/20] Updating & re-injecting tests updating test for the new error return formet updated assertion functions to handle a nil Map,Slice,Ptr.....type fixed hasValue check not comparing Interface but field Value. --- baked_in.go | 2 +- validator.go | 10 +- validator_test.go | 1471 +++++++++++++++++++++------------------------ 3 files changed, 709 insertions(+), 774 deletions(-) diff --git a/baked_in.go b/baked_in.go index 392de19..bf9e512 100644 --- a/baked_in.go +++ b/baked_in.go @@ -408,7 +408,7 @@ func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflec return !field.IsNil() && int64(field.Len()) > 0 default: - return field.IsValid() && field != reflect.Zero(fieldType).Interface() + return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface() } } diff --git a/validator.go b/validator.go index 18ce53a..27d8a63 100644 --- a/validator.go +++ b/validator.go @@ -111,6 +111,10 @@ func (v *Validate) Field(field interface{}, tag string) ValidationErrors { v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "") + if len(errs) == 0 { + return nil + } + return errs } @@ -122,6 +126,10 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "") + if len(errs) == 0 { + return nil + } + return errs } @@ -321,7 +329,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. v.traverseMap(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name) default: // throw error, if not a slice or map then should not have gotten here - // bad dive tag usage + // bad dive tag panic("dive error! can't dive on a non slice or map") } } diff --git a/validator_test.go b/validator_test.go index 90ead12..ea4656e 100644 --- a/validator_test.go +++ b/validator_test.go @@ -125,6 +125,20 @@ func IsEqual(t *testing.T, val1, val2 interface{}) bool { return true } + switch v1.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + if v1.IsNil() { + v1 = reflect.ValueOf(nil) + } + } + + switch v2.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + if v2.IsNil() { + v2 = reflect.ValueOf(nil) + } + } + v1Underlying := reflect.Zero(reflect.TypeOf(v1)).Interface() v2Underlying := reflect.Zero(reflect.TypeOf(v2)).Interface() @@ -143,13 +157,16 @@ func IsEqual(t *testing.T, val1, val2 interface{}) bool { } CASE1: + // fmt.Println("CASE 1") return reflect.DeepEqual(v1.Interface(), v2.Interface()) - CASE2: + // fmt.Println("CASE 2") return reflect.DeepEqual(v1.Interface(), v2) CASE3: + // fmt.Println("CASE 3") return reflect.DeepEqual(v1, v2.Interface()) CASE4: + // fmt.Println("CASE 4") return reflect.DeepEqual(v1, v2) } @@ -202,29 +219,38 @@ func PanicMatchesSkip(t *testing.T, skip int, fn func(), matches string) { fn() } -func TestValidation(t *testing.T) { +func AssertError(t *testing.T, errs ValidationErrors, key, field, expectedTag string) { - type Inner struct { - Name string - } + val, ok := errs[key] + EqualSkip(t, 2, ok, true) + NotEqualSkip(t, 2, val, nil) + EqualSkip(t, 2, val.Field, field) + EqualSkip(t, 2, val.Tag, expectedTag) +} - type Test struct { - Name string `validate:"required"` - Arr []string `validate:"required"` - // Inner *Inner `validate:"required"` - } +func TestValidation(t *testing.T) { - // inner := &Inner{ - // Name: "", + // type Inner struct { + // Name string // } - tst := &Test{Name: "Dean"} - // tst := &Test{Inner: inner} + // type Test struct { + // Name string `validate:"required"` + // Arr []string `validate:"required"` + // // Inner *Inner `validate:"required"` + // } + + // // inner := &Inner{ + // // Name: "", + // // } - errs := validate.Struct(tst) + // tst := &Test{Name: "Dean"} + // // tst := &Test{Inner: inner} - fmt.Println(errs) - fmt.Println(errs == nil) + // errs := validate.Struct(tst) + + // fmt.Println(errs) + // fmt.Println(errs == nil) } // func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors { @@ -2972,873 +2998,774 @@ func TestValidation(t *testing.T) { // NotEqual(t, errs, nil) // } -// func TestUrl(t *testing.T) { - -// var tests = []struct { -// param string -// expected bool -// }{ -// {"http://foo.bar#com", true}, -// {"http://foobar.com", true}, -// {"https://foobar.com", true}, -// {"foobar.com", false}, -// {"http://foobar.coffee/", true}, -// {"http://foobar.中文网/", true}, -// {"http://foobar.org/", true}, -// {"http://foobar.org:8080/", true}, -// {"ftp://foobar.ru/", true}, -// {"http://user:pass@www.foobar.com/", true}, -// {"http://127.0.0.1/", true}, -// {"http://duckduckgo.com/?q=%2F", true}, -// {"http://localhost:3000/", true}, -// {"http://foobar.com/?foo=bar#baz=qux", true}, -// {"http://foobar.com?foo=bar", true}, -// {"http://www.xn--froschgrn-x9a.net/", true}, -// {"", false}, -// {"xyz://foobar.com", true}, -// {"invalid.", false}, -// {".com", false}, -// {"rtmp://foobar.com", true}, -// {"http://www.foo_bar.com/", true}, -// {"http://localhost:3000/", true}, -// {"http://foobar.com#baz=qux", true}, -// {"http://foobar.com/t$-_.+!*\\'(),", true}, -// {"http://www.foobar.com/~foobar", true}, -// {"http://www.-foobar.com/", true}, -// {"http://www.foo---bar.com/", true}, -// {"mailto:someone@example.com", true}, -// {"irc://irc.server.org/channel", true}, -// {"irc://#channel@network", true}, -// {"/abs/test/dir", false}, -// {"./rel/test/dir", false}, -// } -// for i, test := range tests { - -// err := validate.Field(test.param, "url") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d URL failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "url") { -// t.Fatalf("Index: %d URL failed Error: %s", i, err) -// } -// } -// } - -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") -// } - -// func TestUri(t *testing.T) { - -// var tests = []struct { -// param string -// expected bool -// }{ -// {"http://foo.bar#com", true}, -// {"http://foobar.com", true}, -// {"https://foobar.com", true}, -// {"foobar.com", false}, -// {"http://foobar.coffee/", true}, -// {"http://foobar.中文网/", true}, -// {"http://foobar.org/", true}, -// {"http://foobar.org:8080/", true}, -// {"ftp://foobar.ru/", true}, -// {"http://user:pass@www.foobar.com/", true}, -// {"http://127.0.0.1/", true}, -// {"http://duckduckgo.com/?q=%2F", true}, -// {"http://localhost:3000/", true}, -// {"http://foobar.com/?foo=bar#baz=qux", true}, -// {"http://foobar.com?foo=bar", true}, -// {"http://www.xn--froschgrn-x9a.net/", true}, -// {"", false}, -// {"xyz://foobar.com", true}, -// {"invalid.", false}, -// {".com", false}, -// {"rtmp://foobar.com", true}, -// {"http://www.foo_bar.com/", true}, -// {"http://localhost:3000/", true}, -// {"http://foobar.com#baz=qux", true}, -// {"http://foobar.com/t$-_.+!*\\'(),", true}, -// {"http://www.foobar.com/~foobar", true}, -// {"http://www.-foobar.com/", true}, -// {"http://www.foo---bar.com/", true}, -// {"mailto:someone@example.com", true}, -// {"irc://irc.server.org/channel", true}, -// {"irc://#channel@network", true}, -// {"/abs/test/dir", true}, -// {"./rel/test/dir", false}, -// } -// for i, test := range tests { - -// err := validate.Field(test.param, "uri") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d URI failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uri") { -// t.Fatalf("Index: %d URI failed Error: %s", i, err) -// } -// } -// } - -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") -// } - -// func TestOrTag(t *testing.T) { -// s := "rgba(0,31,255,0.5)" -// err := validate.Field(s, "rgb|rgba") -// Equal(t, err, nil) - -// s = "rgba(0,31,255,0.5)" -// err = validate.Field(s, "rgb|rgba|len=18") -// Equal(t, err, nil) - -// s = "this ain't right" -// err = validate.Field(s, "rgb|rgba") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb|rgba") - -// s = "this ain't right" -// err = validate.Field(s, "rgb|rgba|len=10") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb|rgba|len") - -// s = "this is right" -// err = validate.Field(s, "rgb|rgba|len=13") -// Equal(t, err, nil) - -// s = "" -// err = validate.Field(s, "omitempty,rgb|rgba") -// Equal(t, err, nil) -// } - -// func TestHsla(t *testing.T) { - -// s := "hsla(360,100%,100%,1)" -// err := validate.Field(s, "hsla") -// Equal(t, err, nil) - -// s = "hsla(360,100%,100%,0.5)" -// err = validate.Field(s, "hsla") -// Equal(t, err, nil) - -// s = "hsla(0,0%,0%, 0)" -// err = validate.Field(s, "hsla") -// Equal(t, err, nil) - -// s = "hsl(361,100%,50%,1)" -// err = validate.Field(s, "hsla") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsla") - -// s = "hsl(361,100%,50%)" -// err = validate.Field(s, "hsla") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsla") - -// s = "hsla(361,100%,50%)" -// err = validate.Field(s, "hsla") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsla") - -// s = "hsla(360,101%,50%)" -// err = validate.Field(s, "hsla") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsla") - -// s = "hsla(360,100%,101%)" -// err = validate.Field(s, "hsla") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsla") - -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "hsla") }, "interface conversion: interface is int, not string") -// } - -// func TestHsl(t *testing.T) { - -// s := "hsl(360,100%,50%)" -// err := validate.Field(s, "hsl") -// Equal(t, err, nil) - -// s = "hsl(0,0%,0%)" -// err = validate.Field(s, "hsl") -// Equal(t, err, nil) - -// s = "hsl(361,100%,50%)" -// err = validate.Field(s, "hsl") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsl") +func TestUrl(t *testing.T) { + + var tests = []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.中文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", true}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"xyz://foobar.com", true}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", true}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", true}, + {"irc://irc.server.org/channel", true}, + {"irc://#channel@network", true}, + {"/abs/test/dir", false}, + {"./rel/test/dir", false}, + } + for i, test := range tests { -// s = "hsl(361,101%,50%)" -// err = validate.Field(s, "hsl") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsl") + errs := validate.Field(test.param, "url") -// s = "hsl(361,100%,101%)" -// err = validate.Field(s, "hsl") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsl") + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d URL failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d URL failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "url" { + t.Fatalf("Index: %d URL failed Error: %s", i, errs) + } + } + } + } -// s = "hsl(-10,100%,100%)" -// err = validate.Field(s, "hsl") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hsl") + i := 1 + PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") +} -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "hsl") }, "interface conversion: interface is int, not string") -// } +func TestUri(t *testing.T) { + + var tests = []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.中文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", true}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"xyz://foobar.com", true}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", true}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", true}, + {"irc://irc.server.org/channel", true}, + {"irc://#channel@network", true}, + {"/abs/test/dir", true}, + {"./rel/test/dir", false}, + } + for i, test := range tests { -// func TestRgba(t *testing.T) { + errs := validate.Field(test.param, "uri") -// s := "rgba(0,31,255,0.5)" -// err := validate.Field(s, "rgba") -// Equal(t, err, nil) + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d URI failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d URI failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "uri" { + t.Fatalf("Index: %d URI failed Error: %s", i, errs) + } + } + } + } -// s = "rgba(0,31,255,0.12)" -// err = validate.Field(s, "rgba") -// Equal(t, err, nil) + i := 1 + PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") +} -// s = "rgba(12%,55%,100%,0.12)" -// err = validate.Field(s, "rgba") -// Equal(t, err, nil) +func TestOrTag(t *testing.T) { + s := "rgba(0,31,255,0.5)" + errs := validate.Field(s, "rgb|rgba") + Equal(t, errs, nil) + + s = "rgba(0,31,255,0.5)" + errs = validate.Field(s, "rgb|rgba|len=18") + Equal(t, errs, nil) + + s = "this ain't right" + errs = validate.Field(s, "rgb|rgba") + NotEqual(t, errs, nil) + // Equal(t, err.Tag, "rgb|rgba") + AssertError(t, errs, "", "", "rgb|rgba") + + s = "this ain't right" + errs = validate.Field(s, "rgb|rgba|len=10") + NotEqual(t, errs, nil) + // Equal(t, err.Tag, "rgb|rgba|len") + AssertError(t, errs, "", "", "rgb|rgba|len") + + s = "this is right" + errs = validate.Field(s, "rgb|rgba|len=13") + Equal(t, errs, nil) + + s = "" + errs = validate.Field(s, "omitempty,rgb|rgba") + Equal(t, errs, nil) +} -// s = "rgba( 0, 31, 255, 0.5)" -// err = validate.Field(s, "rgba") -// Equal(t, err, nil) +func TestHsla(t *testing.T) { -// s = "rgba(12%,55,100%,0.12)" -// err = validate.Field(s, "rgba") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgba") + s := "hsla(360,100%,100%,1)" + errs := validate.Field(s, "hsla") + Equal(t, errs, nil) -// s = "rgb(0, 31, 255)" -// err = validate.Field(s, "rgba") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgba") + s = "hsla(360,100%,100%,0.5)" + errs = validate.Field(s, "hsla") + Equal(t, errs, nil) -// s = "rgb(1,349,275,0.5)" -// err = validate.Field(s, "rgba") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgba") + s = "hsla(0,0%,0%, 0)" + errs = validate.Field(s, "hsla") + Equal(t, errs, nil) -// s = "rgb(01,31,255,0.5)" -// err = validate.Field(s, "rgba") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgba") + s = "hsl(361,100%,50%,1)" + errs = validate.Field(s, "hsla") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsla") -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "rgba") }, "interface conversion: interface is int, not string") -// } + s = "hsl(361,100%,50%)" + errs = validate.Field(s, "hsla") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsla") -// func TestRgb(t *testing.T) { + s = "hsla(361,100%,50%)" + errs = validate.Field(s, "hsla") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsla") -// s := "rgb(0,31,255)" -// err := validate.Field(s, "rgb") -// Equal(t, err, nil) + s = "hsla(360,101%,50%)" + errs = validate.Field(s, "hsla") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsla") -// s = "rgb(0, 31, 255)" -// err = validate.Field(s, "rgb") -// Equal(t, err, nil) + s = "hsla(360,100%,101%)" + errs = validate.Field(s, "hsla") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsla") -// s = "rgb(10%, 50%, 100%)" -// err = validate.Field(s, "rgb") -// Equal(t, err, nil) + i := 1 + PanicMatches(t, func() { validate.Field(i, "hsla") }, "interface conversion: interface is int, not string") +} -// s = "rgb(10%, 50%, 55)" -// err = validate.Field(s, "rgb") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb") +func TestHsl(t *testing.T) { -// s = "rgb(1,349,275)" -// err = validate.Field(s, "rgb") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb") + s := "hsl(360,100%,50%)" + errs := validate.Field(s, "hsl") + Equal(t, errs, nil) -// s = "rgb(01,31,255)" -// err = validate.Field(s, "rgb") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb") + s = "hsl(0,0%,0%)" + errs = validate.Field(s, "hsl") + Equal(t, errs, nil) -// s = "rgba(0,31,255)" -// err = validate.Field(s, "rgb") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "rgb") + s = "hsl(361,100%,50%)" + errs = validate.Field(s, "hsl") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsl") -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "rgb") }, "interface conversion: interface is int, not string") -// } + s = "hsl(361,101%,50%)" + errs = validate.Field(s, "hsl") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsl") -// func TestEmail(t *testing.T) { + s = "hsl(361,100%,101%)" + errs = validate.Field(s, "hsl") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsl") -// s := "test@mail.com" -// err := validate.Field(s, "email") -// Equal(t, err, nil) + s = "hsl(-10,100%,100%)" + errs = validate.Field(s, "hsl") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hsl") -// s = "" -// err = validate.Field(s, "email") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "email") + i := 1 + PanicMatches(t, func() { validate.Field(i, "hsl") }, "interface conversion: interface is int, not string") +} -// s = "test@email" -// err = validate.Field(s, "email") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "email") +func TestRgba(t *testing.T) { -// s = "test@email." -// err = validate.Field(s, "email") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "email") + s := "rgba(0,31,255,0.5)" + errs := validate.Field(s, "rgba") + Equal(t, errs, nil) -// s = "@email.com" -// err = validate.Field(s, "email") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "email") + s = "rgba(0,31,255,0.12)" + errs = validate.Field(s, "rgba") + Equal(t, errs, nil) -// i := true -// PanicMatches(t, func() { validate.Field(i, "email") }, "interface conversion: interface is bool, not string") -// } + s = "rgba(12%,55%,100%,0.12)" + errs = validate.Field(s, "rgba") + Equal(t, errs, nil) -// func TestHexColor(t *testing.T) { + s = "rgba( 0, 31, 255, 0.5)" + errs = validate.Field(s, "rgba") + Equal(t, errs, nil) -// s := "#fff" -// err := validate.Field(s, "hexcolor") -// Equal(t, err, nil) + s = "rgba(12%,55,100%,0.12)" + errs = validate.Field(s, "rgba") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgba") -// s = "#c2c2c2" -// err = validate.Field(s, "hexcolor") -// Equal(t, err, nil) + s = "rgb(0, 31, 255)" + errs = validate.Field(s, "rgba") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgba") -// s = "fff" -// err = validate.Field(s, "hexcolor") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hexcolor") + s = "rgb(1,349,275,0.5)" + errs = validate.Field(s, "rgba") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgba") -// s = "fffFF" -// err = validate.Field(s, "hexcolor") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hexcolor") + s = "rgb(01,31,255,0.5)" + errs = validate.Field(s, "rgba") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgba") -// i := true -// PanicMatches(t, func() { validate.Field(i, "hexcolor") }, "interface conversion: interface is bool, not string") -// } + i := 1 + PanicMatches(t, func() { validate.Field(i, "rgba") }, "interface conversion: interface is int, not string") +} -// func TestHexadecimal(t *testing.T) { +func TestRgb(t *testing.T) { -// s := "ff0044" -// err := validate.Field(s, "hexadecimal") -// Equal(t, err, nil) + s := "rgb(0,31,255)" + errs := validate.Field(s, "rgb") + Equal(t, errs, nil) -// s = "abcdefg" -// err = validate.Field(s, "hexadecimal") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "hexadecimal") + s = "rgb(0, 31, 255)" + errs = validate.Field(s, "rgb") + Equal(t, errs, nil) -// i := true -// PanicMatches(t, func() { validate.Field(i, "hexadecimal") }, "interface conversion: interface is bool, not string") -// } + s = "rgb(10%, 50%, 100%)" + errs = validate.Field(s, "rgb") + Equal(t, errs, nil) -// func TestNumber(t *testing.T) { + s = "rgb(10%, 50%, 55)" + errs = validate.Field(s, "rgb") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgb") -// s := "1" -// err := validate.Field(s, "number") -// Equal(t, err, nil) + s = "rgb(1,349,275)" + errs = validate.Field(s, "rgb") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgb") -// s = "+1" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + s = "rgb(01,31,255)" + errs = validate.Field(s, "rgb") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgb") -// s = "-1" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + s = "rgba(0,31,255)" + errs = validate.Field(s, "rgb") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "rgb") -// s = "1.12" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + i := 1 + PanicMatches(t, func() { validate.Field(i, "rgb") }, "interface conversion: interface is int, not string") +} -// s = "+1.12" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") +func TestEmail(t *testing.T) { -// s = "-1.12" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + s := "test@mail.com" + errs := validate.Field(s, "email") + Equal(t, errs, nil) -// s = "1." -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + s = "" + errs = validate.Field(s, "email") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "email") -// s = "1.o" -// err = validate.Field(s, "number") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "number") + s = "test@email" + errs = validate.Field(s, "email") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "email") -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "number") }, "interface conversion: interface is int, not string") -// } + s = "test@email." + errs = validate.Field(s, "email") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "email") -// func TestNumeric(t *testing.T) { + s = "@email.com" + errs = validate.Field(s, "email") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "email") -// s := "1" -// err := validate.Field(s, "numeric") -// Equal(t, err, nil) + i := true + PanicMatches(t, func() { validate.Field(i, "email") }, "interface conversion: interface is bool, not string") +} -// s = "+1" -// err = validate.Field(s, "numeric") -// Equal(t, err, nil) +func TestHexColor(t *testing.T) { -// s = "-1" -// err = validate.Field(s, "numeric") -// Equal(t, err, nil) + s := "#fff" + errs := validate.Field(s, "hexcolor") + Equal(t, errs, nil) -// s = "1.12" -// err = validate.Field(s, "numeric") -// Equal(t, err, nil) + s = "#c2c2c2" + errs = validate.Field(s, "hexcolor") + Equal(t, errs, nil) -// s = "+1.12" -// err = validate.Field(s, "numeric") -// Equal(t, err, nil) + s = "fff" + errs = validate.Field(s, "hexcolor") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hexcolor") -// s = "-1.12" -// err = validate.Field(s, "numeric") -// Equal(t, err, nil) + s = "fffFF" + errs = validate.Field(s, "hexcolor") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hexcolor") -// s = "1." -// err = validate.Field(s, "numeric") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "numeric") + i := true + PanicMatches(t, func() { validate.Field(i, "hexcolor") }, "interface conversion: interface is bool, not string") +} -// s = "1.o" -// err = validate.Field(s, "numeric") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "numeric") +func TestHexadecimal(t *testing.T) { -// i := 1 -// PanicMatches(t, func() { validate.Field(i, "numeric") }, "interface conversion: interface is int, not string") -// } + s := "ff0044" + errs := validate.Field(s, "hexadecimal") + Equal(t, errs, nil) -// func TestAlphaNumeric(t *testing.T) { + s = "abcdefg" + errs = validate.Field(s, "hexadecimal") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "hexadecimal") -// s := "abcd123" -// err := validate.Field(s, "alphanum") -// Equal(t, err, nil) + i := true + PanicMatches(t, func() { validate.Field(i, "hexadecimal") }, "interface conversion: interface is bool, not string") +} -// s = "abc!23" -// err = validate.Field(s, "alphanum") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "alphanum") +func TestNumber(t *testing.T) { + + s := "1" + errs := validate.Field(s, "number") + Equal(t, errs, nil) + + s = "+1" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "-1" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "1.12" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "+1.12" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "-1.12" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "1." + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + s = "1.o" + errs = validate.Field(s, "number") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "number") + + i := 1 + PanicMatches(t, func() { validate.Field(i, "number") }, "interface conversion: interface is int, not string") +} -// PanicMatches(t, func() { validate.Field(1, "alphanum") }, "interface conversion: interface is int, not string") -// } +func TestNumeric(t *testing.T) { -// func TestAlpha(t *testing.T) { + s := "1" + errs := validate.Field(s, "numeric") + Equal(t, errs, nil) -// s := "abcd" -// err := validate.Field(s, "alpha") -// Equal(t, err, nil) + s = "+1" + errs = validate.Field(s, "numeric") + Equal(t, errs, nil) -// s = "abc1" -// err = validate.Field(s, "alpha") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "alpha") + s = "-1" + errs = validate.Field(s, "numeric") + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Field(1, "alpha") }, "interface conversion: interface is int, not string") -// } + s = "1.12" + errs = validate.Field(s, "numeric") + Equal(t, errs, nil) -// func TestFlattening(t *testing.T) { - -// tSuccess := &TestString{ -// Required: "Required", -// Len: "length==10", -// Min: "min=1", -// Max: "1234567890", -// MinMax: "12345", -// Lt: "012345678", -// Lte: "0123456789", -// Gt: "01234567890", -// Gte: "0123456789", -// OmitEmpty: "", -// Sub: &SubTest{ -// Test: "1", -// }, -// SubIgnore: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "1", -// }, -// Iface: &Impl{ -// F: "123", -// }, -// } - -// err1 := validate.Struct(tSuccess).Flatten() -// Equal(t, len(err1), 0) - -// tFail := &TestString{ -// Required: "", -// Len: "", -// Min: "", -// Max: "12345678901", -// MinMax: "", -// OmitEmpty: "12345678901", -// Sub: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "", -// }, -// Iface: &Impl{ -// F: "12", -// }, -// } + s = "+1.12" + errs = validate.Field(s, "numeric") + Equal(t, errs, nil) -// err2 := validate.Struct(tFail).Flatten() + s = "-1.12" + errs = validate.Field(s, "numeric") + Equal(t, errs, nil) -// // Assert Top Level -// NotEqual(t, err2, nil) + s = "1." + errs = validate.Field(s, "numeric") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "numeric") -// // Assert Fields -// AssertMapFieldError(t, err2, "Len", "len") -// AssertMapFieldError(t, err2, "Gt", "gt") -// AssertMapFieldError(t, err2, "Gte", "gte") + s = "1.o" + errs = validate.Field(s, "numeric") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "numeric") -// // Assert Struct Field -// AssertMapFieldError(t, err2, "Sub.Test", "required") + i := 1 + PanicMatches(t, func() { validate.Field(i, "numeric") }, "interface conversion: interface is int, not string") +} -// // Assert Anonymous Struct Field -// AssertMapFieldError(t, err2, "Anonymous.A", "required") +func TestAlphaNumeric(t *testing.T) { -// // Assert Interface Field -// AssertMapFieldError(t, err2, "Iface.F", "len") -// } + s := "abcd123" + errs := validate.Field(s, "alphanum") + Equal(t, errs, nil) -// func TestStructStringValidation(t *testing.T) { - -// validate.SetMaxStructPoolSize(11) - -// tSuccess := &TestString{ -// Required: "Required", -// Len: "length==10", -// Min: "min=1", -// Max: "1234567890", -// MinMax: "12345", -// Lt: "012345678", -// Lte: "0123456789", -// Gt: "01234567890", -// Gte: "0123456789", -// OmitEmpty: "", -// Sub: &SubTest{ -// Test: "1", -// }, -// SubIgnore: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "1", -// }, -// Iface: &Impl{ -// F: "123", -// }, -// } + s = "abc!23" + errs = validate.Field(s, "alphanum") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "alphanum") -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) + PanicMatches(t, func() { validate.Field(1, "alphanum") }, "interface conversion: interface is int, not string") +} -// tFail := &TestString{ -// Required: "", -// Len: "", -// Min: "", -// Max: "12345678901", -// MinMax: "", -// Lt: "0123456789", -// Lte: "01234567890", -// Gt: "1", -// Gte: "1", -// OmitEmpty: "12345678901", -// Sub: &SubTest{ -// Test: "", -// }, -// Anonymous: struct { -// A string `validate:"required"` -// }{ -// A: "", -// }, -// Iface: &Impl{ -// F: "12", -// }, -// } +func TestAlpha(t *testing.T) { -// err = validate.Struct(tFail) + s := "abcd" + errs := validate.Field(s, "alpha") + Equal(t, errs, nil) -// // Assert Top Level -// NotEqual(t, err, nil) -// Equal(t, err.Struct, "TestString") -// Equal(t, len(err.Errors), 10) -// Equal(t, len(err.StructErrors), 3) - -// // Assert Fields -// AssertFieldError(t, err, "Required", "required") -// AssertFieldError(t, err, "Len", "len") -// AssertFieldError(t, err, "Min", "min") -// AssertFieldError(t, err, "Max", "max") -// AssertFieldError(t, err, "MinMax", "min") -// AssertFieldError(t, err, "Gt", "gt") -// AssertFieldError(t, err, "Gte", "gte") -// AssertFieldError(t, err, "OmitEmpty", "max") - -// // Assert Anonymous embedded struct -// AssertStruct(t, err, "Anonymous", "") - -// // Assert SubTest embedded struct -// val := AssertStruct(t, err, "Sub", "SubTest") -// Equal(t, len(val.Errors), 1) -// Equal(t, len(val.StructErrors), 0) - -// AssertFieldError(t, val, "Test", "required") - -// errors := err.Error() -// NotEqual(t, errors, nil) -// } + s = "abc1" + errs = validate.Field(s, "alpha") + NotEqual(t, errs, nil) -// func TestStructInt32Validation(t *testing.T) { + AssertError(t, errs, "", "", "alpha") -// tSuccess := &TestInt32{ -// Required: 1, -// Len: 10, -// Min: 1, -// Max: 10, -// MinMax: 5, -// Lt: 9, -// Lte: 10, -// Gt: 11, -// Gte: 10, -// OmitEmpty: 0, -// } + PanicMatches(t, func() { validate.Field(1, "alpha") }, "interface conversion: interface is int, not string") +} -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) +func TestStructStringValidation(t *testing.T) { + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + Lt: "012345678", + Lte: "0123456789", + Gt: "01234567890", + Gte: "0123456789", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + Iface: &Impl{ + F: "123", + }, + } -// tFail := &TestInt32{ -// Required: 0, -// Len: 11, -// Min: -1, -// Max: 11, -// MinMax: -1, -// Lt: 10, -// Lte: 11, -// Gt: 10, -// Gte: 9, -// OmitEmpty: 11, -// } + errs := validate.Struct(tSuccess) + Equal(t, errs, nil) + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + Lt: "0123456789", + Lte: "01234567890", + Gt: "1", + Gte: "1", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + Iface: &Impl{ + F: "12", + }, + } -// err = validate.Struct(tFail) + errs = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, errs, nil) + Equal(t, len(errs), 13) + + // Assert Fields + AssertError(t, errs, "TestString.Required", "Required", "required") + AssertError(t, errs, "TestString.Len", "Len", "len") + AssertError(t, errs, "TestString.Min", "Min", "min") + AssertError(t, errs, "TestString.Max", "Max", "max") + AssertError(t, errs, "TestString.MinMax", "MinMax", "min") + AssertError(t, errs, "TestString.Lt", "Lt", "lt") + AssertError(t, errs, "TestString.Lte", "Lte", "lte") + AssertError(t, errs, "TestString.Gt", "Gt", "gt") + AssertError(t, errs, "TestString.Gte", "Gte", "gte") + AssertError(t, errs, "TestString.OmitEmpty", "OmitEmpty", "max") + + // Nested Struct Field Errs + AssertError(t, errs, "TestString.Anonymous.A", "A", "required") + AssertError(t, errs, "TestString.Sub.Test", "Test", "required") + AssertError(t, errs, "TestString.Iface.F", "F", "len") +} -// // Assert Top Level -// NotEqual(t, err, nil) -// Equal(t, err.Struct, "TestInt32") -// Equal(t, len(err.Errors), 10) -// Equal(t, len(err.StructErrors), 0) - -// // Assert Fields -// AssertFieldError(t, err, "Required", "required") -// AssertFieldError(t, err, "Len", "len") -// AssertFieldError(t, err, "Min", "min") -// AssertFieldError(t, err, "Max", "max") -// AssertFieldError(t, err, "MinMax", "min") -// AssertFieldError(t, err, "Lt", "lt") -// AssertFieldError(t, err, "Lte", "lte") -// AssertFieldError(t, err, "Gt", "gt") -// AssertFieldError(t, err, "Gte", "gte") -// AssertFieldError(t, err, "OmitEmpty", "max") -// } +func TestStructInt32Validation(t *testing.T) { + + tSuccess := &TestInt32{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + Lt: 9, + Lte: 10, + Gt: 11, + Gte: 10, + OmitEmpty: 0, + } -// func TestStructUint64Validation(t *testing.T) { + errs := validate.Struct(tSuccess) + Equal(t, errs, nil) + + tFail := &TestInt32{ + Required: 0, + Len: 11, + Min: -1, + Max: 11, + MinMax: -1, + Lt: 10, + Lte: 11, + Gt: 10, + Gte: 9, + OmitEmpty: 11, + } -// tSuccess := &TestUint64{ -// Required: 1, -// Len: 10, -// Min: 1, -// Max: 10, -// MinMax: 5, -// OmitEmpty: 0, -// } + errs = validate.Struct(tFail) + + // Assert Top Level + NotEqual(t, errs, nil) + Equal(t, len(errs), 10) + + // Assert Fields + AssertError(t, errs, "TestInt32.Required", "Required", "required") + AssertError(t, errs, "TestInt32.Len", "Len", "len") + AssertError(t, errs, "TestInt32.Min", "Min", "min") + AssertError(t, errs, "TestInt32.Max", "Max", "max") + AssertError(t, errs, "TestInt32.MinMax", "MinMax", "min") + AssertError(t, errs, "TestInt32.Lt", "Lt", "lt") + AssertError(t, errs, "TestInt32.Lte", "Lte", "lte") + AssertError(t, errs, "TestInt32.Gt", "Gt", "gt") + AssertError(t, errs, "TestInt32.Gte", "Gte", "gte") + AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "max") +} -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) +func TestStructUint64Validation(t *testing.T) { -// tFail := &TestUint64{ -// Required: 0, -// Len: 11, -// Min: 0, -// Max: 11, -// MinMax: 0, -// OmitEmpty: 11, -// } + tSuccess := &TestUint64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } -// err = validate.Struct(tFail) + errs := validate.Struct(tSuccess) + Equal(t, errs, nil) -// // Assert Top Level -// NotEqual(t, err, nil) -// Equal(t, err.Struct, "TestUint64") -// Equal(t, len(err.Errors), 6) -// Equal(t, len(err.StructErrors), 0) - -// // Assert Fields -// AssertFieldError(t, err, "Required", "required") -// AssertFieldError(t, err, "Len", "len") -// AssertFieldError(t, err, "Min", "min") -// AssertFieldError(t, err, "Max", "max") -// AssertFieldError(t, err, "MinMax", "min") -// AssertFieldError(t, err, "OmitEmpty", "max") -// } + tFail := &TestUint64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } -// func TestStructFloat64Validation(t *testing.T) { + errs = validate.Struct(tFail) -// tSuccess := &TestFloat64{ -// Required: 1, -// Len: 10, -// Min: 1, -// Max: 10, -// MinMax: 5, -// OmitEmpty: 0, -// } + // Assert Top Level + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) + // Assert Fields + AssertError(t, errs, "TestUint64.Required", "Required", "required") + AssertError(t, errs, "TestUint64.Len", "Len", "len") + AssertError(t, errs, "TestUint64.Min", "Min", "min") + AssertError(t, errs, "TestUint64.Max", "Max", "max") + AssertError(t, errs, "TestUint64.MinMax", "MinMax", "min") + AssertError(t, errs, "TestUint64.OmitEmpty", "OmitEmpty", "max") +} -// tFail := &TestFloat64{ -// Required: 0, -// Len: 11, -// Min: 0, -// Max: 11, -// MinMax: 0, -// OmitEmpty: 11, -// } +func TestStructFloat64Validation(t *testing.T) { -// err = validate.Struct(tFail) + tSuccess := &TestFloat64{ + Required: 1, + Len: 10, + Min: 1, + Max: 10, + MinMax: 5, + OmitEmpty: 0, + } -// // Assert Top Level -// NotEqual(t, err, nil) -// Equal(t, err.Struct, "TestFloat64") -// Equal(t, len(err.Errors), 6) -// Equal(t, len(err.StructErrors), 0) - -// // Assert Fields -// AssertFieldError(t, err, "Required", "required") -// AssertFieldError(t, err, "Len", "len") -// AssertFieldError(t, err, "Min", "min") -// AssertFieldError(t, err, "Max", "max") -// AssertFieldError(t, err, "MinMax", "min") -// AssertFieldError(t, err, "OmitEmpty", "max") -// } + errs := validate.Struct(tSuccess) + Equal(t, errs, nil) -// func TestStructSliceValidation(t *testing.T) { + tFail := &TestFloat64{ + Required: 0, + Len: 11, + Min: 0, + Max: 11, + MinMax: 0, + OmitEmpty: 11, + } -// tSuccess := &TestSlice{ -// Required: []int{1}, -// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, -// Min: []int{1, 2}, -// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, -// MinMax: []int{1, 2, 3, 4, 5}, -// OmitEmpty: []int{}, -// } + errs = validate.Struct(tFail) -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) + // Assert Top Level + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) -// tFail := &TestSlice{ -// Required: []int{}, -// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, -// Min: []int{}, -// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, -// MinMax: []int{}, -// OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, -// } + // Assert Fields + AssertError(t, errs, "TestFloat64.Required", "Required", "required") + AssertError(t, errs, "TestFloat64.Len", "Len", "len") + AssertError(t, errs, "TestFloat64.Min", "Min", "min") + AssertError(t, errs, "TestFloat64.Max", "Max", "max") + AssertError(t, errs, "TestFloat64.MinMax", "MinMax", "min") + AssertError(t, errs, "TestFloat64.OmitEmpty", "OmitEmpty", "max") +} -// err = validate.Struct(tFail) +func TestStructSliceValidation(t *testing.T) { -// // Assert Top Level -// NotEqual(t, err, nil) -// Equal(t, err.Struct, "TestSlice") -// Equal(t, len(err.Errors), 6) -// Equal(t, len(err.StructErrors), 0) - -// // Assert Fields -// AssertFieldError(t, err, "Required", "required") -// AssertFieldError(t, err, "Len", "len") -// AssertFieldError(t, err, "Min", "min") -// AssertFieldError(t, err, "Max", "max") -// AssertFieldError(t, err, "MinMax", "min") -// AssertFieldError(t, err, "OmitEmpty", "max") -// } + tSuccess := &TestSlice{ + Required: []int{1}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + Min: []int{1, 2}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + MinMax: []int{1, 2, 3, 4, 5}, + OmitEmpty: []int{}, + } -// func TestInvalidStruct(t *testing.T) { -// s := &SubTest{ -// Test: "1", -// } + errs := validate.Struct(tSuccess) + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Struct(s.Test) }, "interface passed for validation is not a struct") -// } + tFail := &TestSlice{ + Required: []int{}, + Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + Min: []int{}, + Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + MinMax: []int{}, + OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, + } -// func TestInvalidField(t *testing.T) { -// s := &SubTest{ -// Test: "1", -// } + errs = validate.Struct(tFail) + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) + + // Assert Field Errors + AssertError(t, errs, "TestSlice.Required", "Required", "required") + AssertError(t, errs, "TestSlice.Len", "Len", "len") + AssertError(t, errs, "TestSlice.Min", "Min", "min") + AssertError(t, errs, "TestSlice.Max", "Max", "max") + AssertError(t, errs, "TestSlice.MinMax", "MinMax", "min") + AssertError(t, errs, "TestSlice.OmitEmpty", "OmitEmpty", "max") +} -// PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to fieldWithNameAndValue") -// } +func TestInvalidStruct(t *testing.T) { + s := &SubTest{ + Test: "1", + } -// func TestInvalidTagField(t *testing.T) { -// s := &SubTest{ -// Test: "1", -// } + PanicMatches(t, func() { validate.Struct(s.Test) }, "value passed for validation is not a struct") +} -// PanicMatches(t, func() { validate.Field(s.Test, "") }, fmt.Sprintf("Invalid validation tag on field %s", "")) -// } +func TestInvalidField(t *testing.T) { + s := &SubTest{ + Test: "1", + } -// func TestInvalidValidatorFunction(t *testing.T) { -// s := &SubTest{ -// Test: "1", -// } + PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to traverseField") +} -// PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) -// } +func TestInvalidTagField(t *testing.T) { + s := &SubTest{ + Test: "1", + } -// func TestPoolObjectMaxSizeValidation(t *testing.T) { -// // this will ensure that the pool objects are let go -// // when the pool is saturated -// validate.SetMaxStructPoolSize(0) + PanicMatches(t, func() { validate.Field(s.Test, "") }, fmt.Sprintf("Invalid validation tag on field %s", "")) +} -// tSuccess := &TestSlice{ -// Required: []int{1}, -// Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, -// Min: []int{1, 2}, -// Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, -// MinMax: []int{1, 2, 3, 4, 5}, -// OmitEmpty: []int{}, -// } +func TestInvalidValidatorFunction(t *testing.T) { + s := &SubTest{ + Test: "1", + } -// for i := 0; i < 2; i++ { -// err := validate.Struct(tSuccess) -// Equal(t, err, nil) -// } -// } + PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) +} From 4ce39526f6d8fdf62988cdcfcc3d71fe6a13321e Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Wed, 15 Jul 2015 23:29:31 -0400 Subject: [PATCH 12/20] Update Test Case + fix errors Updated all tests to comply with new api. Fix a few bugs found with validation. --- validator.go | 38 +- validator_test.go | 4725 +++++++++++++++++++++------------------------ 2 files changed, 2205 insertions(+), 2558 deletions(-) diff --git a/validator.go b/validator.go index 27d8a63..5312c4e 100644 --- a/validator.go +++ b/validator.go @@ -10,10 +10,12 @@ package validator import ( "bytes" + "errors" "fmt" "reflect" "strings" "time" + "unicode" ) const ( @@ -27,7 +29,7 @@ const ( skipValidationTag = "-" diveTag = "dive" fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" - invaldField = "Invalid field passed to traverseField" + invalidField = "Invalid field passed to traverseField" arrayIndexFieldName = "%s[%d]" mapIndexFieldName = "%s[%v]" ) @@ -103,6 +105,24 @@ func New(config Config) *Validate { return &Validate{config: config} } +// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key +// NOTE: if the key already exists, it will get replaced. +// NOTE: this method is not thread-safe +func (v *Validate) RegisterValidation(key string, f Func) error { + + if len(key) == 0 { + return errors.New("Function Key cannot be empty") + } + + if f == nil { + return errors.New("Function cannot be empty") + } + + v.config.ValidationFuncs[key] = f + + return nil +} + // Field allows validation of a single field, still using tag style validation to check multiple errors func (v *Validate) Field(field interface{}, tag string) ValidationErrors { @@ -173,6 +193,11 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec for i := 0; i < numFields; i++ { fld = typ.Field(i) + + if !unicode.IsUpper(rune(fld.Name[0])) { + continue + } + v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.config.TagName), fld.Name) } } @@ -228,7 +253,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. switch kind { case reflect.Invalid: - panic(invaldField) + panic(invalidField) case reflect.Struct, reflect.Interface: if kind == reflect.Interface { @@ -251,7 +276,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. if typ != timeType && typ != timePtrType { - if isStructField { + if isStructField || kind == reflect.Struct { // required passed validationa above so stop here // if only validating the structs existance. @@ -262,8 +287,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false) return } - - panic(invaldField) + panic(invalidField) } FALLTHROUGH: fallthrough @@ -345,7 +369,7 @@ func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect. idxField = idxField.Elem() } - v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i)) + v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i)) } } @@ -359,7 +383,7 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va idxField = idxField.Elem() } - v.traverseField(topStruct, currentStruct, current, errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface())) + v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface())) } } diff --git a/validator_test.go b/validator_test.go index ea4656e..5103f38 100644 --- a/validator_test.go +++ b/validator_test.go @@ -6,6 +6,7 @@ import ( "reflect" "runtime" "testing" + "time" ) // NOTES: @@ -228,2775 +229,2399 @@ func AssertError(t *testing.T, errs ValidationErrors, key, field, expectedTag st EqualSkip(t, 2, val.Tag, expectedTag) } -func TestValidation(t *testing.T) { +func TestBadKeyValidation(t *testing.T) { + type Test struct { + Name string `validate:"required, "` + } + + tst := &Test{ + Name: "test", + } + + PanicMatches(t, func() { validate.Struct(tst) }, "Undefined validation function on field Name") +} + +func TestInterfaceErrValidation(t *testing.T) { + + var v1 interface{} + var v2 interface{} + + v2 = 1 + v1 = v2 + + errs := validate.Field(v1, "len=1") + Equal(t, errs, nil) + + errs = validate.Field(v2, "len=1") + Equal(t, errs, nil) + + type ExternalCMD struct { + Userid string `json:"userid"` + Action uint32 `json:"action"` + Data interface{} `json:"data,omitempty" validate:"required"` + } + + s := &ExternalCMD{ + Userid: "123456", + Action: 10000, + // Data: 1, + } + + errs = validate.Struct(s) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "ExternalCMD.Data", "Data", "required") + + type ExternalCMD2 struct { + Userid string `json:"userid"` + Action uint32 `json:"action"` + Data interface{} `json:"data,omitempty" validate:"len=1"` + } + + s2 := &ExternalCMD2{ + Userid: "123456", + Action: 10000, + // Data: 1, + } + + errs = validate.Struct(s2) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "ExternalCMD2.Data", "Data", "len") + + s3 := &ExternalCMD2{ + Userid: "123456", + Action: 10000, + Data: 2, + } + + errs = validate.Struct(s3) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "ExternalCMD2.Data", "Data", "len") + + type Inner struct { + Name string `validate:"required"` + } + + inner := &Inner{ + Name: "", + } + + s4 := &ExternalCMD{ + Userid: "123456", + Action: 10000, + Data: inner, + } + + errs = validate.Struct(s4) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "ExternalCMD.Data.Name", "Name", "required") + + type TestMapStructPtr struct { + Errs map[int]interface{} `validate:"gt=0,dive,len=2"` + } + + mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + + msp := &TestMapStructPtr{ + Errs: mip, + } + + errs = validate.Struct(msp) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "TestMapStructPtr.Errs[3]", "Errs[3]", "len") + + type TestMultiDimensionalStructs struct { + Errs [][]interface{} `validate:"gt=0,dive,dive"` + } + + var errStructArray [][]interface{} + + errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + + tms := &TestMultiDimensionalStructs{ + Errs: errStructArray, + } + + errs = validate.Struct(tms) + NotEqual(t, errs, nil) + Equal(t, len(errs), 4) + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "required") + + type TestMultiDimensionalStructsPtr2 struct { + Errs [][]*Inner `validate:"gt=0,dive,dive,required"` + } + + var errStructPtr2Array [][]*Inner + + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + + tmsp2 := &TestMultiDimensionalStructsPtr2{ + Errs: errStructPtr2Array, + } + + errs = validate.Struct(tmsp2) + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "required") + + m := map[int]interface{}{0: "ok", 3: "", 4: "ok"} + + errs = validate.Field(m, "len=3,dive,len=2") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "[3]", "[3]", "len") + + errs = validate.Field(m, "len=2,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "", "", "len") + + arr := []interface{}{"ok", "", "ok"} + + errs = validate.Field(arr, "len=3,dive,len=2") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "[1]", "[1]", "len") + + errs = validate.Field(arr, "len=2,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "", "", "len") + + type MyStruct struct { + A, B string + C interface{} + } + + var a MyStruct + + a.A = "value" + a.C = "nu" + + errs = validate.Struct(a) + Equal(t, errs, nil) +} + +func TestMapDiveValidation(t *testing.T) { + + n := map[int]interface{}{0: nil} + errs := validate.Field(n, "omitempty,required") + Equal(t, errs, nil) + + m := map[int]string{0: "ok", 3: "", 4: "ok"} + + errs = validate.Field(m, "len=3,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "[3]", "[3]", "required") + + errs = validate.Field(m, "len=2,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "", "", "len") + + type Inner struct { + Name string `validate:"required"` + } + + type TestMapStruct struct { + Errs map[int]Inner `validate:"gt=0,dive"` + } + + mi := map[int]Inner{0: Inner{"ok"}, 3: Inner{""}, 4: Inner{"ok"}} + + ms := &TestMapStruct{ + Errs: mi, + } + + errs = validate.Struct(ms) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "TestMapStruct.Errs[3].Name", "Name", "required") + + // for full test coverage + fmt.Sprint(errs.Error()) + + type TestMapTimeStruct struct { + Errs map[int]*time.Time `validate:"gt=0,dive,required"` + } + + t1 := time.Now().UTC() + + mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil} + + mt := &TestMapTimeStruct{ + Errs: mta, + } + + errs = validate.Struct(mt) + NotEqual(t, errs, nil) + Equal(t, len(errs), 2) + AssertError(t, errs, "TestMapTimeStruct.Errs[3]", "Errs[3]", "required") + AssertError(t, errs, "TestMapTimeStruct.Errs[4]", "Errs[4]", "required") + + type TestMapStructPtr struct { + Errs map[int]*Inner `validate:"gt=0,dive,required"` + } + + mip := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + + msp := &TestMapStructPtr{ + Errs: mip, + } + + errs = validate.Struct(msp) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "TestMapStructPtr.Errs[3]", "Errs[3]", "required") + + type TestMapStructPtr2 struct { + Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"` + } + + mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} + + msp2 := &TestMapStructPtr2{ + Errs: mip2, + } + + errs = validate.Struct(msp2) + Equal(t, errs, nil) +} + +func TestArrayDiveValidation(t *testing.T) { + + arr := []string{"ok", "", "ok"} + + errs := validate.Field(arr, "len=3,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "[1]", "[1]", "required") + + errs = validate.Field(arr, "len=2,dive,required") + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "", "", "len") + + type BadDive struct { + Name string `validate:"dive"` + } + + bd := &BadDive{ + Name: "TEST", + } + + PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") + + type Test struct { + Errs []string `validate:"gt=0,dive,required"` + } + + test := &Test{ + Errs: []string{"ok", "", "ok"}, + } + + errs = validate.Struct(test) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "Test.Errs[1]", "Errs[1]", "required") + + test = &Test{ + Errs: []string{"ok", "ok", ""}, + } + + errs = validate.Struct(test) + NotEqual(t, errs, nil) + Equal(t, len(errs), 1) + AssertError(t, errs, "Test.Errs[2]", "Errs[2]", "required") + + type TestMultiDimensional struct { + Errs [][]string `validate:"gt=0,dive,dive,required"` + } + + var errArray [][]string + + errArray = append(errArray, []string{"ok", "", ""}) + errArray = append(errArray, []string{"ok", "", ""}) + + tm := &TestMultiDimensional{ + Errs: errArray, + } + + errs = validate.Struct(tm) + NotEqual(t, errs, nil) + Equal(t, len(errs), 4) + AssertError(t, errs, "TestMultiDimensional.Errs[0][1]", "Errs[0][1]", "required") + AssertError(t, errs, "TestMultiDimensional.Errs[0][2]", "Errs[0][2]", "required") + AssertError(t, errs, "TestMultiDimensional.Errs[1][1]", "Errs[1][1]", "required") + AssertError(t, errs, "TestMultiDimensional.Errs[1][2]", "Errs[1][2]", "required") + + type Inner struct { + Name string `validate:"required"` + } + + type TestMultiDimensionalStructs struct { + Errs [][]Inner `validate:"gt=0,dive,dive"` + } + + var errStructArray [][]Inner + + errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) + errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) + + tms := &TestMultiDimensionalStructs{ + Errs: errStructArray, + } + + errs = validate.Struct(tms) + NotEqual(t, errs, nil) + Equal(t, len(errs), 4) + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "required") + + type TestMultiDimensionalStructsPtr struct { + Errs [][]*Inner `validate:"gt=0,dive,dive"` + } + + var errStructPtrArray [][]*Inner + + errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + + tmsp := &TestMultiDimensionalStructsPtr{ + Errs: errStructPtrArray, + } + + errs = validate.Struct(tmsp) + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[2][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[2][2]", "Errs[2][2]", "") + // for full test coverage + fmt.Sprint(errs.Error()) + + type TestMultiDimensionalStructsPtr2 struct { + Errs [][]*Inner `validate:"gt=0,dive,dive,required"` + } + + var errStructPtr2Array [][]*Inner + + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + + tmsp2 := &TestMultiDimensionalStructsPtr2{ + Errs: errStructPtr2Array, + } + + errs = validate.Struct(tmsp2) + NotEqual(t, errs, nil) + Equal(t, len(errs), 6) + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "required") + + type TestMultiDimensionalStructsPtr3 struct { + Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"` + } + + var errStructPtr3Array [][]*Inner + + errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) + errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) + + tmsp3 := &TestMultiDimensionalStructsPtr3{ + Errs: errStructPtr3Array, + } + + errs = validate.Struct(tmsp3) + NotEqual(t, errs, nil) + Equal(t, len(errs), 5) + AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][1].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][2].Name", "Name", "required") + AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[2][1].Name", "Name", "required") + + type TestMultiDimensionalTimeTime struct { + Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` + } + + var errTimePtr3Array [][]*time.Time + + t1 := time.Now().UTC() + t2 := time.Now().UTC() + t3 := time.Now().UTC().Add(time.Hour * 24) + + errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3}) + errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil}) + errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil}) + + tmtp3 := &TestMultiDimensionalTimeTime{ + Errs: errTimePtr3Array, + } + + errs = validate.Struct(tmtp3) + NotEqual(t, errs, nil) + Equal(t, len(errs), 3) + AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[1][2]", "Errs[1][2]", "required") + AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][1]", "Errs[2][1]", "required") + AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][2]", "Errs[2][2]", "required") + + type TestMultiDimensionalTimeTime2 struct { + Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` + } + + var errTimeArray [][]*time.Time + + t1 = time.Now().UTC() + t2 = time.Now().UTC() + t3 = time.Now().UTC().Add(time.Hour * 24) + + errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3}) + errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil}) + errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil}) + + tmtp := &TestMultiDimensionalTimeTime2{ + Errs: errTimeArray, + } + + errs = validate.Struct(tmtp) + NotEqual(t, errs, nil) + Equal(t, len(errs), 3) + AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[1][2]", "Errs[1][2]", "required") + AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][1]", "Errs[2][1]", "required") + AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][2]", "Errs[2][2]", "required") +} + +func TestNilStructPointerValidation(t *testing.T) { + type Inner struct { + Data string + } + + type Outer struct { + Inner *Inner `validate:"omitempty"` + } + + inner := &Inner{ + Data: "test", + } + + outer := &Outer{ + Inner: inner, + } + + errs := validate.Struct(outer) + Equal(t, errs, nil) + + outer = &Outer{ + Inner: nil, + } + + errs = validate.Struct(outer) + Equal(t, errs, nil) + + type Inner2 struct { + Data string + } + + type Outer2 struct { + Inner2 *Inner2 `validate:"required"` + } + + inner2 := &Inner2{ + Data: "test", + } + + outer2 := &Outer2{ + Inner2: inner2, + } + + errs = validate.Struct(outer2) + Equal(t, errs, nil) + + outer2 = &Outer2{ + Inner2: nil, + } + + errs = validate.Struct(outer2) + NotEqual(t, errs, nil) + AssertError(t, errs, "Outer2.Inner2", "Inner2", "required") + + type Inner3 struct { + Data string + } + + type Outer3 struct { + Inner3 *Inner3 + } + + inner3 := &Inner3{ + Data: "test", + } + + outer3 := &Outer3{ + Inner3: inner3, + } + + errs = validate.Struct(outer3) + Equal(t, errs, nil) + + type Inner4 struct { + Data string + } + + type Outer4 struct { + Inner4 *Inner4 `validate:"-"` + } + + inner4 := &Inner4{ + Data: "test", + } + + outer4 := &Outer4{ + Inner4: inner4, + } + + errs = validate.Struct(outer4) + Equal(t, errs, nil) +} + +func TestSSNValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"00-90-8787", false}, + {"66690-76", false}, + {"191 60 2869", true}, + {"191-60-2869", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "ssn") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d SSN failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d SSN failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "ssn" { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } + } + } + } +} + +func TestLongitudeValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"-180.000", true}, + {"180.1", false}, + {"+73.234", true}, + {"+382.3811", false}, + {"23.11111111", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "longitude") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Longitude failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Longitude failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "longitude" { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } + } + } + } +} + +func TestLatitudeValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"-90.000", true}, + {"+90", true}, + {"47.1231231", true}, + {"+99.9", false}, + {"108", false}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "latitude") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "latitude" { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } + } + } + } +} + +func TestDataURIValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, + {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, + {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, + {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + + "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + + "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + + "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + + "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + + "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, + {"data:image/png;base64,12345", false}, + {"", false}, + {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "datauri") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "datauri" { + t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) + } + } + } + } +} + +func TestMultibyteValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", true}, + {"abc", false}, + {"123", false}, + {"<>@;.-=", false}, + {"ひらがな・カタカナ、.漢字", true}, + {"あいうえお foobar", true}, + {"test@example.com", true}, + {"test@example.com", true}, + {"1234abcDExyz", true}, + {"カタカナ", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "multibyte") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "multibyte" { + t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) + } + } + } + } +} + +func TestPrintableASCIIValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", true}, + {"foobar", false}, + {"xyz098", false}, + {"123456", false}, + {"カタカナ", false}, + {"foobar", true}, + {"0987654321", true}, + {"test@example.com", true}, + {"1234abcDEF", true}, + {"newline\n", false}, + {"\x19test\x7F", false}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "printascii") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "printascii" { + t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) + } + } + } + } +} + +func TestASCIIValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", true}, + {"foobar", false}, + {"xyz098", false}, + {"123456", false}, + {"カタカナ", false}, + {"foobar", true}, + {"0987654321", true}, + {"test@example.com", true}, + {"1234abcDEF", true}, + {"", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "ascii") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "ascii" { + t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) + } + } + } + } +} + +func TestUUID5Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + + {"", false}, + {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, + {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, + {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, + {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, + {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "uuid5") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "uuid5" { + t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) + } + } + } + } +} + +func TestUUID4Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, + {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, + {"934859", false}, + {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, + {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "uuid4") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "uuid4" { + t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) + } + } + } + } +} + +func TestUUID3Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"412452646", false}, + {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, + {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, + {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "uuid3") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "uuid3" { + t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) + } + } + } + } +} + +func TestUUIDValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, + {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, + {"a987fbc94bed3078cf079141ba07c9f3", false}, + {"934859", false}, + {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, + {"aaaaaaaa-1111-1111-aaag-111111111111", false}, + {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "uuid") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d UUID failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "uuid" { + t.Fatalf("Index: %d UUID failed Error: %s", i, errs) + } + } + } + } +} + +func TestISBNValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"foo", false}, + {"3836221195", true}, + {"1-61729-085-8", true}, + {"3 423 21412 0", true}, + {"3 401 01319 X", true}, + {"9784873113685", true}, + {"978-4-87311-368-5", true}, + {"978 3401013190", true}, + {"978-3-8362-2119-1", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "isbn") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "isbn" { + t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) + } + } + } + } +} + +func TestISBN13Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"foo", false}, + {"3-8362-2119-5", false}, + {"01234567890ab", false}, + {"978 3 8362 2119 0", false}, + {"9784873113685", true}, + {"978-4-87311-368-5", true}, + {"978 3401013190", true}, + {"978-3-8362-2119-1", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "isbn13") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "isbn13" { + t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) + } + } + } + } +} + +func TestISBN10Validation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"", false}, + {"foo", false}, + {"3423214121", false}, + {"978-3836221191", false}, + {"3-423-21412-1", false}, + {"3 423 21412 1", false}, + {"3836221195", true}, + {"1-61729-085-8", true}, + {"3 423 21412 0", true}, + {"3 401 01319 X", true}, + } + + for i, test := range tests { + + errs := validate.Field(test.param, "isbn10") + + if test.expected == true { + if !IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) + } + } else { + if IsEqual(t, errs, nil) { + t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) + } else { + val := errs[""] + if val.Tag != "isbn10" { + t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) + } + } + } + } +} + +func TestExcludesRuneValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludesrune=☻"` + Tag string + ExpectedNil bool + }{ + {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, + {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestExcludesAllValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludesall=@!{}[]"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, + {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } + + username := "joeybloggs " + + errs := validate.Field(username, "excludesall=@ ") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "excludesall") + + excluded := "," + + errs = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "excludesall") + + excluded = "=" + + errs = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "excludesall") +} + +func TestExcludesValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"excludes=@"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, + {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsRuneValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"containsrune=☻"` + Tag string + ExpectedNil bool + }{ + {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, + {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsAnyValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"containsany=@!{}[]"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, + {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestContainsValidation(t *testing.T) { + + tests := []struct { + Value string `validate:"contains=@"` + Tag string + ExpectedNil bool + }{ + {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, + {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, + } + + for i, s := range tests { + errs := validate.Field(s.Value, s.Tag) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + + errs = validate.Struct(s) + + if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { + t.Fatalf("Index: %d failed Error: %s", i, errs) + } + } +} + +func TestIsNeFieldValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcdef" + i2 := 3 + j2 = 2 + k2 = 1.5434456 + arr2 := []string{"test", "test2"} + arr3 := []string{"test"} + now2 := now + + errs := validate.FieldWithValue(s, s2, "nefield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(i2, i, "nefield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(j2, j, "nefield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(k2, k, "nefield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(arr2, arr, "nefield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(now2, now, "nefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "nefield") + + errs = validate.FieldWithValue(arr3, arr, "nefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "nefield") + + type Test struct { + Start *time.Time `validate:"nefield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs = validate.Struct(sv) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Start", "Start", "nefield") + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + Equal(t, errs, nil) + + channel := make(chan string) + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "nefield") }, "struct or field value not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, channel, "nefield") }, "Bad field type chan string") + PanicMatches(t, func() { validate.FieldWithValue(5, now, "nefield") }, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"nefield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestIsNeValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcdef" + i := 3 + j = 2 + k = 1.5434 + arr := []string{"test"} + now := time.Now().UTC() + + errs := validate.Field(s, "ne=abcd") + Equal(t, errs, nil) + + errs = validate.Field(i, "ne=1") + Equal(t, errs, nil) + + errs = validate.Field(j, "ne=1") + Equal(t, errs, nil) + + errs = validate.Field(k, "ne=1.543") + Equal(t, errs, nil) + + errs = validate.Field(arr, "ne=2") + Equal(t, errs, nil) + + errs = validate.Field(arr, "ne=1") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ne") + + PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") +} + +func TestIsEqFieldValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcd" + i2 := 1 + j2 = 1 + k2 = 1.543 + arr2 := []string{"test"} + arr3 := []string{"test", "test2"} + now2 := now + + errs := validate.FieldWithValue(s, s2, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(i2, i, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(j2, j, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(k2, k, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(arr2, arr, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(now2, now, "eqfield") + Equal(t, errs, nil) + + errs = validate.FieldWithValue(arr3, arr, "eqfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "eqfield") - // type Inner struct { - // Name string - // } + type Test struct { + Start *time.Time `validate:"eqfield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs = validate.Struct(sv) + Equal(t, errs, nil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Start", "Start", "eqfield") + + channel := make(chan string) + + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "eqfield") }, "struct or field value not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, channel, "eqfield") }, "Bad field type chan string") + PanicMatches(t, func() { validate.FieldWithValue(5, now, "eqfield") }, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"eqfield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") +} + +func TestIsEqValidation(t *testing.T) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + errs := validate.Field(s, "eq=abcd") + Equal(t, errs, nil) + + errs = validate.Field(i, "eq=1") + Equal(t, errs, nil) + + errs = validate.Field(j, "eq=1") + Equal(t, errs, nil) + + errs = validate.Field(k, "eq=1.543") + Equal(t, errs, nil) + + errs = validate.Field(arr, "eq=1") + Equal(t, errs, nil) - // type Test struct { - // Name string `validate:"required"` - // Arr []string `validate:"required"` - // // Inner *Inner `validate:"required"` - // } + errs = validate.Field(arr, "eq=2") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "eq") + + PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") +} + +func TestBase64Validation(t *testing.T) { + + s := "dW5pY29ybg==" + + errs := validate.Field(s, "base64") + Equal(t, errs, nil) + + s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" + errs = validate.Field(s, "base64") + Equal(t, errs, nil) + + s = "" + errs = validate.Field(s, "base64") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "base64") + + s = "dW5pY29ybg== foo bar" + errs = validate.Field(s, "base64") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "base64") +} + +func TestStructOnlyValidation(t *testing.T) { - // // inner := &Inner{ - // // Name: "", - // // } + type Inner struct { + Test string `validate:"len=5"` + } + + type Outer struct { + InnerStruct *Inner `validate:"required,structonly"` + } + + outer := &Outer{ + InnerStruct: nil, + } + + errs := validate.Struct(outer) + NotEqual(t, errs, nil) - // tst := &Test{Name: "Dean"} - // // tst := &Test{Inner: inner} + inner := &Inner{ + Test: "1234", + } - // errs := validate.Struct(tst) + outer = &Outer{ + InnerStruct: inner, + } - // fmt.Println(errs) - // fmt.Println(errs == nil) + errs = validate.Struct(outer) + Equal(t, errs, nil) } -// func AssertStruct(t *testing.T, s *StructErrors, structFieldName string, expectedStructName string) *StructErrors { +func TestGtField(t *testing.T) { + + type TimeTest struct { + Start *time.Time `validate:"required,gt"` + End *time.Time `validate:"required,gt,gtfield=Start"` + } + + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) + + timeTest := &TimeTest{ + Start: &start, + End: &end, + } -// val, ok := s.StructErrors[structFieldName] -// EqualSkip(t, 2, ok, true) -// NotEqualSkip(t, 2, val, nil) -// EqualSkip(t, 2, val.Struct, expectedStructName) + errs := validate.Struct(timeTest) + Equal(t, errs, nil) -// return val -// } + timeTest = &TimeTest{ + Start: &end, + End: &start, + } -// func AssertFieldError(t *testing.T, s *StructErrors, field string, expectedTag string) { + errs = validate.Struct(timeTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "TimeTest.End", "End", "gtfield") -// val, ok := s.Errors[field] -// EqualSkip(t, 2, ok, true) -// NotEqualSkip(t, 2, val, nil) -// EqualSkip(t, 2, val.Field, field) -// EqualSkip(t, 2, val.Tag, expectedTag) -// } + errs = validate.FieldWithValue(&start, &end, "gtfield") + Equal(t, errs, nil) -// func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, expectedTag string) { + errs = validate.FieldWithValue(&end, &start, "gtfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtfield") -// val, ok := s[field] -// EqualSkip(t, 2, ok, true) -// NotEqualSkip(t, 2, val, nil) -// EqualSkip(t, 2, val.Field, field) -// EqualSkip(t, 2, val.Tag, expectedTag) -// } + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtfield=Val1"` + } -// func TestBadKeyValidation(t *testing.T) { -// type Test struct { -// Name string `validate:"required, "` -// } + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } -// tst := &Test{ -// Name: "test", -// } + errs = validate.Struct(intTest) + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Struct(tst) }, "Invalid validation tag on field Name") -// } + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } -// func TestFlattenValidation(t *testing.T) { + errs = validate.Struct(intTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "IntTest.Val2", "Val2", "gtfield") -// type Inner struct { -// Name string `validate:"required"` -// } + errs = validate.FieldWithValue(int(1), int(5), "gtfield") + Equal(t, errs, nil) -// type TestMultiDimensionalStructsPtr struct { -// Errs [][]*Inner `validate:"gt=0,dive,dive,required"` -// } + errs = validate.FieldWithValue(int(5), int(1), "gtfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtfield") -// var errStructPtrArray [][]*Inner + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtfield=Val1"` + } -// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{"ok"}}) + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } -// tmsp := &TestMultiDimensionalStructsPtr{ -// Errs: errStructPtrArray, -// } + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) -// errs := validate.Struct(tmsp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) -// // for full test coverage -// fmt.Sprint(errs.Error()) + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } -// fieldErr := errs.Errors["Errs"] -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, fieldErr.Field, "Errs") -// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) + errs = validate.Struct(uIntTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "UIntTest.Val2", "Val2", "gtfield") -// innerSlice1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSlice1.IsPlaceholderErr, true) -// Equal(t, innerSlice1.Field, "Errs[0]") - -// flatFieldErr, ok := fieldErr.Flatten()["[0][1].Inner.Name"] -// Equal(t, ok, true) -// Equal(t, flatFieldErr.Field, "Name") -// Equal(t, flatFieldErr.Tag, "required") - -// structErrFlatten, ok := errs.Flatten()["Errs[0][1].Inner.Name"] -// Equal(t, ok, true) -// Equal(t, structErrFlatten.Field, "Name") -// Equal(t, structErrFlatten.Tag, "required") - -// errStructPtrArray = [][]*Inner{} -// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, nil, &Inner{"ok"}}) - -// tmsp = &TestMultiDimensionalStructsPtr{ -// Errs: errStructPtrArray, -// } - -// errs = validate.Struct(tmsp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) -// // for full test coverage -// fmt.Sprint(errs.Error()) - -// fieldErr = errs.Errors["Errs"] -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, fieldErr.Field, "Errs") -// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - -// innerSlice1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSlice1.IsPlaceholderErr, true) -// Equal(t, innerSlice1.Field, "Errs[0]") - -// flatFieldErr, ok = fieldErr.Flatten()["[0][1]"] -// Equal(t, ok, true) -// Equal(t, flatFieldErr.Field, "Errs[0][1]") -// Equal(t, flatFieldErr.Tag, "required") - -// type TestMapStructPtr struct { -// Errs map[int]*Inner `validate:"gt=0,dive,required"` -// } - -// mip := map[int]*Inner{0: &Inner{"ok"}, 3: &Inner{""}, 4: &Inner{"ok"}} - -// msp := &TestMapStructPtr{ -// Errs: mip, -// } - -// errs = validate.Struct(msp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldError := errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 1) - -// innerStructError, ok := fieldError.MapErrs[3].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, innerStructError.Struct, "Inner") -// Equal(t, len(innerStructError.Errors), 1) - -// innerInnerFieldError, ok := innerStructError.Errors["Name"] -// Equal(t, ok, true) -// Equal(t, innerInnerFieldError.IsPlaceholderErr, false) -// Equal(t, innerInnerFieldError.IsSliceOrArray, false) -// Equal(t, innerInnerFieldError.Field, "Name") -// Equal(t, innerInnerFieldError.Tag, "required") - -// flatErrs, ok := errs.Flatten()["Errs[3].Inner.Name"] -// Equal(t, ok, true) -// Equal(t, flatErrs.Field, "Name") -// Equal(t, flatErrs.Tag, "required") - -// mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - -// msp2 := &TestMapStructPtr{ -// Errs: mip2, -// } - -// errs = validate.Struct(msp2) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldError = errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 1) - -// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerFieldError.IsPlaceholderErr, false) -// Equal(t, innerFieldError.IsSliceOrArray, false) -// Equal(t, innerFieldError.Field, "Errs[3]") -// Equal(t, innerFieldError.Tag, "required") - -// flatErrs, ok = errs.Flatten()["Errs[3]"] -// Equal(t, ok, true) -// Equal(t, flatErrs.Field, "Errs[3]") -// Equal(t, flatErrs.Tag, "required") - -// type TestMapInnerArrayStruct struct { -// Errs map[int][]string `validate:"gt=0,dive,dive,required"` -// } - -// mias := map[int][]string{0: []string{"ok"}, 3: []string{"ok", ""}, 4: []string{"ok"}} - -// mia := &TestMapInnerArrayStruct{ -// Errs: mias, -// } - -// errs = validate.Struct(mia) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// flatErrs, ok = errs.Flatten()["Errs[3][1]"] -// Equal(t, ok, true) -// Equal(t, flatErrs.Field, "Errs[3][1]") -// Equal(t, flatErrs.Tag, "required") -// } - -// func TestInterfaceErrValidation(t *testing.T) { - -// var v1 interface{} -// var v2 interface{} - -// v2 = 1 -// v1 = v2 - -// err := validate.Field(v1, "len=1") -// Equal(t, err, nil) -// err = validate.Field(v2, "len=1") -// Equal(t, err, nil) - -// type ExternalCMD struct { -// Userid string `json:"userid"` -// Action uint32 `json:"action"` -// Data interface{} `json:"data,omitempty" validate:"required"` -// } - -// s := &ExternalCMD{ -// Userid: "123456", -// Action: 10000, -// // Data: 1, -// } - -// errs := validate.Struct(s) -// NotEqual(t, errs, nil) -// Equal(t, errs.Errors["Data"].Field, "Data") -// Equal(t, errs.Errors["Data"].Tag, "required") - -// type ExternalCMD2 struct { -// Userid string `json:"userid"` -// Action uint32 `json:"action"` -// Data interface{} `json:"data,omitempty" validate:"len=1"` -// } - -// s2 := &ExternalCMD2{ -// Userid: "123456", -// Action: 10000, -// // Data: 1, -// } - -// errs = validate.Struct(s2) -// NotEqual(t, errs, nil) -// Equal(t, errs.Errors["Data"].Field, "Data") -// Equal(t, errs.Errors["Data"].Tag, "len") -// Equal(t, errs.Errors["Data"].Param, "1") - -// s3 := &ExternalCMD2{ -// Userid: "123456", -// Action: 10000, -// Data: 2, -// } - -// errs = validate.Struct(s3) -// NotEqual(t, errs, nil) -// Equal(t, errs.Errors["Data"].Field, "Data") -// Equal(t, errs.Errors["Data"].Tag, "len") -// Equal(t, errs.Errors["Data"].Param, "1") - -// type Inner struct { -// Name string `validate:"required"` -// } - -// inner := &Inner{ -// Name: "", -// } - -// s4 := &ExternalCMD{ -// Userid: "123456", -// Action: 10000, -// Data: inner, -// } - -// errs = validate.Struct(s4) -// NotEqual(t, errs, nil) -// Equal(t, errs.StructErrors["Data"].Struct, "Inner") -// Equal(t, errs.StructErrors["Data"].Errors["Name"].Field, "Name") -// Equal(t, errs.StructErrors["Data"].Errors["Name"].Tag, "required") - -// type TestMapStructPtr struct { -// Errs map[int]interface{} `validate:"gt=0,dive,len=2"` -// } - -// mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - -// msp := &TestMapStructPtr{ -// Errs: mip, -// } - -// errs = validate.Struct(msp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldError := errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 1) - -// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerFieldError.IsPlaceholderErr, false) -// Equal(t, innerFieldError.IsMap, false) -// Equal(t, len(innerFieldError.MapErrs), 0) -// Equal(t, innerFieldError.Field, "Errs[3]") -// Equal(t, innerFieldError.Tag, "len") - -// type TestMultiDimensionalStructs struct { -// Errs [][]interface{} `validate:"gt=0,dive,dive,len=2"` -// } - -// var errStructArray [][]interface{} - -// errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) - -// tms := &TestMultiDimensionalStructs{ -// Errs: errStructArray, -// } - -// errs = validate.Struct(tms) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok := errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - -// sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerInnersliceError1 := innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// type TestMultiDimensionalStructsPtr2 struct { -// Errs [][]*Inner `validate:"gt=0,dive,dive,len=2"` -// } - -// var errStructPtr2Array [][]*Inner - -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) - -// tmsp2 := &TestMultiDimensionalStructsPtr2{ -// Errs: errStructPtr2Array, -// } - -// errs = validate.Struct(tmsp2) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSliceStructError2.IsPlaceholderErr, false) -// Equal(t, innerSliceStructError2.IsSliceOrArray, false) -// Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) -// Equal(t, innerSliceStructError2.Field, "Errs[2][2]") - -// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// m := map[int]interface{}{0: "ok", 3: "", 4: "ok"} - -// err = validate.Field(m, "len=3,dive,len=2") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, true) -// Equal(t, err.IsMap, true) -// Equal(t, len(err.MapErrs), 1) - -// err = validate.Field(m, "len=2,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, false) -// Equal(t, err.IsMap, false) -// Equal(t, len(err.MapErrs), 0) - -// arr := []interface{}{"ok", "", "ok"} - -// err = validate.Field(arr, "len=3,dive,len=2") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, true) -// Equal(t, err.IsSliceOrArray, true) -// Equal(t, len(err.SliceOrArrayErrs), 1) - -// err = validate.Field(arr, "len=2,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, false) -// Equal(t, err.IsSliceOrArray, false) -// Equal(t, len(err.SliceOrArrayErrs), 0) - -// type MyStruct struct { -// A, B string -// C interface{} -// } - -// var a MyStruct - -// a.A = "value" -// a.C = "nu" - -// errs = validate.Struct(a) -// Equal(t, errs, nil) -// } - -// func TestMapDiveValidation(t *testing.T) { - -// n := map[int]interface{}{0: nil} -// err := validate.Field(n, "omitempty,required") - -// m := map[int]string{0: "ok", 3: "", 4: "ok"} - -// err = validate.Field(m, "len=3,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, true) -// Equal(t, err.IsMap, true) -// Equal(t, len(err.MapErrs), 1) - -// err = validate.Field(m, "len=2,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, false) -// Equal(t, err.IsMap, false) -// Equal(t, len(err.MapErrs), 0) - -// type Inner struct { -// Name string `validate:"required"` -// } - -// type TestMapStruct struct { -// Errs map[int]Inner `validate:"gt=0,dive"` -// } - -// mi := map[int]Inner{0: Inner{"ok"}, 3: Inner{""}, 4: Inner{"ok"}} - -// ms := &TestMapStruct{ -// Errs: mi, -// } - -// errs := validate.Struct(ms) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) -// // for full test coverage -// fmt.Sprint(errs.Error()) - -// fieldError := errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 1) - -// structErr, ok := fieldError.MapErrs[3].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(structErr.Errors), 1) - -// innerErr := structErr.Errors["Name"] -// Equal(t, innerErr.IsPlaceholderErr, false) -// Equal(t, innerErr.IsMap, false) -// Equal(t, len(innerErr.MapErrs), 0) -// Equal(t, innerErr.Field, "Name") -// Equal(t, innerErr.Tag, "required") - -// type TestMapTimeStruct struct { -// Errs map[int]*time.Time `validate:"gt=0,dive,required"` -// } - -// t1 := time.Now().UTC() - -// mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil} - -// mt := &TestMapTimeStruct{ -// Errs: mta, -// } - -// errs = validate.Struct(mt) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldError = errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 2) - -// innerErr, ok = fieldError.MapErrs[3].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerErr.IsPlaceholderErr, false) -// Equal(t, innerErr.IsMap, false) -// Equal(t, len(innerErr.MapErrs), 0) -// Equal(t, innerErr.Field, "Errs[3]") -// Equal(t, innerErr.Tag, "required") - -// type TestMapStructPtr struct { -// Errs map[int]*Inner `validate:"gt=0,dive,required"` -// } - -// mip := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - -// msp := &TestMapStructPtr{ -// Errs: mip, -// } - -// errs = validate.Struct(msp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldError = errs.Errors["Errs"] -// Equal(t, fieldError.IsPlaceholderErr, true) -// Equal(t, fieldError.IsMap, true) -// Equal(t, len(fieldError.MapErrs), 1) - -// innerFieldError, ok := fieldError.MapErrs[3].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerFieldError.IsPlaceholderErr, false) -// Equal(t, innerFieldError.IsMap, false) -// Equal(t, len(innerFieldError.MapErrs), 0) -// Equal(t, innerFieldError.Field, "Errs[3]") -// Equal(t, innerFieldError.Tag, "required") - -// type TestMapStructPtr2 struct { -// Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"` -// } - -// mip2 := map[int]*Inner{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} - -// msp2 := &TestMapStructPtr2{ -// Errs: mip2, -// } - -// errs = validate.Struct(msp2) -// Equal(t, errs, nil) -// } - -// func TestArrayDiveValidation(t *testing.T) { - -// arr := []string{"ok", "", "ok"} - -// err := validate.Field(arr, "len=3,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, true) -// Equal(t, err.IsSliceOrArray, true) -// Equal(t, len(err.SliceOrArrayErrs), 1) - -// // flat := err.Flatten() -// // fe, ok := flat["[1]"] -// // Equal(t, ok, true) -// // Equal(t, fe.Tag, "required") - -// err = validate.Field(arr, "len=2,dive,required") -// NotEqual(t, err, nil) -// Equal(t, err.IsPlaceholderErr, false) -// Equal(t, err.IsSliceOrArray, false) -// Equal(t, len(err.SliceOrArrayErrs), 0) - -// type BadDive struct { -// Name string `validate:"dive"` -// } - -// bd := &BadDive{ -// Name: "TEST", -// } - -// PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") - -// type Test struct { -// Errs []string `validate:"gt=0,dive,required"` -// } - -// test := &Test{ -// Errs: []string{"ok", "", "ok"}, -// } - -// errs := validate.Struct(test) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// // flat = errs.Flatten() -// // me, ok := flat["Errs[1]"] -// // Equal(t, ok, true) -// // Equal(t, me.Field, "Errs[1]") -// // Equal(t, me.Tag, "required") - -// fieldErr, ok := errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - -// innerErr, ok := fieldErr.SliceOrArrayErrs[1].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerErr.Tag, required) -// Equal(t, innerErr.IsPlaceholderErr, false) -// Equal(t, innerErr.Field, "Errs[1]") - -// test = &Test{ -// Errs: []string{"ok", "ok", ""}, -// } - -// errs = validate.Struct(test) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 1) - -// innerErr, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerErr.Tag, required) -// Equal(t, innerErr.IsPlaceholderErr, false) -// Equal(t, innerErr.Field, "Errs[2]") - -// type TestMultiDimensional struct { -// Errs [][]string `validate:"gt=0,dive,dive,required"` -// } - -// var errArray [][]string - -// errArray = append(errArray, []string{"ok", "", ""}) -// errArray = append(errArray, []string{"ok", "", ""}) - -// tm := &TestMultiDimensional{ -// Errs: errArray, -// } - -// errs = validate.Struct(tm) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - -// sliceError1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) -// Equal(t, sliceError1.Field, "Errs[0]") - -// innerSliceError1, ok := sliceError1.SliceOrArrayErrs[1].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSliceError1.IsPlaceholderErr, false) -// Equal(t, innerSliceError1.Tag, required) -// Equal(t, innerSliceError1.IsSliceOrArray, false) -// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) -// Equal(t, innerSliceError1.Field, "Errs[0][1]") - -// type Inner struct { -// Name string `validate:"required"` -// } - -// type TestMultiDimensionalStructs struct { -// Errs [][]Inner `validate:"gt=0,dive,dive"` -// } - -// var errStructArray [][]Inner - -// errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) -// errStructArray = append(errStructArray, []Inner{Inner{"ok"}, Inner{""}, Inner{""}}) - -// tms := &TestMultiDimensionalStructs{ -// Errs: errStructArray, -// } - -// errs = validate.Struct(tms) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok := sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerInnersliceError1 := innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// type TestMultiDimensionalStructsPtr struct { -// Errs [][]*Inner `validate:"gt=0,dive,dive"` -// } - -// var errStructPtrArray [][]*Inner - -// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) - -// tmsp := &TestMultiDimensionalStructsPtr{ -// Errs: errStructPtrArray, -// } - -// errs = validate.Struct(tmsp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) -// // for full test coverage -// fmt.Sprint(errs.Error()) - -// // flat := errs.Flatten() -// // // fmt.Println(errs) -// // fmt.Println(flat) -// // expect Errs[0][1].Inner.Name -// // me, ok := flat["Errs[1]"] -// // Equal(t, ok, true) -// // Equal(t, me.Field, "Errs[1]") -// // Equal(t, me.Tag, "required") - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - -// // flat := fieldErr.Flatten() -// // fmt.Println(errs) -// // fmt.Println(flat) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// type TestMultiDimensionalStructsPtr2 struct { -// Errs [][]*Inner `validate:"gt=0,dive,dive,required"` -// } - -// var errStructPtr2Array [][]*Inner - -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr2Array = append(errStructPtr2Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) - -// tmsp2 := &TestMultiDimensionalStructsPtr2{ -// Errs: errStructPtr2Array, -// } - -// errs = validate.Struct(tmsp2) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerSliceStructError2, ok := sliceError1.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSliceStructError2.IsPlaceholderErr, false) -// Equal(t, innerSliceStructError2.IsSliceOrArray, false) -// Equal(t, len(innerSliceStructError2.SliceOrArrayErrs), 0) -// Equal(t, innerSliceStructError2.Field, "Errs[2][2]") - -// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// type TestMultiDimensionalStructsPtr3 struct { -// Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"` -// } - -// var errStructPtr3Array [][]*Inner - -// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{""}}) -// errStructPtr3Array = append(errStructPtr3Array, []*Inner{&Inner{"ok"}, &Inner{""}, nil}) - -// tmsp3 := &TestMultiDimensionalStructsPtr3{ -// Errs: errStructPtr3Array, -// } - -// errs = validate.Struct(tmsp3) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 3) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceStructError1, ok = sliceError1.SliceOrArrayErrs[1].(*StructErrors) -// Equal(t, ok, true) -// Equal(t, len(innerSliceStructError1.Errors), 1) - -// innerInnersliceError1 = innerSliceStructError1.Errors["Name"] -// Equal(t, innerInnersliceError1.IsPlaceholderErr, false) -// Equal(t, innerInnersliceError1.IsSliceOrArray, false) -// Equal(t, len(innerInnersliceError1.SliceOrArrayErrs), 0) - -// type TestMultiDimensionalTimeTime struct { -// Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` -// } - -// var errTimePtr3Array [][]*time.Time - -// t1 := time.Now().UTC() -// t2 := time.Now().UTC() -// t3 := time.Now().UTC().Add(time.Hour * 24) - -// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3}) -// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil}) -// errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil}) - -// tmtp3 := &TestMultiDimensionalTimeTime{ -// Errs: errTimePtr3Array, -// } - -// errs = validate.Struct(tmtp3) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSliceError1.IsPlaceholderErr, false) -// Equal(t, innerSliceError1.IsSliceOrArray, false) -// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) -// Equal(t, innerSliceError1.Field, "Errs[2][1]") -// Equal(t, innerSliceError1.Tag, required) - -// type TestMultiDimensionalTimeTime2 struct { -// Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` -// } - -// var errTimeArray [][]*time.Time - -// t1 = time.Now().UTC() -// t2 = time.Now().UTC() -// t3 = time.Now().UTC().Add(time.Hour * 24) - -// errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3}) -// errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil}) -// errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil}) - -// tmtp := &TestMultiDimensionalTimeTime2{ -// Errs: errTimeArray, -// } - -// errs = validate.Struct(tmtp) -// NotEqual(t, errs, nil) -// Equal(t, len(errs.Errors), 1) - -// fieldErr, ok = errs.Errors["Errs"] -// Equal(t, ok, true) -// Equal(t, fieldErr.IsPlaceholderErr, true) -// Equal(t, fieldErr.IsSliceOrArray, true) -// Equal(t, len(fieldErr.SliceOrArrayErrs), 2) - -// sliceError1, ok = fieldErr.SliceOrArrayErrs[2].(*FieldError) -// Equal(t, ok, true) -// Equal(t, sliceError1.IsPlaceholderErr, true) -// Equal(t, sliceError1.IsSliceOrArray, true) -// Equal(t, len(sliceError1.SliceOrArrayErrs), 2) - -// innerSliceError1, ok = sliceError1.SliceOrArrayErrs[1].(*FieldError) -// Equal(t, ok, true) -// Equal(t, innerSliceError1.IsPlaceholderErr, false) -// Equal(t, innerSliceError1.IsSliceOrArray, false) -// Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0) -// Equal(t, innerSliceError1.Field, "Errs[2][1]") -// Equal(t, innerSliceError1.Tag, required) -// } - -// func TestNilStructPointerValidation(t *testing.T) { -// type Inner struct { -// Data string -// } - -// type Outer struct { -// Inner *Inner `validate:"omitempty"` -// } - -// inner := &Inner{ -// Data: "test", -// } - -// outer := &Outer{ -// Inner: inner, -// } - -// errs := validate.Struct(outer) -// Equal(t, errs, nil) - -// outer = &Outer{ -// Inner: nil, -// } - -// errs = validate.Struct(outer) -// Equal(t, errs, nil) - -// type Inner2 struct { -// Data string -// } - -// type Outer2 struct { -// Inner2 *Inner2 `validate:"required"` -// } - -// inner2 := &Inner2{ -// Data: "test", -// } - -// outer2 := &Outer2{ -// Inner2: inner2, -// } - -// errs = validate.Struct(outer2) -// Equal(t, errs, nil) - -// outer2 = &Outer2{ -// Inner2: nil, -// } - -// errs = validate.Struct(outer2) -// NotEqual(t, errs, nil) - -// type Inner3 struct { -// Data string -// } - -// type Outer3 struct { -// Inner3 *Inner3 -// } - -// inner3 := &Inner3{ -// Data: "test", -// } - -// outer3 := &Outer3{ -// Inner3: inner3, -// } - -// errs = validate.Struct(outer3) -// Equal(t, errs, nil) - -// type Inner4 struct { -// Data string -// } - -// type Outer4 struct { -// Inner4 *Inner4 `validate:"-"` -// } - -// inner4 := &Inner4{ -// Data: "test", -// } - -// outer4 := &Outer4{ -// Inner4: inner4, -// } - -// errs = validate.Struct(outer4) -// Equal(t, errs, nil) -// } - -// func TestSSNValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"00-90-8787", false}, -// {"66690-76", false}, -// {"191 60 2869", true}, -// {"191-60-2869", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "ssn") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d SSN failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ssn") { -// t.Fatalf("Index: %d SSN failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestLongitudeValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"-180.000", true}, -// {"180.1", false}, -// {"+73.234", true}, -// {"+382.3811", false}, -// {"23.11111111", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "longitude") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d Longitude failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "longitude") { -// t.Fatalf("Index: %d Longitude failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestLatitudeValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"-90.000", true}, -// {"+90", true}, -// {"47.1231231", true}, -// {"+99.9", false}, -// {"108", false}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "latitude") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d Latitude failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "latitude") { -// t.Fatalf("Index: %d Latitude failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestDataURIValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, -// {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, -// {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, -// {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + -// "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + -// "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + -// "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + -// "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + -// "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, -// {"data:image/png;base64,12345", false}, -// {"", false}, -// {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "datauri") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d DataURI failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "datauri") { -// t.Fatalf("Index: %d DataURI failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestMultibyteValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", true}, -// {"abc", false}, -// {"123", false}, -// {"<>@;.-=", false}, -// {"ひらがな・カタカナ、.漢字", true}, -// {"あいうえお foobar", true}, -// {"test@example.com", true}, -// {"test@example.com", true}, -// {"1234abcDExyz", true}, -// {"カタカナ", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "multibyte") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "multibyte") { -// t.Fatalf("Index: %d Multibyte failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestPrintableASCIIValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", true}, -// {"foobar", false}, -// {"xyz098", false}, -// {"123456", false}, -// {"カタカナ", false}, -// {"foobar", true}, -// {"0987654321", true}, -// {"test@example.com", true}, -// {"1234abcDEF", true}, -// {"newline\n", false}, -// {"\x19test\x7F", false}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "printascii") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "printascii") { -// t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestASCIIValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", true}, -// {"foobar", false}, -// {"xyz098", false}, -// {"123456", false}, -// {"カタカナ", false}, -// {"foobar", true}, -// {"0987654321", true}, -// {"test@example.com", true}, -// {"1234abcDEF", true}, -// {"", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "ascii") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d ASCII failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "ascii") { -// t.Fatalf("Index: %d ASCII failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestUUID5Validation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ - -// {"", false}, -// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, -// {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, -// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, -// {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, -// {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "uuid5") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid5") { -// t.Fatalf("Index: %d UUID5 failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestUUID4Validation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, -// {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, -// {"934859", false}, -// {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, -// {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "uuid4") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid4") { -// t.Fatalf("Index: %d UUID4 failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestUUID3Validation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"412452646", false}, -// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, -// {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, -// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "uuid3") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid3") { -// t.Fatalf("Index: %d UUID3 failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestUUIDValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, -// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, -// {"a987fbc94bed3078cf079141ba07c9f3", false}, -// {"934859", false}, -// {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, -// {"aaaaaaaa-1111-1111-aaag-111111111111", false}, -// {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "uuid") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d UUID failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "uuid") { -// t.Fatalf("Index: %d UUID failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestISBNValidation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"foo", false}, -// {"3836221195", true}, -// {"1-61729-085-8", true}, -// {"3 423 21412 0", true}, -// {"3 401 01319 X", true}, -// {"9784873113685", true}, -// {"978-4-87311-368-5", true}, -// {"978 3401013190", true}, -// {"978-3-8362-2119-1", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "isbn") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d ISBN failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn") { -// t.Fatalf("Index: %d ISBN failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestISBN13Validation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"foo", false}, -// {"3-8362-2119-5", false}, -// {"01234567890ab", false}, -// {"978 3 8362 2119 0", false}, -// {"9784873113685", true}, -// {"978-4-87311-368-5", true}, -// {"978 3401013190", true}, -// {"978-3-8362-2119-1", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "isbn13") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn13") { -// t.Fatalf("Index: %d ISBN13 failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestISBN10Validation(t *testing.T) { -// tests := []struct { -// param string -// expected bool -// }{ -// {"", false}, -// {"foo", false}, -// {"3423214121", false}, -// {"978-3836221191", false}, -// {"3-423-21412-1", false}, -// {"3 423 21412 1", false}, -// {"3836221195", true}, -// {"1-61729-085-8", true}, -// {"3 423 21412 0", true}, -// {"3 401 01319 X", true}, -// } - -// for i, test := range tests { - -// err := validate.Field(test.param, "isbn10") - -// if test.expected == true { -// if !IsEqual(t, err, nil) { -// t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) -// } -// } else { -// if IsEqual(t, err, nil) || !IsEqual(t, err.Tag, "isbn10") { -// t.Fatalf("Index: %d ISBN10 failed Error: %s", i, err) -// } -// } -// } -// } - -// func TestExcludesRuneValidation(t *testing.T) { - -// tests := []struct { -// Value string `validate:"excludesrune=☻"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, -// {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, -// } - -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) - -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } - -// errs := validate.Struct(s) - -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } -// } - -// func TestExcludesAllValidation(t *testing.T) { - -// tests := []struct { -// Value string `validate:"excludesall=@!{}[]"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, -// {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, -// } - -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) - -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } - -// errs := validate.Struct(s) - -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } - -// username := "joeybloggs " - -// err := validate.Field(username, "excludesall=@ ") -// NotEqual(t, err, nil) - -// excluded := "," - -// err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") -// NotEqual(t, err, nil) - -// excluded = "=" - -// err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") -// NotEqual(t, err, nil) -// } - -// func TestExcludesValidation(t *testing.T) { - -// tests := []struct { -// Value string `validate:"excludes=@"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, -// {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, -// } - -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) - -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } - -// errs := validate.Struct(s) - -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } -// } - -// func TestContainsRuneValidation(t *testing.T) { - -// tests := []struct { -// Value string `validate:"containsrune=☻"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, -// {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, -// } - -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) - -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } + errs = validate.FieldWithValue(uint(1), uint(5), "gtfield") + Equal(t, errs, nil) -// errs := validate.Struct(s) + errs = validate.FieldWithValue(uint(5), uint(1), "gtfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtfield") -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } -// } - -// func TestContainsAnyValidation(t *testing.T) { - -// tests := []struct { -// Value string `validate:"containsany=@!{}[]"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, -// {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, -// } - -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtfield=Val1"` + } -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } -// errs := validate.Struct(s) + errs = validate.Struct(floatTest) + Equal(t, errs, nil) -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } -// } + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } -// func TestContainsValidation(t *testing.T) { + errs = validate.Struct(floatTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "FloatTest.Val2", "Val2", "gtfield") -// tests := []struct { -// Value string `validate:"contains=@"` -// Tag string -// ExpectedNil bool -// }{ -// {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, -// {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, -// } + errs = validate.FieldWithValue(float32(1), float32(5), "gtfield") + Equal(t, errs, nil) -// for i, s := range tests { -// err := validate.Field(s.Value, s.Tag) - -// if (s.ExpectedNil && err != nil) || (!s.ExpectedNil && err == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, err) -// } - -// errs := validate.Struct(s) - -// if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { -// t.Fatalf("Index: %d failed Error: %s", i, errs) -// } -// } -// } - -// func TestIsNeFieldValidation(t *testing.T) { - -// var j uint64 -// var k float64 -// s := "abcd" -// i := 1 -// j = 1 -// k = 1.543 -// arr := []string{"test"} -// now := time.Now().UTC() - -// var j2 uint64 -// var k2 float64 -// s2 := "abcdef" -// i2 := 3 -// j2 = 2 -// k2 = 1.5434456 -// arr2 := []string{"test", "test2"} -// arr3 := []string{"test"} -// now2 := now + errs = validate.FieldWithValue(float32(5), float32(1), "gtfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtfield") -// err := validate.FieldWithValue(s, s2, "nefield") -// Equal(t, err, nil) - -// err = validate.FieldWithValue(i2, i, "nefield") -// Equal(t, err, nil) - -// err = validate.FieldWithValue(j2, j, "nefield") -// Equal(t, err, nil) - -// err = validate.FieldWithValue(k2, k, "nefield") -// Equal(t, err, nil) - -// err = validate.FieldWithValue(arr2, arr, "nefield") -// Equal(t, err, nil) - -// err = validate.FieldWithValue(now2, now, "nefield") -// NotEqual(t, err, nil) - -// err = validate.FieldWithValue(arr3, arr, "nefield") -// NotEqual(t, err, nil) - -// type Test struct { -// Start *time.Time `validate:"nefield=End"` -// End *time.Time -// } - -// sv := &Test{ -// Start: &now, -// End: &now, -// } - -// errs := validate.Struct(sv) -// NotEqual(t, errs, nil) + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtfield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtfield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtfield") }, "Bad Top Level field type") -// now3 := time.Now().UTC() + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtfield=NonExistantField"` + } -// sv = &Test{ -// Start: &now, -// End: &now3, -// } + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } -// errs = validate.Struct(sv) -// Equal(t, errs, nil) + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} -// channel := make(chan string) - -// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "nefield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(5, channel, "nefield") }, "Bad field type chan string") -// PanicMatches(t, func() { validate.FieldWithValue(5, now, "nefield") }, "Bad Top Level field type") +func TestLtField(t *testing.T) { -// type Test2 struct { -// Start *time.Time `validate:"nefield=NonExistantField"` -// End *time.Time -// } + type TimeTest struct { + Start *time.Time `validate:"required,lt,ltfield=End"` + End *time.Time `validate:"required,lt"` + } -// sv2 := &Test2{ -// Start: &now, -// End: &now, -// } + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) -// PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") -// } + timeTest := &TimeTest{ + Start: &start, + End: &end, + } -// func TestIsNeValidation(t *testing.T) { + errs := validate.Struct(timeTest) + Equal(t, errs, nil) -// var j uint64 -// var k float64 -// s := "abcdef" -// i := 3 -// j = 2 -// k = 1.5434 -// arr := []string{"test"} -// now := time.Now().UTC() + timeTest = &TimeTest{ + Start: &end, + End: &start, + } -// err := validate.Field(s, "ne=abcd") -// Equal(t, err, nil) + errs = validate.Struct(timeTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "TimeTest.Start", "Start", "ltfield") -// err = validate.Field(i, "ne=1") -// Equal(t, err, nil) + errs = validate.FieldWithValue(&end, &start, "ltfield") + Equal(t, errs, nil) -// err = validate.Field(j, "ne=1") -// Equal(t, err, nil) + errs = validate.FieldWithValue(&start, &end, "ltfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltfield") -// err = validate.Field(k, "ne=1.543") -// Equal(t, err, nil) + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltfield=Val1"` + } -// err = validate.Field(arr, "ne=2") -// Equal(t, err, nil) + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } -// err = validate.Field(arr, "ne=1") -// NotEqual(t, err, nil) + errs = validate.Struct(intTest) + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") -// } + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } -// func TestIsEqFieldValidation(t *testing.T) { + errs = validate.Struct(intTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "IntTest.Val2", "Val2", "ltfield") -// var j uint64 -// var k float64 -// s := "abcd" -// i := 1 -// j = 1 -// k = 1.543 -// arr := []string{"test"} -// now := time.Now().UTC() + errs = validate.FieldWithValue(int(5), int(1), "ltfield") + Equal(t, errs, nil) -// var j2 uint64 -// var k2 float64 -// s2 := "abcd" -// i2 := 1 -// j2 = 1 -// k2 = 1.543 -// arr2 := []string{"test"} -// arr3 := []string{"test", "test2"} -// now2 := now + errs = validate.FieldWithValue(int(1), int(5), "ltfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltfield") -// err := validate.FieldWithValue(s, s2, "eqfield") -// Equal(t, err, nil) + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltfield=Val1"` + } -// err = validate.FieldWithValue(i2, i, "eqfield") -// Equal(t, err, nil) + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } -// err = validate.FieldWithValue(j2, j, "eqfield") -// Equal(t, err, nil) + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) -// err = validate.FieldWithValue(k2, k, "eqfield") -// Equal(t, err, nil) + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } -// err = validate.FieldWithValue(arr2, arr, "eqfield") -// Equal(t, err, nil) + errs = validate.Struct(uIntTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "UIntTest.Val2", "Val2", "ltfield") -// err = validate.FieldWithValue(now2, now, "eqfield") -// Equal(t, err, nil) + errs = validate.FieldWithValue(uint(5), uint(1), "ltfield") + Equal(t, errs, nil) -// err = validate.FieldWithValue(arr3, arr, "eqfield") -// NotEqual(t, err, nil) + errs = validate.FieldWithValue(uint(1), uint(5), "ltfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltfield") -// type Test struct { -// Start *time.Time `validate:"eqfield=End"` -// End *time.Time -// } + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltfield=Val1"` + } -// sv := &Test{ -// Start: &now, -// End: &now, -// } + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } -// errs := validate.Struct(sv) -// Equal(t, errs, nil) + errs = validate.Struct(floatTest) + Equal(t, errs, nil) -// now3 := time.Now().UTC() + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } -// sv = &Test{ -// Start: &now, -// End: &now3, -// } + errs = validate.Struct(floatTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "FloatTest.Val2", "Val2", "ltfield") -// errs = validate.Struct(sv) -// NotEqual(t, errs, nil) + errs = validate.FieldWithValue(float32(5), float32(1), "ltfield") + Equal(t, errs, nil) -// channel := make(chan string) + errs = validate.FieldWithValue(float32(1), float32(5), "ltfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltfield") -// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "eqfield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(5, channel, "eqfield") }, "Bad field type chan string") -// PanicMatches(t, func() { validate.FieldWithValue(5, now, "eqfield") }, "Bad Top Level field type") + PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltfield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltfield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltfield") }, "Bad Top Level field type") -// type Test2 struct { -// Start *time.Time `validate:"eqfield=NonExistantField"` -// End *time.Time -// } + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltfield=NonExistantField"` + } -// sv2 := &Test2{ -// Start: &now, -// End: &now, -// } + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } -// PanicMatches(t, func() { validate.Struct(sv2) }, "Field \"NonExistantField\" not found in struct") -// } + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} -// func TestIsEqValidation(t *testing.T) { +func TestLteField(t *testing.T) { -// var j uint64 -// var k float64 -// s := "abcd" -// i := 1 -// j = 1 -// k = 1.543 -// arr := []string{"test"} -// now := time.Now().UTC() + type TimeTest struct { + Start *time.Time `validate:"required,lte,ltefield=End"` + End *time.Time `validate:"required,lte"` + } -// err := validate.Field(s, "eq=abcd") -// Equal(t, err, nil) + now := time.Now() + start := now.Add(time.Hour * 24 * -1 * 2) + end := start.Add(time.Hour * 24) -// err = validate.Field(i, "eq=1") -// Equal(t, err, nil) + timeTest := &TimeTest{ + Start: &start, + End: &end, + } -// err = validate.Field(j, "eq=1") -// Equal(t, err, nil) + errs := validate.Struct(timeTest) + Equal(t, errs, nil) -// err = validate.Field(k, "eq=1.543") -// Equal(t, err, nil) + timeTest = &TimeTest{ + Start: &end, + End: &start, + } -// err = validate.Field(arr, "eq=1") -// Equal(t, err, nil) + errs = validate.Struct(timeTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "TimeTest.Start", "Start", "ltefield") -// err = validate.Field(arr, "eq=2") -// NotEqual(t, err, nil) + errs = validate.FieldWithValue(&end, &start, "ltefield") + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") -// } + errs = validate.FieldWithValue(&start, &end, "ltefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltefield") -// func TestBase64Validation(t *testing.T) { + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,ltefield=Val1"` + } -// s := "dW5pY29ybg==" + intTest := &IntTest{ + Val1: 5, + Val2: 1, + } -// err := validate.Field(s, "base64") -// Equal(t, err, nil) + errs = validate.Struct(intTest) + Equal(t, errs, nil) -// s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" -// err = validate.Field(s, "base64") -// Equal(t, err, nil) + intTest = &IntTest{ + Val1: 1, + Val2: 5, + } -// s = "" -// err = validate.Field(s, "base64") -// NotEqual(t, err, nil) + errs = validate.Struct(intTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "IntTest.Val2", "Val2", "ltefield") -// s = "dW5pY29ybg== foo bar" -// err = validate.Field(s, "base64") -// NotEqual(t, err, nil) -// } + errs = validate.FieldWithValue(int(5), int(1), "ltefield") + Equal(t, errs, nil) -// func TestStructOnlyValidation(t *testing.T) { + errs = validate.FieldWithValue(int(1), int(5), "ltefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltefield") -// type Inner struct { -// Test string `validate:"len=5"` -// } + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,ltefield=Val1"` + } -// type Outer struct { -// InnerStruct *Inner `validate:"required,structonly"` -// } + uIntTest := &UIntTest{ + Val1: 5, + Val2: 1, + } -// outer := &Outer{ -// InnerStruct: nil, -// } + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) -// errs := validate.Struct(outer) -// NotEqual(t, errs, nil) + uIntTest = &UIntTest{ + Val1: 1, + Val2: 5, + } -// inner := &Inner{ -// Test: "1234", -// } + errs = validate.Struct(uIntTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "UIntTest.Val2", "Val2", "ltefield") -// outer = &Outer{ -// InnerStruct: inner, -// } + errs = validate.FieldWithValue(uint(5), uint(1), "ltefield") + Equal(t, errs, nil) -// errs = validate.Struct(outer) -// Equal(t, errs, nil) -// } + errs = validate.FieldWithValue(uint(1), uint(5), "ltefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltefield") -// func TestGtField(t *testing.T) { + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,ltefield=Val1"` + } -// type TimeTest struct { -// Start *time.Time `validate:"required,gt"` -// End *time.Time `validate:"required,gt,gtfield=Start"` -// } + floatTest := &FloatTest{ + Val1: 5, + Val2: 1, + } -// now := time.Now() -// start := now.Add(time.Hour * 24) -// end := start.Add(time.Hour * 24) + errs = validate.Struct(floatTest) + Equal(t, errs, nil) -// timeTest := &TimeTest{ -// Start: &start, -// End: &end, -// } + floatTest = &FloatTest{ + Val1: 1, + Val2: 5, + } -// errs := validate.Struct(timeTest) -// Equal(t, errs, nil) + errs = validate.Struct(floatTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "FloatTest.Val2", "Val2", "ltefield") -// timeTest = &TimeTest{ -// Start: &end, -// End: &start, -// } + errs = validate.FieldWithValue(float32(5), float32(1), "ltefield") + Equal(t, errs, nil) -// errs2 := validate.Struct(timeTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "End", "gtfield") + errs = validate.FieldWithValue(float32(1), float32(5), "ltefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "ltefield") -// err3 := validate.FieldWithValue(&start, &end, "gtfield") -// Equal(t, err3, nil) + PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltefield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltefield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltefield") }, "Bad Top Level field type") -// err3 = validate.FieldWithValue(&end, &start, "gtfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtfield") + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,ltefield=NonExistantField"` + } -// type IntTest struct { -// Val1 int `validate:"required"` -// Val2 int `validate:"required,gtfield=Val1"` -// } + timeTest2 := &TimeTest2{ + Start: &end, + End: &start, + } -// intTest := &IntTest{ -// Val1: 1, -// Val2: 5, -// } + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} -// errs = validate.Struct(intTest) -// Equal(t, errs, nil) +func TestGteField(t *testing.T) { -// intTest = &IntTest{ -// Val1: 5, -// Val2: 1, -// } + type TimeTest struct { + Start *time.Time `validate:"required,gte"` + End *time.Time `validate:"required,gte,gtefield=Start"` + } -// errs2 = validate.Struct(intTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtfield") + now := time.Now() + start := now.Add(time.Hour * 24) + end := start.Add(time.Hour * 24) -// err3 = validate.FieldWithValue(int(1), int(5), "gtfield") -// Equal(t, err3, nil) + timeTest := &TimeTest{ + Start: &start, + End: &end, + } -// err3 = validate.FieldWithValue(int(5), int(1), "gtfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtfield") + errs := validate.Struct(timeTest) + Equal(t, errs, nil) -// type UIntTest struct { -// Val1 uint `validate:"required"` -// Val2 uint `validate:"required,gtfield=Val1"` -// } + timeTest = &TimeTest{ + Start: &end, + End: &start, + } -// uIntTest := &UIntTest{ -// Val1: 1, -// Val2: 5, -// } + errs = validate.Struct(timeTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "TimeTest.End", "End", "gtefield") -// errs = validate.Struct(uIntTest) -// Equal(t, errs, nil) + errs = validate.FieldWithValue(&start, &end, "gtefield") + Equal(t, errs, nil) -// uIntTest = &UIntTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs2 = validate.Struct(uIntTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtfield") - -// err3 = validate.FieldWithValue(uint(1), uint(5), "gtfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(uint(5), uint(1), "gtfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtfield") - -// type FloatTest struct { -// Val1 float64 `validate:"required"` -// Val2 float64 `validate:"required,gtfield=Val1"` -// } - -// floatTest := &FloatTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs = validate.Struct(floatTest) -// Equal(t, errs, nil) - -// floatTest = &FloatTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs2 = validate.Struct(floatTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtfield") - -// err3 = validate.FieldWithValue(float32(1), float32(5), "gtfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(float32(5), float32(1), "gtfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtfield") - -// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtfield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtfield") }, "Bad field type string") -// PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtfield") }, "Bad Top Level field type") - -// type TimeTest2 struct { -// Start *time.Time `validate:"required"` -// End *time.Time `validate:"required,gtfield=NonExistantField"` -// } - -// timeTest2 := &TimeTest2{ -// Start: &start, -// End: &end, -// } - -// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -// } - -// func TestLtField(t *testing.T) { - -// type TimeTest struct { -// Start *time.Time `validate:"required,lt,ltfield=End"` -// End *time.Time `validate:"required,lt"` -// } - -// now := time.Now() -// start := now.Add(time.Hour * 24 * -1 * 2) -// end := start.Add(time.Hour * 24) - -// timeTest := &TimeTest{ -// Start: &start, -// End: &end, -// } - -// errs := validate.Struct(timeTest) -// Equal(t, errs, nil) - -// timeTest = &TimeTest{ -// Start: &end, -// End: &start, -// } - -// errs2 := validate.Struct(timeTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Start", "ltfield") - -// err3 := validate.FieldWithValue(&end, &start, "ltfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(&start, &end, "ltfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltfield") - -// type IntTest struct { -// Val1 int `validate:"required"` -// Val2 int `validate:"required,ltfield=Val1"` -// } - -// intTest := &IntTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs = validate.Struct(intTest) -// Equal(t, errs, nil) - -// intTest = &IntTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(intTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltfield") - -// err3 = validate.FieldWithValue(int(5), int(1), "ltfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(int(1), int(5), "ltfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltfield") + errs = validate.FieldWithValue(&end, &start, "gtefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtefield") -// type UIntTest struct { -// Val1 uint `validate:"required"` -// Val2 uint `validate:"required,ltfield=Val1"` -// } - -// uIntTest := &UIntTest{ -// Val1: 5, -// Val2: 1, -// } + type IntTest struct { + Val1 int `validate:"required"` + Val2 int `validate:"required,gtefield=Val1"` + } -// errs = validate.Struct(uIntTest) -// Equal(t, errs, nil) - -// uIntTest = &UIntTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(uIntTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltfield") - -// err3 = validate.FieldWithValue(uint(5), uint(1), "ltfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(uint(1), uint(5), "ltfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltfield") - -// type FloatTest struct { -// Val1 float64 `validate:"required"` -// Val2 float64 `validate:"required,ltfield=Val1"` -// } - -// floatTest := &FloatTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs = validate.Struct(floatTest) -// Equal(t, errs, nil) - -// floatTest = &FloatTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(floatTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltfield") - -// err3 = validate.FieldWithValue(float32(5), float32(1), "ltfield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(float32(1), float32(5), "ltfield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltfield") - -// PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltfield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltfield") }, "Bad field type string") -// PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltfield") }, "Bad Top Level field type") - -// type TimeTest2 struct { -// Start *time.Time `validate:"required"` -// End *time.Time `validate:"required,ltfield=NonExistantField"` -// } - -// timeTest2 := &TimeTest2{ -// Start: &end, -// End: &start, -// } - -// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -// } - -// func TestLteField(t *testing.T) { - -// type TimeTest struct { -// Start *time.Time `validate:"required,lte,ltefield=End"` -// End *time.Time `validate:"required,lte"` -// } - -// now := time.Now() -// start := now.Add(time.Hour * 24 * -1 * 2) -// end := start.Add(time.Hour * 24) - -// timeTest := &TimeTest{ -// Start: &start, -// End: &end, -// } - -// errs := validate.Struct(timeTest) -// Equal(t, errs, nil) - -// timeTest = &TimeTest{ -// Start: &end, -// End: &start, -// } - -// errs2 := validate.Struct(timeTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Start", "ltefield") - -// err3 := validate.FieldWithValue(&end, &start, "ltefield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(&start, &end, "ltefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltefield") - -// type IntTest struct { -// Val1 int `validate:"required"` -// Val2 int `validate:"required,ltefield=Val1"` -// } - -// intTest := &IntTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs = validate.Struct(intTest) -// Equal(t, errs, nil) - -// intTest = &IntTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(intTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltefield") - -// err3 = validate.FieldWithValue(int(5), int(1), "ltefield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(int(1), int(5), "ltefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltefield") + intTest := &IntTest{ + Val1: 1, + Val2: 5, + } -// type UIntTest struct { -// Val1 uint `validate:"required"` -// Val2 uint `validate:"required,ltefield=Val1"` -// } - -// uIntTest := &UIntTest{ -// Val1: 5, -// Val2: 1, -// } + errs = validate.Struct(intTest) + Equal(t, errs, nil) -// errs = validate.Struct(uIntTest) -// Equal(t, errs, nil) - -// uIntTest = &UIntTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(uIntTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltefield") - -// err3 = validate.FieldWithValue(uint(5), uint(1), "ltefield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(uint(1), uint(5), "ltefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltefield") - -// type FloatTest struct { -// Val1 float64 `validate:"required"` -// Val2 float64 `validate:"required,ltefield=Val1"` -// } - -// floatTest := &FloatTest{ -// Val1: 5, -// Val2: 1, -// } - -// errs = validate.Struct(floatTest) -// Equal(t, errs, nil) - -// floatTest = &FloatTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs2 = validate.Struct(floatTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "ltefield") - -// err3 = validate.FieldWithValue(float32(5), float32(1), "ltefield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(float32(1), float32(5), "ltefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "ltefield") - -// PanicMatches(t, func() { validate.FieldWithValue(nil, 5, "ltefield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(1, "T", "ltefield") }, "Bad field type string") -// PanicMatches(t, func() { validate.FieldWithValue(1, end, "ltefield") }, "Bad Top Level field type") - -// type TimeTest2 struct { -// Start *time.Time `validate:"required"` -// End *time.Time `validate:"required,ltefield=NonExistantField"` -// } - -// timeTest2 := &TimeTest2{ -// Start: &end, -// End: &start, -// } - -// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -// } - -// func TestGteField(t *testing.T) { - -// type TimeTest struct { -// Start *time.Time `validate:"required,gte"` -// End *time.Time `validate:"required,gte,gtefield=Start"` -// } - -// now := time.Now() -// start := now.Add(time.Hour * 24) -// end := start.Add(time.Hour * 24) - -// timeTest := &TimeTest{ -// Start: &start, -// End: &end, -// } - -// errs := validate.Struct(timeTest) -// Equal(t, errs, nil) - -// timeTest = &TimeTest{ -// Start: &end, -// End: &start, -// } - -// errs2 := validate.Struct(timeTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "End", "gtefield") - -// err3 := validate.FieldWithValue(&start, &end, "gtefield") -// Equal(t, err3, nil) - -// err3 = validate.FieldWithValue(&end, &start, "gtefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtefield") - -// type IntTest struct { -// Val1 int `validate:"required"` -// Val2 int `validate:"required,gtefield=Val1"` -// } - -// intTest := &IntTest{ -// Val1: 1, -// Val2: 5, -// } - -// errs = validate.Struct(intTest) -// Equal(t, errs, nil) + intTest = &IntTest{ + Val1: 5, + Val2: 1, + } -// intTest = &IntTest{ -// Val1: 5, -// Val2: 1, -// } + errs = validate.Struct(intTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "IntTest.Val2", "Val2", "gtefield") -// errs2 = validate.Struct(intTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtefield") + errs = validate.FieldWithValue(int(1), int(5), "gtefield") + Equal(t, errs, nil) -// err3 = validate.FieldWithValue(int(1), int(5), "gtefield") -// Equal(t, err3, nil) + errs = validate.FieldWithValue(int(5), int(1), "gtefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtefield") -// err3 = validate.FieldWithValue(int(5), int(1), "gtefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtefield") + type UIntTest struct { + Val1 uint `validate:"required"` + Val2 uint `validate:"required,gtefield=Val1"` + } -// type UIntTest struct { -// Val1 uint `validate:"required"` -// Val2 uint `validate:"required,gtefield=Val1"` -// } + uIntTest := &UIntTest{ + Val1: 1, + Val2: 5, + } -// uIntTest := &UIntTest{ -// Val1: 1, -// Val2: 5, -// } + errs = validate.Struct(uIntTest) + Equal(t, errs, nil) -// errs = validate.Struct(uIntTest) -// Equal(t, errs, nil) + uIntTest = &UIntTest{ + Val1: 5, + Val2: 1, + } -// uIntTest = &UIntTest{ -// Val1: 5, -// Val2: 1, -// } + errs = validate.Struct(uIntTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "UIntTest.Val2", "Val2", "gtefield") -// errs2 = validate.Struct(uIntTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtefield") + errs = validate.FieldWithValue(uint(1), uint(5), "gtefield") + Equal(t, errs, nil) -// err3 = validate.FieldWithValue(uint(1), uint(5), "gtefield") -// Equal(t, err3, nil) + errs = validate.FieldWithValue(uint(5), uint(1), "gtefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtefield") -// err3 = validate.FieldWithValue(uint(5), uint(1), "gtefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtefield") + type FloatTest struct { + Val1 float64 `validate:"required"` + Val2 float64 `validate:"required,gtefield=Val1"` + } -// type FloatTest struct { -// Val1 float64 `validate:"required"` -// Val2 float64 `validate:"required,gtefield=Val1"` -// } + floatTest := &FloatTest{ + Val1: 1, + Val2: 5, + } -// floatTest := &FloatTest{ -// Val1: 1, -// Val2: 5, -// } + errs = validate.Struct(floatTest) + Equal(t, errs, nil) -// errs = validate.Struct(floatTest) -// Equal(t, errs, nil) + floatTest = &FloatTest{ + Val1: 5, + Val2: 1, + } -// floatTest = &FloatTest{ -// Val1: 5, -// Val2: 1, -// } + errs = validate.Struct(floatTest) + NotEqual(t, errs, nil) + AssertError(t, errs, "FloatTest.Val2", "Val2", "gtefield") -// errs2 = validate.Struct(floatTest).Flatten() -// NotEqual(t, errs2, nil) -// AssertMapFieldError(t, errs2, "Val2", "gtefield") + errs = validate.FieldWithValue(float32(1), float32(5), "gtefield") + Equal(t, errs, nil) -// err3 = validate.FieldWithValue(float32(1), float32(5), "gtefield") -// Equal(t, err3, nil) + errs = validate.FieldWithValue(float32(5), float32(1), "gtefield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gtefield") -// err3 = validate.FieldWithValue(float32(5), float32(1), "gtefield") -// NotEqual(t, err3, nil) -// Equal(t, err3.Tag, "gtefield") + PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtefield") }, "struct not passed for cross validation") + PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtefield") }, "Bad field type string") + PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtefield") }, "Bad Top Level field type") -// PanicMatches(t, func() { validate.FieldWithValue(nil, 1, "gtefield") }, "struct not passed for cross validation") -// PanicMatches(t, func() { validate.FieldWithValue(5, "T", "gtefield") }, "Bad field type string") -// PanicMatches(t, func() { validate.FieldWithValue(5, start, "gtefield") }, "Bad Top Level field type") + type TimeTest2 struct { + Start *time.Time `validate:"required"` + End *time.Time `validate:"required,gtefield=NonExistantField"` + } -// type TimeTest2 struct { -// Start *time.Time `validate:"required"` -// End *time.Time `validate:"required,gtefield=NonExistantField"` -// } + timeTest2 := &TimeTest2{ + Start: &start, + End: &end, + } -// timeTest2 := &TimeTest2{ -// Start: &start, -// End: &end, -// } + PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") +} -// PanicMatches(t, func() { validate.Struct(timeTest2) }, "Field \"NonExistantField\" not found in struct") -// } +func TestValidateByTagAndValue(t *testing.T) { -// func TestValidateByTagAndValue(t *testing.T) { + val := "test" + field := "test" + errs := validate.FieldWithValue(val, field, "required") + Equal(t, errs, nil) -// val := "test" -// field := "test" -// err := validate.FieldWithValue(val, field, "required") -// Equal(t, err, nil) + fn := func(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// fn := func(val interface{}, current interface{}, field interface{}, param string) bool { + return current.String() == field.String() + } -// return current.(string) == field.(string) -// } + validate.RegisterValidation("isequaltestfunc", fn) -// validate.AddFunction("isequaltestfunc", fn) + errs = validate.FieldWithValue(val, field, "isequaltestfunc") + Equal(t, errs, nil) -// err = validate.FieldWithValue(val, field, "isequaltestfunc") -// Equal(t, err, nil) + val = "unequal" -// val = "unequal" + errs = validate.FieldWithValue(val, field, "isequaltestfunc") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "isequaltestfunc") +} -// err = validate.FieldWithValue(val, field, "isequaltestfunc") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "isequaltestfunc") -// } +func TestAddFunctions(t *testing.T) { -// func TestAddFunctions(t *testing.T) { + fn := func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { -// fn := func(val interface{}, current interface{}, field interface{}, param string) bool { + return true + } -// return true -// } + config := Config{ + TagName: "validateme", + ValidationFuncs: BakedInValidators, + } -// validate := New("validateme", BakedInValidators) + validate := New(config) -// err := validate.AddFunction("new", fn) -// Equal(t, err, nil) + errs := validate.RegisterValidation("new", fn) + Equal(t, errs, nil) -// err = validate.AddFunction("", fn) -// NotEqual(t, err, nil) + errs = validate.RegisterValidation("", fn) + NotEqual(t, errs, nil) -// validate.AddFunction("new", nil) -// NotEqual(t, err, nil) + validate.RegisterValidation("new", nil) + NotEqual(t, errs, nil) -// err = validate.AddFunction("new", fn) -// Equal(t, err, nil) -// } + errs = validate.RegisterValidation("new", fn) + Equal(t, errs, nil) +} -// func TestChangeTag(t *testing.T) { +func TestChangeTag(t *testing.T) { -// validate := New("validateme", BakedInValidators) -// validate.SetTag("val") + config := Config{ + TagName: "val", + ValidationFuncs: BakedInValidators, + } + validate := New(config) -// type Test struct { -// Name string `val:"len=4"` -// } -// s := &Test{ -// Name: "TEST", -// } + type Test struct { + Name string `val:"len=4"` + } + s := &Test{ + Name: "TEST", + } -// err := validate.Struct(s) -// Equal(t, err, nil) -// } + errs := validate.Struct(s) + Equal(t, errs, nil) +} -// func TestUnexposedStruct(t *testing.T) { +func TestUnexposedStruct(t *testing.T) { -// type Test struct { -// Name string -// unexposed struct { -// A string `validate:"required"` -// } -// } + type Test struct { + Name string + unexposed struct { + A string `validate:"required"` + } + } -// s := &Test{ -// Name: "TEST", -// } + s := &Test{ + Name: "TEST", + } -// err := validate.Struct(s) -// Equal(t, err, nil) -// } + errs := validate.Struct(s) + Equal(t, errs, nil) +} -// func TestBadParams(t *testing.T) { +func TestBadParams(t *testing.T) { -// i := 1 -// err := validate.Field(i, "-") -// Equal(t, err, nil) + i := 1 + errs := validate.Field(i, "-") + Equal(t, errs, nil) -// PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") -// PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") -// var ui uint = 1 -// PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") + var ui uint = 1 + PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") -// f := 1.23 -// PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") -// } + f := 1.23 + PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") +} -// func TestLength(t *testing.T) { +func TestLength(t *testing.T) { -// i := true -// PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") -// } + i := true + PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") +} -// func TestIsGt(t *testing.T) { +func TestIsGt(t *testing.T) { -// myMap := map[string]string{} -// err := validate.Field(myMap, "gt=0") -// NotEqual(t, err, nil) + myMap := map[string]string{} + errs := validate.Field(myMap, "gt=0") + NotEqual(t, errs, nil) -// f := 1.23 -// err = validate.Field(f, "gt=5") -// NotEqual(t, err, nil) + f := 1.23 + errs = validate.Field(f, "gt=5") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gt") -// var ui uint = 5 -// err = validate.Field(ui, "gt=10") -// NotEqual(t, err, nil) + var ui uint = 5 + errs = validate.Field(ui, "gt=10") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gt") -// i := true -// PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") + i := true + PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") -// tm := time.Now().UTC() -// tm = tm.Add(time.Hour * 24) + tm := time.Now().UTC() + tm = tm.Add(time.Hour * 24) -// err = validate.Field(tm, "gt") -// Equal(t, err, nil) + errs = validate.Field(tm, "gt") + Equal(t, errs, nil) -// t2 := time.Now().UTC() + t2 := time.Now().UTC() -// err = validate.Field(t2, "gt") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "gt") + errs = validate.Field(t2, "gt") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gt") -// type Test struct { -// Now *time.Time `validate:"gt"` -// } -// s := &Test{ -// Now: &tm, -// } + type Test struct { + Now *time.Time `validate:"gt"` + } + s := &Test{ + Now: &tm, + } -// errs := validate.Struct(s) -// Equal(t, errs, nil) + errs = validate.Struct(s) + Equal(t, errs, nil) -// s = &Test{ -// Now: &t2, -// } + s = &Test{ + Now: &t2, + } -// errs = validate.Struct(s) -// NotEqual(t, errs, nil) -// } + errs = validate.Struct(s) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Now", "Now", "gt") +} -// func TestIsGte(t *testing.T) { +func TestIsGte(t *testing.T) { -// i := true -// PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") + i := true + PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") -// t1 := time.Now().UTC() -// t1 = t1.Add(time.Hour * 24) + t1 := time.Now().UTC() + t1 = t1.Add(time.Hour * 24) -// err := validate.Field(t1, "gte") -// Equal(t, err, nil) + errs := validate.Field(t1, "gte") + Equal(t, errs, nil) -// t2 := time.Now().UTC() + t2 := time.Now().UTC() -// err = validate.Field(t2, "gte") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "gte") -// Equal(t, err.Type, reflect.TypeOf(time.Time{})) + errs = validate.Field(t2, "gte") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "gte") -// type Test struct { -// Now *time.Time `validate:"gte"` -// } -// s := &Test{ -// Now: &t1, -// } + type Test struct { + Now *time.Time `validate:"gte"` + } + s := &Test{ + Now: &t1, + } -// errs := validate.Struct(s) -// Equal(t, errs, nil) + errs = validate.Struct(s) + Equal(t, errs, nil) -// s = &Test{ -// Now: &t2, -// } + s = &Test{ + Now: &t2, + } -// errs = validate.Struct(s) -// NotEqual(t, errs, nil) -// } + errs = validate.Struct(s) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Now", "Now", "gte") +} -// func TestIsLt(t *testing.T) { +func TestIsLt(t *testing.T) { -// myMap := map[string]string{} -// err := validate.Field(myMap, "lt=0") -// NotEqual(t, err, nil) + myMap := map[string]string{} + errs := validate.Field(myMap, "lt=0") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "lt") -// f := 1.23 -// err = validate.Field(f, "lt=0") -// NotEqual(t, err, nil) + f := 1.23 + errs = validate.Field(f, "lt=0") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "lt") -// var ui uint = 5 -// err = validate.Field(ui, "lt=0") -// NotEqual(t, err, nil) + var ui uint = 5 + errs = validate.Field(ui, "lt=0") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "lt") -// i := true -// PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") + i := true + PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") -// t1 := time.Now().UTC() + t1 := time.Now().UTC() -// err = validate.Field(t1, "lt") -// Equal(t, err, nil) + errs = validate.Field(t1, "lt") + Equal(t, errs, nil) -// t2 := time.Now().UTC() -// t2 = t2.Add(time.Hour * 24) + t2 := time.Now().UTC() + t2 = t2.Add(time.Hour * 24) -// err = validate.Field(t2, "lt") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "lt") + errs = validate.Field(t2, "lt") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "lt") -// type Test struct { -// Now *time.Time `validate:"lt"` -// } + type Test struct { + Now *time.Time `validate:"lt"` + } -// s := &Test{ -// Now: &t1, -// } + s := &Test{ + Now: &t1, + } -// errs := validate.Struct(s) -// Equal(t, errs, nil) + errs = validate.Struct(s) + Equal(t, errs, nil) -// s = &Test{ -// Now: &t2, -// } + s = &Test{ + Now: &t2, + } -// errs = validate.Struct(s) -// NotEqual(t, errs, nil) -// } + errs = validate.Struct(s) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Now", "Now", "lt") +} -// func TestIsLte(t *testing.T) { +func TestIsLte(t *testing.T) { -// i := true -// PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") + i := true + PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") -// t1 := time.Now().UTC() + t1 := time.Now().UTC() -// err := validate.Field(t1, "lte") -// Equal(t, err, nil) + errs := validate.Field(t1, "lte") + Equal(t, errs, nil) -// t2 := time.Now().UTC() -// t2 = t2.Add(time.Hour * 24) + t2 := time.Now().UTC() + t2 = t2.Add(time.Hour * 24) -// err = validate.Field(t2, "lte") -// NotEqual(t, err, nil) -// Equal(t, err.Tag, "lte") + errs = validate.Field(t2, "lte") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "lte") -// type Test struct { -// Now *time.Time `validate:"lte"` -// } + type Test struct { + Now *time.Time `validate:"lte"` + } -// s := &Test{ -// Now: &t1, -// } + s := &Test{ + Now: &t1, + } -// errs := validate.Struct(s) -// Equal(t, errs, nil) + errs = validate.Struct(s) + Equal(t, errs, nil) -// s = &Test{ -// Now: &t2, -// } + s = &Test{ + Now: &t2, + } -// errs = validate.Struct(s) -// NotEqual(t, errs, nil) -// } + errs = validate.Struct(s) + NotEqual(t, errs, nil) +} func TestUrl(t *testing.T) { @@ -3138,13 +2763,11 @@ func TestOrTag(t *testing.T) { s = "this ain't right" errs = validate.Field(s, "rgb|rgba") NotEqual(t, errs, nil) - // Equal(t, err.Tag, "rgb|rgba") AssertError(t, errs, "", "", "rgb|rgba") s = "this ain't right" errs = validate.Field(s, "rgb|rgba|len=10") NotEqual(t, errs, nil) - // Equal(t, err.Tag, "rgb|rgba|len") AssertError(t, errs, "", "", "rgb|rgba|len") s = "this is right" From 7f4b49c751d13fa3bc455151478eaea54e21cf6f Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Thu, 16 Jul 2015 08:20:20 -0400 Subject: [PATCH 13/20] Finished Tests, now at 100% test converage! --- validator.go | 17 +++++++---------- validator_test.go | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/validator.go b/validator.go index 5312c4e..468c720 100644 --- a/validator.go +++ b/validator.go @@ -29,9 +29,10 @@ const ( skipValidationTag = "-" diveTag = "dive" fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" - invalidField = "Invalid field passed to traverseField" arrayIndexFieldName = "%s[%d]" mapIndexFieldName = "%s[%v]" + invalidValidation = "Invalid validation tag on field %s" + undefinedValidation = "Undefined validation function on field %s" ) var ( @@ -251,9 +252,6 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. } switch kind { - - case reflect.Invalid: - panic(invalidField) case reflect.Struct, reflect.Interface: if kind == reflect.Interface { @@ -276,7 +274,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. if typ != timeType && typ != timePtrType { - if isStructField || kind == reflect.Struct { + if kind == reflect.Struct { // required passed validationa above so stop here // if only validating the structs existance. @@ -287,7 +285,6 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false) return } - panic(invalidField) } FALLTHROUGH: fallthrough @@ -328,7 +325,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. key = vals[0] if len(key) == 0 { - panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name))) } if len(vals) > 1 { @@ -401,7 +398,7 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. vals := strings.SplitN(val, tagKeySeparator, 2) if len(vals[0]) == 0 { - panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name))) } param := "" @@ -412,7 +409,7 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. // validate and keep track! valFunc, ok := v.config.ValidationFuncs[vals[0]] if !ok { - panic(fmt.Sprintf("Undefined validation function on field %s", name)) + panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) } if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { @@ -436,7 +433,7 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. valFunc, ok := v.config.ValidationFuncs[key] if !ok { - panic(fmt.Sprintf("Undefined validation function on field %s", name)) + panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) } if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { diff --git a/validator_test.go b/validator_test.go index 5103f38..9d4318f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -239,6 +239,16 @@ func TestBadKeyValidation(t *testing.T) { } PanicMatches(t, func() { validate.Struct(tst) }, "Undefined validation function on field Name") + + type Test2 struct { + Name string `validate:"required,,len=2"` + } + + tst2 := &Test2{ + Name: "test", + } + + PanicMatches(t, func() { validate.Struct(tst2) }, "Invalid validation tag on field Name") } func TestInterfaceErrValidation(t *testing.T) { @@ -2777,6 +2787,11 @@ func TestOrTag(t *testing.T) { s = "" errs = validate.Field(s, "omitempty,rgb|rgba") Equal(t, errs, nil) + + s = "this is right, but a blank or isn't" + + PanicMatches(t, func() { validate.Field(s, "rgb||len=13") }, "Invalid validation tag on field") + PanicMatches(t, func() { validate.Field(s, "rgb|rgbaa|len=13") }, "Undefined validation function on field") } func TestHsla(t *testing.T) { @@ -3390,5 +3405,5 @@ func TestInvalidValidatorFunction(t *testing.T) { Test: "1", } - PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", "")) + PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, "Undefined validation function on field") } From 07f2263b41edf448797800ebbdaf2ade6efd89fe Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Thu, 16 Jul 2015 08:24:27 -0400 Subject: [PATCH 14/20] Benchmark Updates Renamed for better clarity. Added Field OrTag Benchmark. --- benchmarks_test.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/benchmarks_test.go b/benchmarks_test.go index ee836c2..14c5d2b 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -2,13 +2,19 @@ package validator import "testing" -func BenchmarkValidateField(b *testing.B) { +func BenchmarkField(b *testing.B) { for n := 0; n < b.N; n++ { validate.Field("1", "len=1") } } -func BenchmarkValidateStructSimple(b *testing.B) { +func BenchmarkFieldOrTag(b *testing.B) { + for n := 0; n < b.N; n++ { + validate.Field("rgba(0,0,0,1)", "rgb|rgba") + } +} + +func BenchmarkStructSimple(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` @@ -24,7 +30,7 @@ func BenchmarkValidateStructSimple(b *testing.B) { } } -func BenchmarkTemplateParallelSimple(b *testing.B) { +func BenchmarkStructSimpleParallel(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` @@ -42,7 +48,7 @@ func BenchmarkTemplateParallelSimple(b *testing.B) { }) } -func BenchmarkValidateStructLarge(b *testing.B) { +func BenchmarkStructComplex(b *testing.B) { tFail := &TestString{ Required: "", @@ -101,7 +107,7 @@ func BenchmarkValidateStructLarge(b *testing.B) { } } -func BenchmarkTemplateParallelLarge(b *testing.B) { +func BenchmarkStructComplexParallel(b *testing.B) { tFail := &TestString{ Required: "", From 4bdd70340929d731942161b2e75279532062f2a2 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Thu, 16 Jul 2015 08:25:54 -0400 Subject: [PATCH 15/20] Remove old validator code was just keeping around for reference when converting. --- old/validator_old.go | 989 ------------------------------------------- 1 file changed, 989 deletions(-) delete mode 100644 old/validator_old.go diff --git a/old/validator_old.go b/old/validator_old.go deleted file mode 100644 index ccd2a55..0000000 --- a/old/validator_old.go +++ /dev/null @@ -1,989 +0,0 @@ -/** - * Package validator - * - * MISC: - * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank - * - */ - -package validator - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "sync" - "time" - "unicode" -) - -const ( - utf8HexComma = "0x2C" - tagSeparator = "," - orSeparator = "|" - noValidationTag = "-" - tagKeySeparator = "=" - structOnlyTag = "structonly" - omitempty = "omitempty" - required = "required" - fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" - sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s" - mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" - structErrMsg = "Struct:%s\n" - diveTag = "dive" - arrayIndexFieldName = "%s[%d]" - mapIndexFieldName = "%s[%v]" -) - -var structPool *sync.Pool - -// returns new *StructErrors to the pool -func newStructErrors() interface{} { - return &StructErrors{ - Errors: map[string]*FieldError{}, - StructErrors: map[string]*StructErrors{}, - } -} - -type cachedTags struct { - keyVals [][]string - isOrVal bool -} - -type cachedField struct { - index int - name string - tags []*cachedTags - tag string - kind reflect.Kind - typ reflect.Type - isTime bool - isSliceOrArray bool - isMap bool - isTimeSubtype bool - sliceSubtype reflect.Type - mapSubtype reflect.Type - sliceSubKind reflect.Kind - mapSubKind reflect.Kind - dive bool - diveTag string -} - -type cachedStruct struct { - children int - name string - kind reflect.Kind - fields []*cachedField -} - -type structsCacheMap struct { - lock sync.RWMutex - m map[reflect.Type]*cachedStruct -} - -func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) { - s.lock.RLock() - defer s.lock.RUnlock() - value, ok := s.m[key] - return value, ok -} - -func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) { - s.lock.Lock() - defer s.lock.Unlock() - s.m[key] = value -} - -var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}} - -type fieldsCacheMap struct { - lock sync.RWMutex - m map[string][]*cachedTags -} - -func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) { - s.lock.RLock() - defer s.lock.RUnlock() - value, ok := s.m[key] - return value, ok -} - -func (s *fieldsCacheMap) Set(key string, value []*cachedTags) { - s.lock.Lock() - defer s.lock.Unlock() - s.m[key] = value -} - -var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}} - -// 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 - Kind reflect.Kind - Type reflect.Type - Param string - Value interface{} - IsPlaceholderErr bool - IsSliceOrArray bool - IsMap bool - SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors - MapErrs map[interface{}]error // counld be FieldError, StructErrors -} - -// This is intended for use in development + debugging and not intended to be a production error message. -// it also allows FieldError to be used as an Error interface -func (e *FieldError) Error() string { - - if e.IsPlaceholderErr { - - buff := bytes.NewBufferString("") - - if e.IsSliceOrArray { - - for j, err := range e.SliceOrArrayErrs { - buff.WriteString("\n") - buff.WriteString(fmt.Sprintf(sliceErrMsg, e.Field, j, "\n"+err.Error())) - } - - } else if e.IsMap { - - for key, err := range e.MapErrs { - buff.WriteString(fmt.Sprintf(mapErrMsg, e.Field, key, "\n"+err.Error())) - } - } - - return strings.TrimSpace(buff.String()) - } - - return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag) -} - -// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name -// for those that want/need it. -// This is now needed because of the new dive functionality -func (e *FieldError) Flatten() map[string]*FieldError { - - errs := map[string]*FieldError{} - - if e.IsPlaceholderErr { - - if e.IsSliceOrArray { - for key, err := range e.SliceOrArrayErrs { - - fe, ok := err.(*FieldError) - - if ok { - - if flat := fe.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - if fe.IsPlaceholderErr { - errs[fmt.Sprintf("[%#v]%s", key, k)] = v - } else { - errs[fmt.Sprintf("[%#v]", key)] = v - } - - } - } - } else { - - se := err.(*StructErrors) - - if flat := se.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v - } - } - } - } - } - - if e.IsMap { - for key, err := range e.MapErrs { - - fe, ok := err.(*FieldError) - - if ok { - - if flat := fe.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - if fe.IsPlaceholderErr { - errs[fmt.Sprintf("[%#v]%s", key, k)] = v - } else { - errs[fmt.Sprintf("[%#v]", key)] = v - } - } - } - } else { - - se := err.(*StructErrors) - - if flat := se.Flatten(); flat != nil && len(flat) > 0 { - for k, v := range flat { - errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v - } - } - } - } - } - - return errs - } - - errs[e.Field] = e - - return errs -} - -// StructErrors is hierarchical list of field and struct validation errors -// for a non hierarchical representation please see the Flatten method for StructErrors -type StructErrors struct { - // Name of the Struct - Struct string - // Struct Field Errors - Errors map[string]*FieldError - // 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]*StructErrors -} - -// This is intended for use in development + debugging and not intended to be a production error message. -// it also allows StructErrors to be used as an Error interface -func (e *StructErrors) Error() string { - buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct)) - - for _, err := range e.Errors { - buff.WriteString(err.Error()) - buff.WriteString("\n") - } - - for _, err := range e.StructErrors { - buff.WriteString(err.Error()) - } - - return strings.TrimSpace(buff.String()) -} - -// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name -// for those that want/need it -func (e *StructErrors) Flatten() map[string]*FieldError { - - if e == nil { - return nil - } - - errs := map[string]*FieldError{} - - for _, f := range e.Errors { - - if flat := f.Flatten(); flat != nil && len(flat) > 0 { - - for k, fe := range flat { - - if f.IsPlaceholderErr { - errs[f.Field+k] = fe - } else { - errs[k] = fe - } - } - } - } - - for key, val := range e.StructErrors { - - otherErrs := val.Flatten() - - for _, f2 := range otherErrs { - - f2.Field = fmt.Sprintf("%s.%s", key, f2.Field) - errs[f2.Field] = f2 - } - } - - return errs -} - -// Func accepts all values needed for file and cross field validation -// top = top level struct when validating by struct otherwise nil -// current = current level struct when validating by struct otherwise optional comparison value -// f = field value for validation -// 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 - -// 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 -// or make it thread safe on your end -type Validate struct { - // tagName being used. - tagName string - // validateFuncs is a map of validation functions and the tag keys - validationFuncs map[string]Func -} - -// New creates a new Validate instance for use. -func New(tagName string, funcs map[string]Func) *Validate { - - structPool = &sync.Pool{New: newStructErrors} - - return &Validate{ - tagName: tagName, - validationFuncs: funcs, - } -} - -// SetTag sets tagName of the Validator to one of your choosing after creation -// perhaps to dodge a tag name conflict in a specific section of code -// NOTE: this method is not thread-safe -func (v *Validate) SetTag(tagName string) { - v.tagName = tagName -} - -// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained -// performance tuning towards your application, however, the default should be fine for -// nearly all cases. only increase if you have a deeply nested struct structure. -// NOTE: this method is not thread-safe -// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed -func (v *Validate) SetMaxStructPoolSize(max int) { - structPool = &sync.Pool{New: newStructErrors} -} - -// AddFunction adds a validation Func to a Validate's map of validators denoted by the key -// NOTE: if the key already exists, it will get replaced. -// NOTE: this method is not thread-safe -func (v *Validate) AddFunction(key string, f Func) error { - - if len(key) == 0 { - return errors.New("Function Key cannot be empty") - } - - if f == nil { - return errors.New("Function cannot be empty") - } - - v.validationFuncs[key] = f - - return nil -} - -// Struct validates a struct, even it's nested structs, and returns a struct containing the errors -// 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{}) *StructErrors { - - return v.structRecursive(s, s, s) -} - -// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors -func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors { - - structValue := reflect.ValueOf(s) - - if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { - return v.structRecursive(top, current, structValue.Elem().Interface()) - } - - if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { - panic("interface passed for validation is not a struct") - } - - structType := reflect.TypeOf(s) - - var structName string - var numFields int - var cs *cachedStruct - var isCached bool - - cs, isCached = structCache.Get(structType) - - if isCached { - structName = cs.name - numFields = cs.children - } else { - structName = structType.Name() - numFields = structValue.NumField() - cs = &cachedStruct{name: structName, children: numFields} - } - - validationErrors := structPool.Get().(*StructErrors) - validationErrors.Struct = structName - - for i := 0; i < numFields; i++ { - - var valueField reflect.Value - var cField *cachedField - var typeField reflect.StructField - - if isCached { - cField = cs.fields[i] - valueField = structValue.Field(cField.index) - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - } else { - valueField = structValue.Field(i) - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - - typeField = structType.Field(i) - - cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))} - - if cField.tag == noValidationTag { - cs.children-- - continue - } - - // if no validation and not a struct (which may containt fields for validation) - if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { - cs.children-- - continue - } - - cField.name = typeField.Name - cField.kind = valueField.Kind() - cField.typ = valueField.Type() - } - - // this can happen if the first cache value was nil - // but the second actually has a value - if cField.kind == reflect.Ptr { - cField.kind = valueField.Kind() - } - - switch cField.kind { - - case reflect.Struct, reflect.Interface: - - if !unicode.IsUpper(rune(cField.name[0])) { - cs.children-- - continue - } - - if cField.isTime { - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - } else { - - if strings.Contains(cField.tag, structOnlyTag) { - cs.children-- - continue - } - - if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() { - - if strings.Contains(cField.tag, omitempty) { - goto CACHEFIELD - } - - tags := strings.Split(cField.tag, tagSeparator) - - if len(tags) > 0 { - - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) - - if len(vals) > 1 { - param = vals[1] - } - - validationErrors.Errors[cField.name] = &FieldError{ - Field: cField.name, - Tag: vals[0], - Param: param, - Value: valueField.Interface(), - Kind: valueField.Kind(), - Type: valueField.Type(), - } - - goto CACHEFIELD - } - } - - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if cField.kind == reflect.Interface { - - valueField = valueField.Elem() - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - } - - if valueField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - goto CACHEFIELD - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil { - validationErrors.StructErrors[cField.name] = structErrors - // free up memory map no longer needed - structErrors = nil - } - } - - case reflect.Slice, reflect.Array: - cField.isSliceOrArray = true - cField.sliceSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) - cField.sliceSubKind = cField.sliceSubtype.Kind() - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - case reflect.Map: - cField.isMap = true - cField.mapSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) - cField.mapSubKind = cField.mapSubtype.Kind() - - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - - default: - if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { - validationErrors.Errors[fieldError.Field] = fieldError - // free up memory reference - fieldError = nil - } - } - - CACHEFIELD: - if !isCached { - cs.fields = append(cs.fields, cField) - } - } - - structCache.Set(structType, cs) - - if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { - structPool.Put(validationErrors) - return nil - } - - return validationErrors -} - -// Field allows validation of a single field, still using tag style validation to check multiple errors -func (v *Validate) Field(f interface{}, tag string) *FieldError { - return v.FieldWithValue(nil, f, tag) -} - -// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors -func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError { - return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil) -} - -func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError { - - var cField *cachedField - var isCached bool - var valueField reflect.Value - - // This is a double check if coming from validate.Struct but need to be here in case function is called directly - if tag == noValidationTag || tag == "" { - return nil - } - - if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") { - return nil - } - - valueField = reflect.ValueOf(f) - - if cacheField == nil { - - if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { - valueField = valueField.Elem() - f = valueField.Interface() - } - - cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()} - - switch cField.kind { - case reflect.Slice, reflect.Array: - isSingleField = false // cached tags mean nothing because it will be split up while diving - cField.isSliceOrArray = true - cField.sliceSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) - cField.sliceSubKind = cField.sliceSubtype.Kind() - case reflect.Map: - isSingleField = false // cached tags mean nothing because it will be split up while diving - cField.isMap = true - cField.mapSubtype = cField.typ.Elem() - cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) - cField.mapSubKind = cField.mapSubtype.Kind() - } - } else { - cField = cacheField - } - - switch cField.kind { - - case reflect.Struct, reflect.Interface, reflect.Invalid: - - if cField.typ != reflect.TypeOf(time.Time{}) { - panic("Invalid field passed to fieldWithNameAndValue") - } - } - - if len(cField.tags) == 0 { - - if isSingleField { - cField.tags, isCached = fieldsCache.Get(tag) - } - - if !isCached { - - for _, t := range strings.Split(tag, tagSeparator) { - - if t == diveTag { - - cField.dive = true - cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") - break - } - - orVals := strings.Split(t, orSeparator) - cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} - cField.tags = append(cField.tags, cTag) - - for i, val := range orVals { - vals := strings.SplitN(val, tagKeySeparator, 2) - - key := strings.TrimSpace(vals[0]) - - if len(key) == 0 { - panic(fmt.Sprintf("Invalid validation tag on field %s", name)) - } - - param := "" - if len(vals) > 1 { - param = strings.Replace(vals[1], utf8HexComma, ",", -1) - } - - cTag.keyVals[i] = []string{key, param} - } - } - - if isSingleField { - fieldsCache.Set(cField.tag, cField.tags) - } - } - } - - var fieldErr *FieldError - var err error - - for _, cTag := range cField.tags { - - if cTag.isOrVal { - - errTag := "" - - for _, val := range cTag.keyVals { - - fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name) - - if err == nil { - return nil - } - - errTag += orSeparator + fieldErr.Tag - } - - errTag = strings.TrimLeft(errTag, orSeparator) - - fieldErr.Tag = errTag - fieldErr.Kind = cField.kind - fieldErr.Type = cField.typ - - return fieldErr - } - - if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil { - - fieldErr.Kind = cField.kind - fieldErr.Type = cField.typ - - return fieldErr - } - } - - if cField.dive { - - if cField.isSliceOrArray { - - if errs := v.traverseSliceOrArray(val, current, valueField, cField); errs != nil && len(errs) > 0 { - - return &FieldError{ - Field: cField.name, - Kind: cField.kind, - Type: cField.typ, - Value: f, - IsPlaceholderErr: true, - IsSliceOrArray: true, - SliceOrArrayErrs: errs, - } - } - - } else if cField.isMap { - if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 { - - return &FieldError{ - Field: cField.name, - Kind: cField.kind, - Type: cField.typ, - Value: f, - IsPlaceholderErr: true, - IsMap: true, - MapErrs: errs, - } - } - } else { - // throw error, if not a slice or map then should not have gotten here - panic("dive error! can't dive on a non slice or map") - } - } - - return nil -} - -func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error { - - errs := map[interface{}]error{} - - for _, key := range valueField.MapKeys() { - - idxField := valueField.MapIndex(key) - - if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - cField.mapSubKind = idxField.Kind() - } - - switch cField.mapSubKind { - case reflect.Struct, reflect.Interface: - - if cField.isTimeSubtype { - - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - - continue - } - - if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { - - if strings.Contains(cField.diveTag, omitempty) { - continue - } - - tags := strings.Split(cField.diveTag, tagSeparator) - - if len(tags) > 0 { - - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) - - if len(vals) > 1 { - param = vals[1] - } - - errs[key.Interface()] = &FieldError{ - Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), - Tag: vals[0], - Param: param, - Value: idxField.Interface(), - Kind: idxField.Kind(), - Type: cField.mapSubtype, - } - } - - continue - } - - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if idxField.Kind() == reflect.Interface { - - idxField = idxField.Elem() - - if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - } - - if idxField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - - continue - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { - errs[key.Interface()] = structErrors - } - - default: - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil { - errs[key.Interface()] = fieldError - } - } - } - - return errs -} - -func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error { - - errs := map[int]error{} - - for i := 0; i < valueField.Len(); i++ { - - idxField := valueField.Index(i) - - if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - cField.sliceSubKind = idxField.Kind() - } - - switch cField.sliceSubKind { - case reflect.Struct, reflect.Interface: - - if cField.isTimeSubtype { - - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - - continue - } - - if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() { - - if strings.Contains(cField.diveTag, omitempty) { - continue - } - - tags := strings.Split(cField.diveTag, tagSeparator) - - if len(tags) > 0 { - - var param string - vals := strings.SplitN(tags[0], tagKeySeparator, 2) - - if len(vals) > 1 { - param = vals[1] - } - - errs[i] = &FieldError{ - Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i), - Tag: vals[0], - Param: param, - Value: idxField.Interface(), - Kind: idxField.Kind(), - Type: cField.sliceSubtype, - } - } - - continue - } - - // if we get here, the field is interface and could be a struct or a field - // and we need to check the inner type and validate - if idxField.Kind() == reflect.Interface { - - idxField = idxField.Elem() - - if idxField.Kind() == reflect.Ptr && !idxField.IsNil() { - idxField = idxField.Elem() - } - - if idxField.Kind() == reflect.Struct { - goto VALIDATESTRUCT - } - - // sending nil for cField as it was type interface and could be anything - // each time and so must be calculated each time and can't be cached reliably - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - - continue - } - - VALIDATESTRUCT: - if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil { - errs[i] = structErrors - } - - default: - if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil { - errs[i] = fieldError - } - } - } - - return errs -} - -func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) { - - // OK to continue because we checked it's existance before getting into this loop - if key == omitempty { - return nil, nil - } - - valFunc, ok := v.validationFuncs[key] - if !ok { - panic(fmt.Sprintf("Undefined validation function on field %s", name)) - } - - if err := valFunc(val, current, f, param); err { - return nil, nil - } - - return &FieldError{ - Field: name, - Tag: key, - Value: f, - Param: param, - }, errors.New(key) -} From e2a5b9881ee240bec073efbe8ad2911e7cd8318d Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Thu, 16 Jul 2015 09:24:18 -0400 Subject: [PATCH 16/20] Add ValidationErrs Caching when no errors occur, let's reuse the existing object. --- validator.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/validator.go b/validator.go index 468c720..4a71e03 100644 --- a/validator.go +++ b/validator.go @@ -14,6 +14,7 @@ import ( "fmt" "reflect" "strings" + "sync" "time" "unicode" ) @@ -38,8 +39,14 @@ const ( var ( timeType = reflect.TypeOf(time.Time{}) timePtrType = reflect.TypeOf(&time.Time{}) + errsPool = &sync.Pool{New: newValidationErrors} ) +// returns new ValidationErrors to the pool +func newValidationErrors() interface{} { + return map[string]*FieldError{} +} + // 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 @@ -91,18 +98,10 @@ type FieldError struct { Type reflect.Type Param string Value interface{} - // IsPlaceholderErr bool - // IsSliceOrArray bool - // IsMap bool - // SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors - // MapErrs map[interface{}]error // counld be FieldError, StructErrors } // New creates a new Validate instance for use. func New(config Config) *Validate { - - // structPool = &sync.Pool{New: newStructErrors} - return &Validate{config: config} } @@ -127,12 +126,13 @@ func (v *Validate) RegisterValidation(key string, f Func) error { // Field allows validation of a single field, still using tag style validation to check multiple errors func (v *Validate) Field(field interface{}, tag string) ValidationErrors { - errs := map[string]*FieldError{} + errs := errsPool.Get().(map[string]*FieldError) fieldVal := reflect.ValueOf(field) v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "") if len(errs) == 0 { + errsPool.Put(errs) return nil } @@ -142,12 +142,13 @@ func (v *Validate) Field(field interface{}, tag string) ValidationErrors { // FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors { - errs := map[string]*FieldError{} + errs := errsPool.Get().(map[string]*FieldError) topVal := reflect.ValueOf(val) v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "") if len(errs) == 0 { + errsPool.Put(errs) return nil } @@ -160,12 +161,13 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string // the Array or Map. func (v *Validate) Struct(current interface{}) ValidationErrors { - errs := map[string]*FieldError{} + errs := errsPool.Get().(map[string]*FieldError) sv := reflect.ValueOf(current) v.tranverseStruct(sv, sv, sv, "", errs, true) if len(errs) == 0 { + errsPool.Put(errs) return nil } From e005b06f11389f52def2c1ce03864b603ebebe44 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Thu, 16 Jul 2015 22:45:36 -0400 Subject: [PATCH 17/20] Add Field Tag caching now the benchmarks are getting back to awesome! --- validator.go | 131 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 52 deletions(-) diff --git a/validator.go b/validator.go index 4a71e03..db665d8 100644 --- a/validator.go +++ b/validator.go @@ -40,6 +40,7 @@ var ( timeType = reflect.TypeOf(time.Time{}) timePtrType = reflect.TypeOf(&time.Time{}) errsPool = &sync.Pool{New: newValidationErrors} + tagsCache = &tagCacheMap{m: map[string][]*tagCache{}} ) // returns new ValidationErrors to the pool @@ -205,6 +206,29 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec } } +type tagCache struct { + tagVals [][]string + isOrVal bool +} + +type tagCacheMap struct { + lock sync.RWMutex + m map[string][]*tagCache +} + +func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *tagCacheMap) Set(key string, value []*tagCache) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) { if tag == skipValidationTag { @@ -296,21 +320,57 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. } } + tags, isCached := tagsCache.Get(tag) + + if !isCached { + + tags = []*tagCache{} + + for _, t := range strings.Split(tag, tagSeparator) { + + if t == diveTag { + tags = append(tags, &tagCache{tagVals: [][]string{[]string{t}}}) + break + } + + // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" + orVals := strings.Split(t, orSeparator) + cTag := &tagCache{isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))} + tags = append(tags, cTag) + + var key string + var param string + + for i, val := range orVals { + vals := strings.SplitN(val, tagKeySeparator, 2) + key = vals[0] + + if len(key) == 0 { + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name))) + } + + if len(vals) > 1 { + param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } + + cTag.tagVals[i] = []string{key, param} + } + } + tagsCache.Set(tag, tags) + } + var dive bool var diveSubTag string - for _, t := range strings.Split(tag, tagSeparator) { - - if t == diveTag { + for _, cTag := range tags { + if cTag.tagVals[0][0] == diveTag { dive = true diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") break } - // no use in checking tags if it's empty and is ok to be - // omitempty needs to be the first tag if you wish to use it - if t == omitempty { + if cTag.tagVals[0][0] == omitempty { if !hasValue(topStruct, currentStruct, current, typ, kind, "") { return @@ -318,26 +378,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. continue } - var key string - var param string - - // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" - if strings.Index(t, orSeparator) == -1 { - vals := strings.SplitN(t, tagKeySeparator, 2) - key = vals[0] - - if len(key) == 0 { - panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name))) - } - - if len(vals) > 1 { - param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) - } - } else { - key = t - } - - if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, key, param, name) { + if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, cTag, name) { return } } @@ -387,45 +428,31 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va } // validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok -func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool { - - // check if key is orVals, it could be! - orVals := strings.Split(key, orSeparator) +// func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool { +func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, cTag *tagCache, name string) bool { - if len(orVals) > 1 { + if cTag.isOrVal { - var errTag string + errTag := "" - for _, val := range orVals { - vals := strings.SplitN(val, tagKeySeparator, 2) - - if len(vals[0]) == 0 { - panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name))) - } - - param := "" - if len(vals) > 1 { - param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) - } + for _, val := range cTag.tagVals { - // validate and keep track! - valFunc, ok := v.config.ValidationFuncs[vals[0]] + valFunc, ok := v.config.ValidationFuncs[val[0]] if !ok { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) } - if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { + if valFunc(topStruct, currentStruct, current, currentType, currentKind, val[1]) { return false } - errTag += orSeparator + vals[0] + errTag += orSeparator + val[0] } errs[errPrefix+name] = &FieldError{ Field: name, Tag: errTag[1:], Value: current.Interface(), - Param: param, Type: currentType, Kind: currentKind, } @@ -433,20 +460,20 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. return true } - valFunc, ok := v.config.ValidationFuncs[key] + valFunc, ok := v.config.ValidationFuncs[cTag.tagVals[0][0]] if !ok { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) } - if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) { + if valFunc(topStruct, currentStruct, current, currentType, currentKind, cTag.tagVals[0][1]) { return false } errs[errPrefix+name] = &FieldError{ Field: name, - Tag: key, + Tag: cTag.tagVals[0][0], Value: current.Interface(), - Param: param, + Param: cTag.tagVals[0][1], Type: currentType, Kind: currentKind, } From 933fe0b7a959b4544a8b16fd8e66b7b7128e85bb Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Fri, 17 Jul 2015 09:31:20 -0400 Subject: [PATCH 18/20] Add test for comma and pipe obfuscation --- validator_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validator_test.go b/validator_test.go index 9d4318f..f9a042c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -229,6 +229,18 @@ func AssertError(t *testing.T, errs ValidationErrors, key, field, expectedTag st EqualSkip(t, 2, val.Tag, expectedTag) } +func TestCommaAndPipeObfuscationValidation(t *testing.T) { + s := "My Name Is, |joeybloggs|" + + errs := validate.Field(s, "excludesall=0x2C") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "excludesall") + + errs = validate.Field(s, "excludesall=0x7C") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "excludesall") +} + func TestBadKeyValidation(t *testing.T) { type Test struct { Name string `validate:"required, "` From 41b4a43989df3988e6b40cd8c90e17d57cee1758 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sun, 19 Jul 2015 10:48:16 -0400 Subject: [PATCH 19/20] Updated documentation for new v6 --- README.md | 62 +++++++--------- doc.go | 105 +++++---------------------- examples/simple.go | 34 +++------ examples_test.go | 177 +++++++++++++++++++++++---------------------- regexes.go | 1 - validator.go | 86 +++++++++++----------- 6 files changed, 192 insertions(+), 273 deletions(-) diff --git a/README.md b/README.md index 36ac9da..7f5af15 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.1)](https://travis-ci.org/bluesuncorp/validator) -[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v5)](https://coveralls.io/r/bluesuncorp/validator?branch=v5) -[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5) +[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v6)](https://travis-ci.org/bluesuncorp/validator) +[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6) +[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6) Package validator implements value validations for structs and individual fields based on tags. @@ -19,20 +19,20 @@ Installation Use go get. - go get gopkg.in/bluesuncorp/validator.v5 + go get gopkg.in/bluesuncorp/validator.v6 or to update - go get -u gopkg.in/bluesuncorp/validator.v5 + go get -u gopkg.in/bluesuncorp/validator.v6 Then import the validator package into your own code. - import "gopkg.in/bluesuncorp/validator.v5" + import "gopkg.in/bluesuncorp/validator.v6" Usage and documentation ------ -Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs. +Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v6 for detailed usage docs. ##### Example: ```go @@ -41,7 +41,7 @@ package main import ( "fmt" - "gopkg.in/bluesuncorp/validator.v5" + "gopkg.in/bluesuncorp/validator.v6" ) // User contains user information @@ -66,7 +66,12 @@ var validate *validator.Validate func main() { - validate = validator.New("validate", validator.BakedInValidators) + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validate = validator.New(config) address := &Address{ Street: "Eavesdown Docks", @@ -83,31 +88,14 @@ func main() { Addresses: []*Address{address}, } - // returns nil or *StructErrors + // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { - // err will be of type *FieldError - err := errs.Errors["Age"] - fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag - fmt.Println(err.Field) // output: Age - fmt.Println(err.Tag) // output: lte - fmt.Println(err.Kind) // output: uint8 - fmt.Println(err.Type) // output: uint8 - fmt.Println(err.Param) // output: 130 - fmt.Println(err.Value) // output: 135 - - // or if you prefer you can use the Flatten function - // NOTE: I find this usefull when using a more hard static approach of checking field errors. - // The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs - // to a routine which loops through the errors, creates and translates the error message into the - // users locale and returns a map of map[string]string // field and error which I then use - // within the HTML rendering. - - flat := errs.Flatten() - fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag] - err = flat["Addresses[0].Address.City"] + fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag + // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag + err := errs["User.Addresses[0].City"] fmt.Println(err.Field) // output: City fmt.Println(err.Tag) // output: required fmt.Println(err.Kind) // output: string @@ -126,14 +114,18 @@ func main() { Benchmarks ------ ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 +NOTE: allocations for structs are up from v5, however ns/op for parallel operations are way down. +It was a decicion not to cache struct info because although it reduced allocation to v5 levels, it +hurt parallel performance too much. ```go $ go test -cpu=4 -bench=. -benchmem=true PASS -BenchmarkValidateField-4 3000000 429 ns/op 192 B/op 2 allocs/op -BenchmarkValidateStructSimple-4 500000 2877 ns/op 657 B/op 10 allocs/op -BenchmarkTemplateParallelSimple-4 500000 3097 ns/op 657 B/op 10 allocs/op -BenchmarkValidateStructLarge-4 100000 15228 ns/op 4350 B/op 62 allocs/op -BenchmarkTemplateParallelLarge-4 100000 14257 ns/op 4354 B/op 62 allocs/op +BenchmarkField-4 5000000 314 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTag-4 500000 2425 ns/op 20 B/op 2 allocs/op +BenchmarkStructSimple-4 500000 3117 ns/op 553 B/op 14 allocs/op +BenchmarkStructSimpleParallel-4 1000000 1149 ns/op 553 B/op 14 allocs/op +BenchmarkStructComplex-4 100000 19580 ns/op 3230 B/op 102 allocs/op +BenchmarkStructComplexParallel-4 200000 6686 ns/op 3232 B/op 102 allocs/op ``` How to Contribute diff --git a/doc.go b/doc.go index f45ef34..df2db9f 100644 --- a/doc.go +++ b/doc.go @@ -1,59 +1,9 @@ /* -Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field and Cross Struct validation for nested structs. +Package validator implements value validations for structs and individual fields based on tags. +It can also handle Cross Field and Cross Struct validation for nested structs and has the ability +to dive into arrays and maps of any type. -Validate - - validate := validator.New("validate", validator.BakedInValidators) - - errs := validate.Struct(//your struct) - valErr := validate.Field(field, "omitempty,min=1,max=10") - -A simple example usage: - - type UserDetail struct { - Details string `validate:"-"` - } - - type User struct { - Name string `validate:"required,max=60"` - PreferedName string `validate:"omitempty,max=60"` - Sub UserDetail - } - - user := &User { - Name: "", - } - - // errs will contain a hierarchical list of errors - // using the StructErrors struct - // or nil if no errors exist - errs := validate.Struct(user) - - // in this case 1 error Name is required - errs.Struct will be "User" - errs.StructErrors will be empty <-- fields that were structs - errs.Errors will have 1 error of type FieldError - - NOTE: Anonymous Structs - they don't have names so expect the Struct name - within StructErrors to be blank. - -Error Handling - -The error can be used like so - - fieldErr, _ := errs["Name"] - fieldErr.Field // "Name" - fieldErr.ErrorTag // "required" - -Both StructErrors and FieldError implement the Error interface but it's -intended use is for development + debugging, not a production error message. - - fieldErr.Error() // Field validation for "Name" failed on the "required" tag - errs.Error() - // Struct: User - // Field validation for "Name" failed on the "required" tag - -Why not a better error message? because this library intends for you to handle your own error messages +Why not a better error message? because this library intends for you to handle your own error messages. Why should I handle my own errors? Many reasons, for us building an internationalized application I needed to know the field and what validation failed so that I could provide an error in the users specific language. @@ -66,21 +16,12 @@ I needed to know the field and what validation failed so that I could provide an return "Translated string based on field" } -The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue! -Flatten will return a map of FieldError's but the field name will be namespaced. - - // if UserDetail Details field failed validation - Field will be "Sub.Details" - - // for Name - Field will be "Name" - Custom Functions Custom functions can be added - //Structure - func customFunc(top interface{}, current interface{}, field interface{}, param string) bool { + // Structure + func customFunc(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if whatever { return false @@ -89,7 +30,7 @@ Custom functions can be added return true } - validate.AddFunction("custom tag name", customFunc) + validate.RegisterValidation("custom tag name", customFunc) // NOTES: using the same tag name as an existing function // will overwrite the existing one @@ -97,7 +38,7 @@ Cross Field Validation Cross Field Validation can be implemented, for example Start & End Date range validation - // NOTE: when calling validate.Struct(val) val will be the top level struct passed + // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed // into the function // when calling validate.FieldWithValue(val, field, tag) val will be // whatever you pass, struct, field... @@ -106,18 +47,7 @@ Cross Field Validation can be implemented, for example Start & End Date range va // Because of the specific requirements and field names within each persons project that // uses this library it is likely that custom functions will need to be created for your // Cross Field Validation needs, however there are some build in Generic Cross Field validations, - // see Baked In Validators and Tags below - - func isDateRangeValid(val interface{}, field interface{}, param string) bool { - - myStruct := val.(myStructType) - - if myStruct.Start.After(field.(time.Time)) { - return false - } - - return true - } + // see Baked In Validators eqfield, nefield, gtfield, gtefield, ltfield, ltefield and Tags below Multiple Validators @@ -135,7 +65,7 @@ Bad Validator definitions are not handled by the library Field `validate:"min=10,max=0"` } - // this definition of min max will never validate + // this definition of min max will never succeed Baked In Validators and Tags @@ -148,6 +78,11 @@ included within the parameter i.e. excludesall=, you will need to use the UTF-8 representation 0x2C, which is replaced in the code as a comma, so the above will become excludesall=0x2C +NOTE3: pipe is the default separator of or validation tags, if you wish to have a pipe +included within the parameter i.e. excludesall=| you will need to use the UTF-8 hex +representation 0x7C, which is replaced in the code as a pipe, so the above will +become excludesall=0x7C + Here is a list of the current built in validators: - @@ -162,14 +97,14 @@ Here is a list of the current built in validators: structonly When a field that is a nest struct in encountered and contains this flag - any validation on the nested struct such as "required" will be run, but - none of the nested struct fields will be validated. This is usefull if - inside of you program you know the struct will be valid, but need to - verify it has been assigned. + any validation on the nested struct will be run, but none of the nested + struct fields will be validated. This is usefull if inside of you program + you know the struct will be valid, but need to verify it has been assigned. + NOTE: only "required" and "omitempty" can be used on a struct itself. omitempty Allows conditional validation, for example if a field is not set with - a value (Determined by the required validator) then other validation + a value (Determined by the "required" validator) then other validation such as min or max won't run, but if a value is set validation will run. (Usage: omitempty) diff --git a/examples/simple.go b/examples/simple.go index 59cb1a9..98dabed 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "gopkg.in/bluesuncorp/validator.v5" + "gopkg.in/bluesuncorp/validator.v6" ) // User contains user information @@ -28,7 +28,12 @@ var validate *validator.Validate func main() { - validate = validator.New("validate", validator.BakedInValidators) + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validate = validator.New(config) address := &Address{ Street: "Eavesdown Docks", @@ -45,31 +50,14 @@ func main() { Addresses: []*Address{address}, } - // returns nil or *StructErrors + // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { - // err will be of type *FieldError - err := errs.Errors["Age"] - fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag - fmt.Println(err.Field) // output: Age - fmt.Println(err.Tag) // output: lte - fmt.Println(err.Kind) // output: uint8 - fmt.Println(err.Type) // output: uint8 - fmt.Println(err.Param) // output: 130 - fmt.Println(err.Value) // output: 135 - - // or if you prefer you can use the Flatten function - // NOTE: I find this usefull when using a more hard static approach of checking field errors. - // The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs - // to a routine which loops through the errors, creates and translates the error message into the - // users locale and returns a map of map[string]string // field and error which I then use - // within the HTML rendering. - - flat := errs.Flatten() - fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag] - err = flat["Addresses[0].Address.City"] + fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag + // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag + err := errs["User.Addresses[0].City"] fmt.Println(err.Field) // output: City fmt.Println(err.Tag) // output: required fmt.Println(err.Kind) // output: string diff --git a/examples_test.go b/examples_test.go index a2aca52..9d01450 100644 --- a/examples_test.go +++ b/examples_test.go @@ -1,89 +1,92 @@ package validator_test -// func ExampleValidate_new() { -// validator.New("validate", validator.BakedInValidators) -// } - -// func ExampleValidate_addFunction() { -// // This should be stored somewhere globally -// var validate *validator.Validate - -// validate = validator.New("validate", validator.BakedInValidators) - -// fn := func(top interface{}, current interface{}, field interface{}, param string) bool { -// return field.(string) == "hello" -// } - -// validate.AddFunction("valueishello", fn) - -// message := "hello" -// err := validate.Field(message, "valueishello") -// fmt.Println(err) -// //Output: -// // -// } - -// func ExampleValidate_field() { -// // This should be stored somewhere globally -// var validate *validator.Validate - -// validate = validator.New("validate", validator.BakedInValidators) - -// i := 0 -// err := validate.Field(i, "gt=1,lte=10") -// fmt.Println(err.Field) -// fmt.Println(err.Tag) -// fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time -// fmt.Println(err.Type) -// fmt.Println(err.Param) -// fmt.Println(err.Value) -// //Output: -// // -// //gt -// //int -// //int -// //1 -// //0 -// } - -// func ExampleValidate_struct() { -// // This should be stored somewhere globally -// var validate *validator.Validate - -// validate = validator.New("validate", validator.BakedInValidators) - -// type ContactInformation struct { -// Phone string `validate:"required"` -// Street string `validate:"required"` -// City string `validate:"required"` -// } - -// type User struct { -// Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) -// Age int8 `validate:"required,gt=0,lt=150"` -// Email string `validate:"email"` -// ContactInformation []*ContactInformation -// } - -// contactInfo := &ContactInformation{ -// Street: "26 Here Blvd.", -// City: "Paradeso", -// } - -// user := &User{ -// Name: "Joey Bloggs", -// Age: 31, -// Email: "joeybloggs@gmail.com", -// ContactInformation: []*ContactInformation{contactInfo}, -// } - -// structError := validate.Struct(user) -// for _, fieldError := range structError.Errors { -// fmt.Println(fieldError.Field) // Phone -// fmt.Println(fieldError.Tag) // required -// //... and so forth -// //Output: -// //Phone -// //required -// } -// } +import ( + "fmt" + + "../validator" +) + +func ExampleValidate_new() { + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validator.New(config) +} + +func ExampleValidate_field() { + // This should be stored somewhere globally + var validate *validator.Validate + + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validate = validator.New(config) + + i := 0 + errs := validate.Field(i, "gt=1,lte=10") + err := errs[""] + fmt.Println(err.Field) + fmt.Println(err.Tag) + fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time + fmt.Println(err.Type) + fmt.Println(err.Param) + fmt.Println(err.Value) + //Output: + // + //gt + //int + //int + //1 + //0 +} + +func ExampleValidate_struct() { + // This should be stored somewhere globally + var validate *validator.Validate + + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validate = validator.New(config) + + type ContactInformation struct { + Phone string `validate:"required"` + Street string `validate:"required"` + City string `validate:"required"` + } + + type User struct { + Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) + Age int8 `validate:"required,gt=0,lt=150"` + Email string `validate:"email"` + ContactInformation []*ContactInformation + } + + contactInfo := &ContactInformation{ + Street: "26 Here Blvd.", + City: "Paradeso", + } + + user := &User{ + Name: "Joey Bloggs", + Age: 31, + Email: "joeybloggs@gmail.com", + ContactInformation: []*ContactInformation{contactInfo}, + } + + errs := validate.Struct(user) + for _, v := range errs { + fmt.Println(v.Field) // Phone + fmt.Println(v.Tag) // required + //... and so forth + //Output: + //Phone + //required + } +} diff --git a/regexes.go b/regexes.go index e3f420e..d061b03 100644 --- a/regexes.go +++ b/regexes.go @@ -59,6 +59,5 @@ var ( ) func matchesRegex(regex *regexp.Regexp, value string) bool { - // fieldAsString := field.(string) //this will panic inherently return regex.MatchString(value) } diff --git a/validator.go b/validator.go index db665d8..6c44f2b 100644 --- a/validator.go +++ b/validator.go @@ -48,16 +48,36 @@ func newValidationErrors() interface{} { return map[string]*FieldError{} } -// 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 -// or make it thread safe on your end +type tagCache struct { + tagVals [][]string + isOrVal bool +} + +type tagCacheMap struct { + lock sync.RWMutex + m map[string][]*tagCache +} + +func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) { + s.lock.RLock() + defer s.lock.RUnlock() + value, ok := s.m[key] + return value, ok +} + +func (s *tagCacheMap) Set(key string, value []*tagCache) { + s.lock.Lock() + defer s.lock.Unlock() + s.m[key] = value +} + +// Validate contains the validator settings passed in using the Config struct type Validate struct { config Config } -// Config contains the options that Validator with use -// passed to the New function +// Config contains the options that a Validator instance will use. +// It is passed to the New() function type Config struct { TagName string ValidationFuncs map[string]Func @@ -71,20 +91,21 @@ type Config struct { type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, 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 +// it exists to allow for multiple errors to be passed from this library +// and yet still subscribe 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. +// Error 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 +// the FieldError found within the ValidationErrors map func (ve ValidationErrors) Error() string { buff := bytes.NewBufferString("") for key, err := range ve { buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag)) + buff.WriteString("\n") } return strings.TrimSpace(buff.String()) @@ -107,7 +128,7 @@ func New(config Config) *Validate { } // RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key -// NOTE: if the key already exists, it will get replaced. +// NOTE: if the key already exists, the previous validation function will be replaced. // NOTE: this method is not thread-safe func (v *Validate) RegisterValidation(key string, f Func) error { @@ -124,7 +145,9 @@ func (v *Validate) RegisterValidation(key string, f Func) error { return nil } -// Field allows validation of a single field, still using tag style validation to check multiple errors +// Field validates a single field using tag style validation and returns ValidationErrors +// NOTE: it returns ValidationErrors instead of a single FieldError because this can also +// validate Array, Slice and maps fields which may contain more than one error func (v *Validate) Field(field interface{}, tag string) ValidationErrors { errs := errsPool.Get().(map[string]*FieldError) @@ -140,7 +163,9 @@ func (v *Validate) Field(field interface{}, tag string) ValidationErrors { return errs } -// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors +// FieldWithValue validates a single field, against another fields value using tag style validation and returns ValidationErrors +// NOTE: it returns ValidationErrors instead of a single FieldError because this can also +// validate Array, Slice and maps fields which may contain more than one error func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors { errs := errsPool.Get().(map[string]*FieldError) @@ -156,10 +181,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string return errs } -// Struct validates a struct, even it's nested structs, and returns a struct containing the errors -// 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. +// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified. func (v *Validate) Struct(current interface{}) ValidationErrors { errs := errsPool.Get().(map[string]*FieldError) @@ -175,6 +197,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors { return errs } +// tranverseStruct traverses a structs fields and then passes them to be validated by traverseField func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool) { if current.Kind() == reflect.Ptr && !current.IsNil() { @@ -206,29 +229,7 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec } } -type tagCache struct { - tagVals [][]string - isOrVal bool -} - -type tagCacheMap struct { - lock sync.RWMutex - m map[string][]*tagCache -} - -func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) { - s.lock.RLock() - defer s.lock.RUnlock() - value, ok := s.m[key] - return value, ok -} - -func (s *tagCacheMap) Set(key string, value []*tagCache) { - s.lock.Lock() - defer s.lock.Unlock() - s.m[key] = value -} - +// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) { if tag == skipValidationTag { @@ -399,6 +400,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. } } +// traverseSlice traverses a Slice or Array's elements and passes them to traverseField for validation func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) { for i := 0; i < current.Len(); i++ { @@ -413,6 +415,7 @@ func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect. } } +// traverseMap traverses a map's elements and passes them to traverseField for validation func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) { for _, key := range current.MapKeys() { @@ -427,8 +430,7 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va } } -// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok -// func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool { +// validateField validates a field based on the provided tag's key and param values and returns true if there is an error or false if all ok func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, cTag *tagCache, name string) bool { if cTag.isOrVal { From ae135ef6134db0362e209e022b42112c87dc7d32 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Jul 2015 11:41:32 -0400 Subject: [PATCH 20/20] Update README.md Change CI to Semaphore --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f5af15..c4da807 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v6)](https://travis-ci.org/bluesuncorp/validator) +[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6) [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6)