summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-playground/validator/v10/baked_in.go
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-10-13 16:49:53 +0200
committerLibravatar tobi <tobi.smethurst@protonmail.com>2025-10-17 15:32:55 +0200
commitea7eeada77a52fd58a9e1a949a39eccc7bce955a (patch)
treeede9f651d0bd7782b565883f9384341530f8320f /vendor/github.com/go-playground/validator/v10/baked_in.go
parent[bugfix] repeated posts on timeline endpoints (#4494) (diff)
downloadgotosocial-ea7eeada77a52fd58a9e1a949a39eccc7bce955a.tar.xz
[chore] update dependencies (#4495)
- github.com/coreos/go-oidc/v3: v3.15.0 -> v3.16.0 - github.com/go-playground/form/v4: v4.2.1 -> v4.3.0 - github.com/go-swagger/go-swagger: v0.32.3 -> v0.33.1 - golang.org/x/crypto: v0.42.0 -> v0.43.0 - golang.org/x/image: v0.31.0 -> v0.32.0 - golang.org/x/net: v0.45.0 -> v0.46.0 - golang.org/x/oauth2: v0.31.0 -> v0.32.0 - golang.org/x/sys: v0.36.0 -> v0.37.0 - golang.org/x/text: v0.29.0 -> v0.30.0 - modernc.org/sqlite: v1.39.0 -> v1.39.1 (w/ concurrency workaround) Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4495 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'vendor/github.com/go-playground/validator/v10/baked_in.go')
-rw-r--r--vendor/github.com/go-playground/validator/v10/baked_in.go301
1 files changed, 184 insertions, 117 deletions
diff --git a/vendor/github.com/go-playground/validator/v10/baked_in.go b/vendor/github.com/go-playground/validator/v10/baked_in.go
index 5332cf3fa..8fd55e77e 100644
--- a/vendor/github.com/go-playground/validator/v10/baked_in.go
+++ b/vendor/github.com/go-playground/validator/v10/baked_in.go
@@ -2,10 +2,12 @@ package validator
import (
"bytes"
+ "cmp"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"io/fs"
"net"
@@ -116,6 +118,7 @@ var (
"fieldcontains": fieldContains,
"fieldexcludes": fieldExcludes,
"alpha": isAlpha,
+ "alphaspace": isAlphaSpace,
"alphanum": isAlphanum,
"alphaunicode": isAlphaUnicode,
"alphanumunicode": isAlphanumUnicode,
@@ -132,6 +135,7 @@ var (
"email": isEmail,
"url": isURL,
"http_url": isHttpURL,
+ "https_url": isHttpsURL,
"uri": isURI,
"urn_rfc2141": isUrnRFC2141, // RFC 2141
"file": isFile,
@@ -244,6 +248,7 @@ var (
"cron": isCron,
"spicedb": isSpiceDB,
"ein": isEIN,
+ "validateFn": isValidateFn,
}
)
@@ -294,7 +299,7 @@ func isOneOf(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v = strconv.FormatUint(field.Uint(), 10)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
for i := 0; i < len(vals); i++ {
if vals[i] == v {
@@ -310,7 +315,7 @@ func isOneOfCI(fl FieldLevel) bool {
field := fl.Field()
if field.Kind() != reflect.String {
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
v := field.String()
for _, val := range vals {
@@ -384,13 +389,13 @@ func isUnique(fl FieldLevel) bool {
}
if uniqueField.Kind() != field.Kind() {
- panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface()))
+ panic(fmt.Sprintf("Bad field type %s:%s", field.Type(), uniqueField.Type()))
}
- return field.Interface() != uniqueField.Interface()
+ return getValue(field) != getValue(uniqueField)
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
}
@@ -471,7 +476,7 @@ func isLongitude(fl FieldLevel) bool {
case reflect.Float64:
v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
return longitudeRegex().MatchString(v)
@@ -494,7 +499,7 @@ func isLatitude(fl FieldLevel) bool {
case reflect.Float64:
v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
return latitudeRegex().MatchString(v)
@@ -945,7 +950,6 @@ func isNeField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() != currentField.Int()
@@ -966,9 +970,8 @@ func isNeField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Interface().(time.Time)
- fieldTime := field.Interface().(time.Time)
+ t := getValue(currentField).(time.Time)
+ fieldTime := getValue(field).(time.Time)
return !fieldTime.Equal(t)
}
@@ -1005,7 +1008,6 @@ func isLteCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() <= topField.Int()
@@ -1023,9 +1025,8 @@ func isLteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
+ topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
}
@@ -1052,7 +1053,6 @@ func isLtCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() < topField.Int()
@@ -1070,9 +1070,8 @@ func isLtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
+ topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Before(topTime)
}
@@ -1098,7 +1097,6 @@ func isGteCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() >= topField.Int()
@@ -1116,9 +1114,8 @@ func isGteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
+ topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.After(topTime) || fieldTime.Equal(topTime)
}
@@ -1144,7 +1141,6 @@ func isGtCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() > topField.Int()
@@ -1162,9 +1158,8 @@ func isGtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
+ topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.After(topTime)
}
@@ -1190,7 +1185,6 @@ func isNeCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return topField.Int() != field.Int()
@@ -1211,9 +1205,8 @@ func isNeCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- t := field.Convert(timeType).Interface().(time.Time)
- fieldTime := topField.Convert(timeType).Interface().(time.Time)
+ t := getValue(field.Convert(timeType)).(time.Time)
+ fieldTime := getValue(topField.Convert(timeType)).(time.Time)
return !fieldTime.Equal(t)
}
@@ -1239,7 +1232,6 @@ func isEqCrossStructField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return topField.Int() == field.Int()
@@ -1260,9 +1252,8 @@ func isEqCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
-
- t := field.Convert(timeType).Interface().(time.Time)
- fieldTime := topField.Convert(timeType).Interface().(time.Time)
+ t := getValue(field.Convert(timeType)).(time.Time)
+ fieldTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Equal(t)
}
@@ -1288,7 +1279,6 @@ func isEqField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() == currentField.Int()
@@ -1309,9 +1299,8 @@ func isEqField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(currentField.Convert(timeType)).(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Equal(t)
}
@@ -1332,7 +1321,6 @@ func isEq(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
return field.String() == param
@@ -1367,7 +1355,7 @@ func isEq(fl FieldLevel) bool {
return field.Bool() == p
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isEqIgnoreCase is the validation function for validating if the current field's string value is
@@ -1382,7 +1370,7 @@ func isEqIgnoreCase(fl FieldLevel) bool {
return strings.EqualFold(field.String(), param)
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2
@@ -1416,7 +1404,7 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
}
if kind != reflect.String {
- panic(fmt.Sprintf("Bad field type %T", currentField.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", currentField.Type()))
}
postcodeRegexInit.Do(initPostcodes)
@@ -1472,16 +1460,7 @@ func isURI(fl FieldLevel) bool {
return err == nil
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
-}
-
-// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
-func isFileURL(path string) bool {
- if !strings.HasPrefix(path, "file:/") {
- return false
- }
- _, err := url.ParseRequestURI(path)
- return err == nil
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isURL is the validation function for validating if the current field's value is a valid URL.
@@ -1497,23 +1476,20 @@ func isURL(fl FieldLevel) bool {
return false
}
- if isFileURL(s) {
- return true
- }
-
url, err := url.Parse(s)
if err != nil || url.Scheme == "" {
return false
}
+ isFileScheme := url.Scheme == "file"
- if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
+ if (isFileScheme && (len(url.Path) == 0 || url.Path == "/")) || (!isFileScheme && len(url.Host) == 0 && len(url.Fragment) == 0 && len(url.Opaque) == 0) {
return false
}
return true
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL.
@@ -1536,7 +1512,30 @@ func isHttpURL(fl FieldLevel) bool {
return url.Scheme == "http" || url.Scheme == "https"
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
+}
+
+// isHttpsURL is the validation function for validating if the current field's value is a valid HTTPS-only URL.
+func isHttpsURL(fl FieldLevel) bool {
+ if !isURL(fl) {
+ return false
+ }
+
+ field := fl.Field()
+ switch field.Kind() {
+ case reflect.String:
+
+ s := strings.ToLower(field.String())
+
+ url, err := url.Parse(s)
+ if err != nil || url.Host == "" {
+ return false
+ }
+
+ return url.Scheme == "https"
+ }
+
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
@@ -1553,7 +1552,7 @@ func isUrnRFC2141(fl FieldLevel) bool {
return match
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isFile is the validation function for validating if the current field's value is a valid existing file path.
@@ -1570,7 +1569,7 @@ func isFile(fl FieldLevel) bool {
return !fileInfo.IsDir()
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isImage is the validation function for validating if the current field's value contains the path to a valid image file
@@ -1632,7 +1631,8 @@ func isImage(fl FieldLevel) bool {
return true
}
}
- return false
+
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isFilePath is the validation function for validating if the current field's value is a valid file path.
@@ -1686,7 +1686,7 @@ func isFilePath(fl FieldLevel) bool {
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
@@ -1768,6 +1768,11 @@ func isAlphanumUnicode(fl FieldLevel) bool {
return alphaUnicodeNumericRegex().MatchString(fl.Field().String())
}
+// isAlphaSpace is the validation function for validating if the current field's value is a valid alpha value with spaces.
+func isAlphaSpace(fl FieldLevel) bool {
+ return alphaSpaceRegex().MatchString(fl.Field().String())
+}
+
// isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
func isAlphaUnicode(fl FieldLevel) bool {
return alphaUnicodeRegex().MatchString(fl.Field().String())
@@ -1796,7 +1801,7 @@ func hasValue(fl FieldLevel) bool {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
default:
- if fl.(*validate).fldIsPointer && field.Interface() != nil {
+ if fl.(*validate).fldIsPointer && getValue(field) != nil {
return true
}
return field.IsValid() && !field.IsZero()
@@ -1807,10 +1812,13 @@ func hasValue(fl FieldLevel) bool {
func hasNotZeroValue(fl FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
- case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+ case reflect.Slice, reflect.Map:
+ // For slices and maps, consider them "not zero" only if they're both non-nil AND have elements
+ return !field.IsNil() && field.Len() > 0
+ case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
default:
- if fl.(*validate).fldIsPointer && field.Interface() != nil {
+ if fl.(*validate).fldIsPointer && getValue(field) != nil {
return !field.IsZero()
}
return field.IsValid() && !field.IsZero()
@@ -1834,7 +1842,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return field.IsNil()
default:
- if nullable && field.Interface() != nil {
+ if nullable && getValue(field) != nil {
return false
}
return field.IsValid() && field.IsZero()
@@ -1851,7 +1859,6 @@ func requireCheckFieldValue(
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() == asInt(value)
@@ -1864,7 +1871,13 @@ func requireCheckFieldValue(
case reflect.Float64:
return field.Float() == asFloat64(value)
- case reflect.Slice, reflect.Map, reflect.Array:
+ case reflect.Slice, reflect.Map:
+ if value == "nil" {
+ return field.IsNil()
+ }
+ return int64(field.Len()) == asInt(value)
+ case reflect.Array:
+ // Arrays can't be nil, so only compare lengths
return int64(field.Len()) == asInt(value)
case reflect.Bool:
@@ -1889,6 +1902,15 @@ func requiredIf(fl FieldLevel) bool {
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
}
+
+ seen := make(map[string]struct{})
+ for i := 0; i < len(params); i += 2 {
+ if _, ok := seen[params[i]]; ok {
+ panic(fmt.Sprintf("Duplicate param %s for required_if %s", params[i], fl.FieldName()))
+ }
+ seen[params[i]] = struct{}{}
+ }
+
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
@@ -2019,8 +2041,11 @@ func excludedWithout(fl FieldLevel) bool {
// requiredWithout is the validation function
// The field under validation must be present and not empty only when any of the other specified fields are not present.
func requiredWithout(fl FieldLevel) bool {
- if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
- return hasValue(fl)
+ params := parseOneOfParam2(fl.Param())
+ for _, param := range params {
+ if requireCheckFieldKind(fl, param, true) {
+ return hasValue(fl)
+ }
}
return true
}
@@ -2060,7 +2085,6 @@ func isGteField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() >= currentField.Int()
@@ -2078,9 +2102,8 @@ func isGteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(currentField.Convert(timeType)).(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t)
}
@@ -2106,7 +2129,6 @@ func isGtField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() > currentField.Int()
@@ -2124,9 +2146,8 @@ func isGtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(currentField.Convert(timeType)).(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.After(t)
}
@@ -2147,7 +2168,6 @@ func isGte(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
p := asInt(param)
@@ -2181,15 +2201,14 @@ func isGte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
-
now := time.Now().UTC()
- t := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(field.Convert(timeType)).(time.Time)
return t.After(now) || t.Equal(now)
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isGt is the validation function for validating if the current field's value is greater than the param's value.
@@ -2198,7 +2217,6 @@ func isGt(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
p := asInt(param)
@@ -2232,11 +2250,11 @@ func isGt(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
- return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
+ return getValue(field.Convert(timeType)).(time.Time).After(time.Now().UTC())
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
@@ -2245,7 +2263,6 @@ func hasLengthOf(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
p := asInt(param)
@@ -2277,7 +2294,7 @@ func hasLengthOf(fl FieldLevel) bool {
return field.Float() == p
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
@@ -2296,7 +2313,6 @@ func isLteField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() <= currentField.Int()
@@ -2314,9 +2330,8 @@ func isLteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(currentField.Convert(timeType)).(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t)
}
@@ -2342,7 +2357,6 @@ func isLtField(fl FieldLevel) bool {
}
switch kind {
-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() < currentField.Int()
@@ -2360,9 +2374,8 @@ func isLtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
-
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(currentField.Convert(timeType)).(time.Time)
+ fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Before(t)
}
@@ -2383,7 +2396,6 @@ func isLte(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
p := asInt(param)
@@ -2417,15 +2429,14 @@ func isLte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
-
now := time.Now().UTC()
- t := field.Convert(timeType).Interface().(time.Time)
+ t := getValue(field.Convert(timeType)).(time.Time)
return t.Before(now) || t.Equal(now)
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isLt is the validation function for validating if the current field's value is less than the param's value.
@@ -2434,7 +2445,6 @@ func isLt(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
-
case reflect.String:
p := asInt(param)
@@ -2468,11 +2478,11 @@ func isLt(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
- return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
+ return getValue(field.Convert(timeType)).(time.Time).Before(time.Now().UTC())
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
@@ -2642,7 +2652,7 @@ func isDir(fl FieldLevel) bool {
return fileInfo.IsDir()
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isDirPath is the validation function for validating if the current field's value is a valid directory.
@@ -2699,7 +2709,7 @@ func isDirPath(fl FieldLevel) bool {
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isJSON is the validation function for validating if the current field's value is a valid json string.
@@ -2714,12 +2724,12 @@ func isJSON(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(byteSliceType) {
- b := field.Convert(byteSliceType).Interface().([]byte)
+ b := getValue(field.Convert(byteSliceType)).([]byte)
return json.Valid(b)
}
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isJWT is the validation function for validating if the current field's value is a valid JWT string.
@@ -2766,7 +2776,7 @@ func isLowercase(fl FieldLevel) bool {
return field.String() == strings.ToLower(field.String())
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isUppercase is the validation function for validating if the current field's value is an uppercase string.
@@ -2780,7 +2790,7 @@ func isUppercase(fl FieldLevel) bool {
return field.String() == strings.ToUpper(field.String())
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
@@ -2794,7 +2804,7 @@ func isDatetime(fl FieldLevel) bool {
return err == nil
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
@@ -2816,7 +2826,7 @@ func isTimeZone(fl FieldLevel) bool {
return err == nil
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
@@ -2860,7 +2870,7 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint() % 1000)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso3166_1_alpha_numeric[code]
@@ -2884,7 +2894,7 @@ func isIso3166AlphaNumericEU(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint() % 1000)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso3166_1_alpha_numeric_eu[code]
@@ -2914,7 +2924,7 @@ func isIso4217Numeric(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint())
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso4217_numeric[code]
@@ -2930,7 +2940,7 @@ func isBCP47LanguageTag(fl FieldLevel) bool {
return err == nil
}
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
@@ -3053,7 +3063,7 @@ func hasLuhnChecksum(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
str = strconv.FormatUint(field.Uint(), 10)
default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
+ panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
size := len(str)
if size < 2 { // there has to be at least one digit that carries a meaning + the checksum
@@ -3079,3 +3089,60 @@ func isEIN(fl FieldLevel) bool {
return einRegex().MatchString(field.String())
}
+
+func isValidateFn(fl FieldLevel) bool {
+ const defaultParam = `Validate`
+
+ field := fl.Field()
+ validateFn := cmp.Or(fl.Param(), defaultParam)
+
+ ok, err := tryCallValidateFn(field, validateFn)
+ if err != nil {
+ return false
+ }
+
+ return ok
+}
+
+var (
+ errMethodNotFound = errors.New(`method not found`)
+ errMethodReturnNoValues = errors.New(`method return o values (void)`)
+ errMethodReturnInvalidType = errors.New(`method should return invalid type`)
+)
+
+func tryCallValidateFn(field reflect.Value, validateFn string) (bool, error) {
+ method := field.MethodByName(validateFn)
+ if field.CanAddr() && !method.IsValid() {
+ method = field.Addr().MethodByName(validateFn)
+ }
+
+ if !method.IsValid() {
+ return false, fmt.Errorf("unable to call %q on type %q: %w",
+ validateFn, field.Type().String(), errMethodNotFound)
+ }
+
+ returnValues := method.Call([]reflect.Value{})
+ if len(returnValues) == 0 {
+ return false, fmt.Errorf("unable to use result of method %q on type %q: %w",
+ validateFn, field.Type().String(), errMethodReturnNoValues)
+ }
+
+ firstReturnValue := returnValues[0]
+
+ switch firstReturnValue.Kind() {
+ case reflect.Bool:
+ return firstReturnValue.Bool(), nil
+ case reflect.Interface:
+ errorType := reflect.TypeOf((*error)(nil)).Elem()
+
+ if firstReturnValue.Type().Implements(errorType) {
+ return firstReturnValue.IsNil(), nil
+ }
+
+ return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got interface %v expect error)",
+ validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
+ default:
+ return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got %v expect error or bool)",
+ validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
+ }
+}