add required_without_all validator

pull/469/head
A Mashmooli 6 years ago
parent 03bfd38cc1
commit 3ec10f9949
  1. 246
      baked_in.go
  2. 19
      validator_test.go

@ -62,108 +62,109 @@ var (
// you can add, remove or even replace items to suite your needs, // you can add, remove or even replace items to suite your needs,
// or even disregard and use your own map if so desired. // or even disregard and use your own map if so desired.
bakedInValidators = map[string]Func{ bakedInValidators = map[string]Func{
"required": hasValue, "required": hasValue,
"required_with": requiredWith, "required_with": requiredWith,
"required_with_all": requiredWithAll, "required_with_all": requiredWithAll,
"required_without": requiredWithout, "required_without": requiredWithout,
"isdefault": isDefault, "required_without_all": requiredWithoutAll,
"len": hasLengthOf, "isdefault": isDefault,
"min": hasMinOf, "len": hasLengthOf,
"max": hasMaxOf, "min": hasMinOf,
"eq": isEq, "max": hasMaxOf,
"ne": isNe, "eq": isEq,
"lt": isLt, "ne": isNe,
"lte": isLte, "lt": isLt,
"gt": isGt, "lte": isLte,
"gte": isGte, "gt": isGt,
"eqfield": isEqField, "gte": isGte,
"eqcsfield": isEqCrossStructField, "eqfield": isEqField,
"necsfield": isNeCrossStructField, "eqcsfield": isEqCrossStructField,
"gtcsfield": isGtCrossStructField, "necsfield": isNeCrossStructField,
"gtecsfield": isGteCrossStructField, "gtcsfield": isGtCrossStructField,
"ltcsfield": isLtCrossStructField, "gtecsfield": isGteCrossStructField,
"ltecsfield": isLteCrossStructField, "ltcsfield": isLtCrossStructField,
"nefield": isNeField, "ltecsfield": isLteCrossStructField,
"gtefield": isGteField, "nefield": isNeField,
"gtfield": isGtField, "gtefield": isGteField,
"ltefield": isLteField, "gtfield": isGtField,
"ltfield": isLtField, "ltefield": isLteField,
"fieldcontains": fieldContains, "ltfield": isLtField,
"fieldexcludes": fieldExcludes, "fieldcontains": fieldContains,
"alpha": isAlpha, "fieldexcludes": fieldExcludes,
"alphanum": isAlphanum, "alpha": isAlpha,
"alphaunicode": isAlphaUnicode, "alphanum": isAlphanum,
"alphanumunicode": isAlphanumUnicode, "alphaunicode": isAlphaUnicode,
"numeric": isNumeric, "alphanumunicode": isAlphanumUnicode,
"number": isNumber, "numeric": isNumeric,
"hexadecimal": isHexadecimal, "number": isNumber,
"hexcolor": isHEXColor, "hexadecimal": isHexadecimal,
"rgb": isRGB, "hexcolor": isHEXColor,
"rgba": isRGBA, "rgb": isRGB,
"hsl": isHSL, "rgba": isRGBA,
"hsla": isHSLA, "hsl": isHSL,
"email": isEmail, "hsla": isHSLA,
"url": isURL, "email": isEmail,
"uri": isURI, "url": isURL,
"urn_rfc2141": isUrnRFC2141, // RFC 2141 "uri": isURI,
"file": isFile, "urn_rfc2141": isUrnRFC2141, // RFC 2141
"base64": isBase64, "file": isFile,
"base64url": isBase64URL, "base64": isBase64,
"contains": contains, "base64url": isBase64URL,
"containsany": containsAny, "contains": contains,
"containsrune": containsRune, "containsany": containsAny,
"excludes": excludes, "containsrune": containsRune,
"excludesall": excludesAll, "excludes": excludes,
"excludesrune": excludesRune, "excludesall": excludesAll,
"startswith": startsWith, "excludesrune": excludesRune,
"endswith": endsWith, "startswith": startsWith,
"isbn": isISBN, "endswith": endsWith,
"isbn10": isISBN10, "isbn": isISBN,
"isbn13": isISBN13, "isbn10": isISBN10,
"eth_addr": isEthereumAddress, "isbn13": isISBN13,
"btc_addr": isBitcoinAddress, "eth_addr": isEthereumAddress,
"btc_addr_bech32": isBitcoinBech32Address, "btc_addr": isBitcoinAddress,
"uuid": isUUID, "btc_addr_bech32": isBitcoinBech32Address,
"uuid3": isUUID3, "uuid": isUUID,
"uuid4": isUUID4, "uuid3": isUUID3,
"uuid5": isUUID5, "uuid4": isUUID4,
"uuid_rfc4122": isUUIDRFC4122, "uuid5": isUUID5,
"uuid3_rfc4122": isUUID3RFC4122, "uuid_rfc4122": isUUIDRFC4122,
"uuid4_rfc4122": isUUID4RFC4122, "uuid3_rfc4122": isUUID3RFC4122,
"uuid5_rfc4122": isUUID5RFC4122, "uuid4_rfc4122": isUUID4RFC4122,
"ascii": isASCII, "uuid5_rfc4122": isUUID5RFC4122,
"printascii": isPrintableASCII, "ascii": isASCII,
"multibyte": hasMultiByteCharacter, "printascii": isPrintableASCII,
"datauri": isDataURI, "multibyte": hasMultiByteCharacter,
"latitude": isLatitude, "datauri": isDataURI,
"longitude": isLongitude, "latitude": isLatitude,
"ssn": isSSN, "longitude": isLongitude,
"ipv4": isIPv4, "ssn": isSSN,
"ipv6": isIPv6, "ipv4": isIPv4,
"ip": isIP, "ipv6": isIPv6,
"cidrv4": isCIDRv4, "ip": isIP,
"cidrv6": isCIDRv6, "cidrv4": isCIDRv4,
"cidr": isCIDR, "cidrv6": isCIDRv6,
"tcp4_addr": isTCP4AddrResolvable, "cidr": isCIDR,
"tcp6_addr": isTCP6AddrResolvable, "tcp4_addr": isTCP4AddrResolvable,
"tcp_addr": isTCPAddrResolvable, "tcp6_addr": isTCP6AddrResolvable,
"udp4_addr": isUDP4AddrResolvable, "tcp_addr": isTCPAddrResolvable,
"udp6_addr": isUDP6AddrResolvable, "udp4_addr": isUDP4AddrResolvable,
"udp_addr": isUDPAddrResolvable, "udp6_addr": isUDP6AddrResolvable,
"ip4_addr": isIP4AddrResolvable, "udp_addr": isUDPAddrResolvable,
"ip6_addr": isIP6AddrResolvable, "ip4_addr": isIP4AddrResolvable,
"ip_addr": isIPAddrResolvable, "ip6_addr": isIP6AddrResolvable,
"unix_addr": isUnixAddrResolvable, "ip_addr": isIPAddrResolvable,
"mac": isMAC, "unix_addr": isUnixAddrResolvable,
"hostname": isHostnameRFC952, // RFC 952 "mac": isMAC,
"hostname_rfc1123": isHostnameRFC1123, // RFC 1123 "hostname": isHostnameRFC952, // RFC 952
"fqdn": isFQDN, "hostname_rfc1123": isHostnameRFC1123, // RFC 1123
"unique": isUnique, "fqdn": isFQDN,
"oneof": isOneOf, "unique": isUnique,
"html": isHTML, "oneof": isOneOf,
"html_encoded": isHTMLEncoded, "html": isHTML,
"url_encoded": isURLEncoded, "html_encoded": isHTMLEncoded,
"dir": isDir, "url_encoded": isURLEncoded,
"dir": isDir,
} }
) )
@ -1435,6 +1436,47 @@ func requiredWithout(fl FieldLevel) bool {
return true return true
} }
// RequiredWithoutAll is the validation function
// The field under validation must be present and not empty only when all of the other specified fields are not present.
func requiredWithoutAll(fl FieldLevel) bool {
field := fl.Field()
isValidateCurrentField := true
params := parseOneOfParam2(fl.Param())
for _, param := range params {
isParamFieldPresent := false
paramField := fl.Parent().FieldByName(param)
switch paramField.Kind() {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
isParamFieldPresent = !paramField.IsNil()
default:
if fl.(*validate).fldIsPointer && paramField.Interface() != nil {
isParamFieldPresent = true
}
isParamFieldPresent = paramField.IsValid() && paramField.Interface() != reflect.Zero(field.Type()).Interface()
}
if isParamFieldPresent {
isValidateCurrentField = !isParamFieldPresent
}
}
if isValidateCurrentField {
switch field.Kind() {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
default:
if fl.(*validate).fldIsPointer && field.Interface() != nil {
return true
}
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
}
}
return true
}
// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. // IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
func isGteField(fl FieldLevel) bool { func isGteField(fl FieldLevel) bool {

@ -8678,3 +8678,22 @@ func TestRequiredWithout(t *testing.T) {
t.Fatalf("failed Error: %s", errs) t.Fatalf("failed Error: %s", errs)
} }
} }
func TestRequiredWithoutAll(t *testing.T) {
test := struct {
Field1 string `validate:"omitempty" json:"field_1"`
Field2 string `validate:"omitempty" json:"field_2"`
Field3 string `validate:"required_without_all=Field1 Field2" json:"field_3"`
}{
Field3: "test_field3",
}
validate := New()
errs := validate.Struct(test)
if errs != nil {
t.Fatalf("failed Error: %s", errs)
}
}

Loading…
Cancel
Save