From 7af3fb7c1fad5e7bb7430f4f7112286a1ccff50c Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 14 Jul 2015 13:36:55 -0400 Subject: [PATCH] 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)