@@ -64,10 +64,16 @@ var (
6464 // or even disregard and use your own map if so desired.
6565 bakedInValidators = map [string ]Func {
6666 "required" : hasValue ,
67+ "required_if" : requiredIf ,
68+ "required_unless" : requiredUnless ,
6769 "required_with" : requiredWith ,
6870 "required_with_all" : requiredWithAll ,
6971 "required_without" : requiredWithout ,
7072 "required_without_all" : requiredWithoutAll ,
73+ "excluded_with" : excludedWith ,
74+ "excluded_with_all" : excludedWithAll ,
75+ "excluded_without" : excludedWithout ,
76+ "excluded_without_all" : excludedWithoutAll ,
7177 "isdefault" : isDefault ,
7278 "len" : hasLengthOf ,
7379 "min" : hasMinOf ,
@@ -174,6 +180,7 @@ var (
174180 "lowercase" : isLowercase ,
175181 "uppercase" : isUppercase ,
176182 "datetime" : isDatetime ,
183+ "timezone" : isTimeZone ,
177184 }
178185)
179186
@@ -241,23 +248,33 @@ func isUnique(fl FieldLevel) bool {
241248
242249 switch field .Kind () {
243250 case reflect .Slice , reflect .Array :
251+ elem := field .Type ().Elem ()
252+ if elem .Kind () == reflect .Ptr {
253+ elem = elem .Elem ()
254+ }
255+
244256 if param == "" {
245- m := reflect .MakeMap (reflect .MapOf (field . Type (). Elem () , v .Type ()))
257+ m := reflect .MakeMap (reflect .MapOf (elem , v .Type ()))
246258
247259 for i := 0 ; i < field .Len (); i ++ {
248- m .SetMapIndex (field .Index (i ), v )
260+ m .SetMapIndex (reflect . Indirect ( field .Index (i ) ), v )
249261 }
250262 return field .Len () == m .Len ()
251263 }
252264
253- sf , ok := field . Type (). Elem () .FieldByName (param )
265+ sf , ok := elem .FieldByName (param )
254266 if ! ok {
255267 panic (fmt .Sprintf ("Bad field name %s" , param ))
256268 }
257269
258- m := reflect .MakeMap (reflect .MapOf (sf .Type , v .Type ()))
270+ sfTyp := sf .Type
271+ if sfTyp .Kind () == reflect .Ptr {
272+ sfTyp = sfTyp .Elem ()
273+ }
274+
275+ m := reflect .MakeMap (reflect .MapOf (sfTyp , v .Type ()))
259276 for i := 0 ; i < field .Len (); i ++ {
260- m .SetMapIndex (field .Index (i ).FieldByName (param ), v )
277+ m .SetMapIndex (reflect . Indirect ( reflect . Indirect ( field .Index (i )) .FieldByName (param ) ), v )
261278 }
262279 return field .Len () == m .Len ()
263280 case reflect .Map :
@@ -1129,7 +1146,7 @@ func isEq(fl FieldLevel) bool {
11291146 return int64 (field .Len ()) == p
11301147
11311148 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1132- p := asInt ( param )
1149+ p := asIntFromType ( field . Type (), param )
11331150
11341151 return field .Int () == p
11351152
@@ -1383,6 +1400,75 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
13831400 }
13841401}
13851402
1403+ // requireCheckFieldValue is a func for check field value
1404+ func requireCheckFieldValue (fl FieldLevel , param string , value string , defaultNotFoundValue bool ) bool {
1405+ field , kind , _ , found := fl .GetStructFieldOKAdvanced2 (fl .Parent (), param )
1406+ if ! found {
1407+ return defaultNotFoundValue
1408+ }
1409+
1410+ switch kind {
1411+
1412+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1413+ return field .Int () == asInt (value )
1414+
1415+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
1416+ return field .Uint () == asUint (value )
1417+
1418+ case reflect .Float32 , reflect .Float64 :
1419+ return field .Float () == asFloat (value )
1420+
1421+ case reflect .Slice , reflect .Map , reflect .Array :
1422+ return int64 (field .Len ()) == asInt (value )
1423+ }
1424+
1425+ // default reflect.String:
1426+ return field .String () == value
1427+ }
1428+
1429+ // requiredIf is the validation function
1430+ // The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
1431+ func requiredIf (fl FieldLevel ) bool {
1432+ params := parseOneOfParam2 (fl .Param ())
1433+ if len (params )% 2 != 0 {
1434+ panic (fmt .Sprintf ("Bad param number for required_if %s" , fl .FieldName ()))
1435+ }
1436+ for i := 0 ; i < len (params ); i += 2 {
1437+ if ! requireCheckFieldValue (fl , params [i ], params [i + 1 ], false ) {
1438+ return true
1439+ }
1440+ }
1441+ return hasValue (fl )
1442+ }
1443+
1444+ // requiredUnless is the validation function
1445+ // The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
1446+ func requiredUnless (fl FieldLevel ) bool {
1447+ params := parseOneOfParam2 (fl .Param ())
1448+ if len (params )% 2 != 0 {
1449+ panic (fmt .Sprintf ("Bad param number for required_unless %s" , fl .FieldName ()))
1450+ }
1451+
1452+ for i := 0 ; i < len (params ); i += 2 {
1453+ if requireCheckFieldValue (fl , params [i ], params [i + 1 ], false ) {
1454+ return true
1455+ }
1456+ }
1457+ return hasValue (fl )
1458+ }
1459+
1460+ // ExcludedWith is the validation function
1461+ // The field under validation must not be present or is empty if any of the other specified fields are present.
1462+ func excludedWith (fl FieldLevel ) bool {
1463+ params := parseOneOfParam2 (fl .Param ())
1464+ for _ , param := range params {
1465+ if ! requireCheckFieldKind (fl , param , true ) {
1466+ return ! hasValue (fl )
1467+ }
1468+ }
1469+ return true
1470+ }
1471+
13861472// RequiredWith is the validation function
13871473// The field under validation must be present and not empty only if any of the other specified fields are present.
13881474func requiredWith (fl FieldLevel ) bool {
@@ -1395,6 +1481,18 @@ func requiredWith(fl FieldLevel) bool {
13951481 return true
13961482}
13971483
1484+ // ExcludedWithAll is the validation function
1485+ // The field under validation must not be present or is empty if all of the other specified fields are present.
1486+ func excludedWithAll (fl FieldLevel ) bool {
1487+ params := parseOneOfParam2 (fl .Param ())
1488+ for _ , param := range params {
1489+ if requireCheckFieldKind (fl , param , true ) {
1490+ return true
1491+ }
1492+ }
1493+ return ! hasValue (fl )
1494+ }
1495+
13981496// RequiredWithAll is the validation function
13991497// The field under validation must be present and not empty only if all of the other specified fields are present.
14001498func requiredWithAll (fl FieldLevel ) bool {
@@ -1407,6 +1505,15 @@ func requiredWithAll(fl FieldLevel) bool {
14071505 return hasValue (fl )
14081506}
14091507
1508+ // ExcludedWithout is the validation function
1509+ // The field under validation must not be present or is empty when any of the other specified fields are not present.
1510+ func excludedWithout (fl FieldLevel ) bool {
1511+ if requireCheckFieldKind (fl , strings .TrimSpace (fl .Param ()), true ) {
1512+ return ! hasValue (fl )
1513+ }
1514+ return true
1515+ }
1516+
14101517// RequiredWithout is the validation function
14111518// The field under validation must be present and not empty only when any of the other specified fields are not present.
14121519func requiredWithout (fl FieldLevel ) bool {
@@ -1416,6 +1523,18 @@ func requiredWithout(fl FieldLevel) bool {
14161523 return true
14171524}
14181525
1526+ // RequiredWithoutAll is the validation function
1527+ // The field under validation must not be present or is empty when all of the other specified fields are not present.
1528+ func excludedWithoutAll (fl FieldLevel ) bool {
1529+ params := parseOneOfParam2 (fl .Param ())
1530+ for _ , param := range params {
1531+ if ! requireCheckFieldKind (fl , param , true ) {
1532+ return true
1533+ }
1534+ }
1535+ return ! hasValue (fl )
1536+ }
1537+
14191538// RequiredWithoutAll is the validation function
14201539// The field under validation must be present and not empty only when all of the other specified fields are not present.
14211540func requiredWithoutAll (fl FieldLevel ) bool {
@@ -1541,7 +1660,7 @@ func isGte(fl FieldLevel) bool {
15411660 return int64 (field .Len ()) >= p
15421661
15431662 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1544- p := asInt ( param )
1663+ p := asIntFromType ( field . Type (), param )
15451664
15461665 return field .Int () >= p
15471666
@@ -1588,7 +1707,7 @@ func isGt(fl FieldLevel) bool {
15881707 return int64 (field .Len ()) > p
15891708
15901709 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1591- p := asInt ( param )
1710+ p := asIntFromType ( field . Type (), param )
15921711
15931712 return field .Int () > p
15941713
@@ -1631,7 +1750,7 @@ func hasLengthOf(fl FieldLevel) bool {
16311750 return int64 (field .Len ()) == p
16321751
16331752 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1634- p := asInt ( param )
1753+ p := asIntFromType ( field . Type (), param )
16351754
16361755 return field .Int () == p
16371756
@@ -1767,7 +1886,7 @@ func isLte(fl FieldLevel) bool {
17671886 return int64 (field .Len ()) <= p
17681887
17691888 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1770- p := asInt ( param )
1889+ p := asIntFromType ( field . Type (), param )
17711890
17721891 return field .Int () <= p
17731892
@@ -1814,7 +1933,7 @@ func isLt(fl FieldLevel) bool {
18141933 return int64 (field .Len ()) < p
18151934
18161935 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
1817- p := asInt ( param )
1936+ p := asIntFromType ( field . Type (), param )
18181937
18191938 return field .Int () < p
18201939
@@ -2087,6 +2206,29 @@ func isDatetime(fl FieldLevel) bool {
20872206
20882207 if field .Kind () == reflect .String {
20892208 _ , err := time .Parse (param , field .String ())
2209+
2210+ return err == nil
2211+ }
2212+
2213+ panic (fmt .Sprintf ("Bad field type %T" , field .Interface ()))
2214+ }
2215+
2216+ // isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
2217+ func isTimeZone (fl FieldLevel ) bool {
2218+ field := fl .Field ()
2219+
2220+ if field .Kind () == reflect .String {
2221+ // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
2222+ if field .String () == "" {
2223+ return false
2224+ }
2225+
2226+ // Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
2227+ if strings .ToLower (field .String ()) == "local" {
2228+ return false
2229+ }
2230+
2231+ _ , err := time .LoadLocation (field .String ())
20902232 if err != nil {
20912233 return false
20922234 }
0 commit comments