Skip to content

Commit 8154ee6

Browse files
author
Dean Karn
authored
Merge branch 'master' into master
2 parents 07a4b7a + f0805c6 commit 8154ee6

File tree

10 files changed

+2114
-147
lines changed

10 files changed

+2114
-147
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# Folders
77
_obj
88
_test
9+
bin
910

1011
# Architecture specific extensions/prefixes
1112
*.[568vq]
@@ -26,4 +27,4 @@ _testmain.go
2627
*.out
2728
*.txt
2829
cover.html
29-
README.html
30+
README.html

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: go
22
go:
3-
- 1.13.7
3+
- 1.15.2
44
- tip
55
matrix:
66
allow_failures:
@@ -25,5 +25,5 @@ script:
2525
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
2626

2727
after_success: |
28-
[ $TRAVIS_GO_VERSION = 1.13.7 ] &&
29-
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
28+
[ $TRAVIS_GO_VERSION = 1.15.2 ] &&
29+
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Baked-in Validations
129129
| contains | Contains |
130130
| containsany | Contains Any |
131131
| containsrune | Contains Rune |
132+
| endswith | Ends With |
132133
| lowercase | Lowercase |
133134
| multibyte | Multi-Byte Characters |
134135
| number | NOT DOCUMENTED IN doc.go |
@@ -197,10 +198,16 @@ Baked-in Validations
197198
| min | Minimum |
198199
| oneof | One Of |
199200
| required | Required |
201+
| required_if | Required If |
202+
| required_unless | Required Unless |
200203
| required_with | Required With |
201204
| required_with_all | Required With All |
202205
| required_without | Required Without |
203206
| required_without_all | Required Without All |
207+
| excluded_with | Excluded With |
208+
| excluded_with_all | Excluded With All |
209+
| excluded_without | Excluded Without |
210+
| excluded_without_all | Excluded Without All |
204211
| unique | Unique |
205212

206213
Benchmarks

baked_in.go

Lines changed: 153 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
13881474
func 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.
14001498
func 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.
14121519
func 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.
14211540
func 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

Comments
 (0)