Skip to content

Commit dcb76a8

Browse files
authored
Merge branch 'go-playground:master' into feature/omitzero
2 parents 835464d + 6c3307e commit dcb76a8

File tree

5 files changed

+171
-4
lines changed

5 files changed

+171
-4
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package validator
22
=================
33
<img align="right" src="logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4-
![Project status](https://img.shields.io/badge/version-10.22.0-green.svg)
4+
![Project status](https://img.shields.io/badge/version-10.23.0-green.svg)
55
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
66
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
77
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@@ -22,6 +22,11 @@ It has the following **unique** features:
2222
- Customizable i18n aware error messages.
2323
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
2424

25+
A Call for Maintainers
26+
----------------------
27+
28+
Please read the discussiong started [here](https://github.com/go-playground/validator/discussions/1330) if you are interested in contributing/helping maintain this package.
29+
2530
Installation
2631
------------
2732

baked_in.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ var (
206206
"fqdn": isFQDN,
207207
"unique": isUnique,
208208
"oneof": isOneOf,
209+
"oneofci": isOneOfCI,
209210
"html": isHTML,
210211
"html_encoded": isHTMLEncoded,
211212
"url_encoded": isURLEncoded,
@@ -214,6 +215,7 @@ var (
214215
"json": isJSON,
215216
"jwt": isJWT,
216217
"hostname_port": isHostnamePort,
218+
"port": isPort,
217219
"lowercase": isLowercase,
218220
"uppercase": isUppercase,
219221
"datetime": isDatetime,
@@ -300,6 +302,23 @@ func isOneOf(fl FieldLevel) bool {
300302
return false
301303
}
302304

305+
// isOneOfCI is the validation function for validating if the current field's value is one of the provided string values (case insensitive).
306+
func isOneOfCI(fl FieldLevel) bool {
307+
vals := parseOneOfParam2(fl.Param())
308+
field := fl.Field()
309+
310+
if field.Kind() != reflect.String {
311+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
312+
}
313+
v := field.String()
314+
for _, val := range vals {
315+
if strings.EqualFold(val, v) {
316+
return true
317+
}
318+
}
319+
return false
320+
}
321+
303322
// isUnique is the validation function for validating if each array|slice|map value is unique
304323
func isUnique(fl FieldLevel) bool {
305324
field := fl.Field()
@@ -1843,7 +1862,14 @@ func requireCheckFieldValue(
18431862
return int64(field.Len()) == asInt(value)
18441863

18451864
case reflect.Bool:
1846-
return field.Bool() == asBool(value)
1865+
return field.Bool() == (value == "true")
1866+
1867+
case reflect.Ptr:
1868+
if field.IsNil() {
1869+
return value == "nil"
1870+
}
1871+
// Handle non-nil pointers
1872+
return requireCheckFieldValue(fl, param, value, defaultNotFoundValue)
18471873
}
18481874

18491875
// default reflect.String:
@@ -2719,6 +2745,13 @@ func isHostnamePort(fl FieldLevel) bool {
27192745
return true
27202746
}
27212747

2748+
// IsPort validates if the current field's value represents a valid port
2749+
func isPort(fl FieldLevel) bool {
2750+
val := fl.Field().Uint()
2751+
2752+
return val >= 1 && val <= 65535
2753+
}
2754+
27222755
// isLowercase is the validation function for validating if the current field's value is a lowercase string.
27232756
func isLowercase(fl FieldLevel) bool {
27242757
field := fl.Field()

doc.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,12 +489,19 @@ For strings, ints, and uints, oneof will ensure that the value
489489
is one of the values in the parameter. The parameter should be
490490
a list of values separated by whitespace. Values may be
491491
strings or numbers. To match strings with spaces in them, include
492-
the target string between single quotes.
492+
the target string between single quotes. Kind of like an 'enum'.
493493
494494
Usage: oneof=red green
495495
oneof='red green' 'blue yellow'
496496
oneof=5 7 9
497497
498+
# One Of Case Insensitive
499+
500+
Works the same as oneof but is case insensitive and therefore only accepts strings.
501+
502+
Usage: oneofci=red green
503+
oneofci='red green' 'blue yellow'
504+
498505
# Greater Than
499506
500507
For numbers, this will ensure that the value is greater than the

regexes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const (
7373
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
7474
mongodbIdRegexString = "^[a-f\\d]{24}$"
7575
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
76-
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
76+
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|((\*|\d+)(\/|-)\d+)|\d+|\*) ?){5,7})`
7777
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
7878
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
7979
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"

validator_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5680,6 +5680,82 @@ func TestOneOfValidation(t *testing.T) {
56805680
}, "Bad field type float64")
56815681
}
56825682

5683+
func TestOneOfCIValidation(t *testing.T) {
5684+
validate := New()
5685+
5686+
passSpecs := []struct {
5687+
f interface{}
5688+
t string
5689+
}{
5690+
{f: "red", t: "oneofci=RED GREEN"},
5691+
{f: "RED", t: "oneofci=red green"},
5692+
{f: "red", t: "oneofci=red green"},
5693+
{f: "RED", t: "oneofci=RED GREEN"},
5694+
{f: "green", t: "oneofci=red green"},
5695+
{f: "red green", t: "oneofci='red green' blue"},
5696+
{f: "blue", t: "oneofci='red green' blue"},
5697+
{f: "GREEN", t: "oneofci=Red Green"},
5698+
{f: "ReD", t: "oneofci=RED GREEN"},
5699+
{f: "gReEn", t: "oneofci=rEd GrEeN"},
5700+
{f: "RED GREEN", t: "oneofci='red green' blue"},
5701+
{f: "red Green", t: "oneofci='RED GREEN' Blue"},
5702+
{f: "Red green", t: "oneofci='Red Green' BLUE"},
5703+
{f: "rEd GrEeN", t: "oneofci='ReD gReEn' BlUe"},
5704+
{f: "BLUE", t: "oneofci='Red Green' BLUE"},
5705+
{f: "BlUe", t: "oneofci='RED GREEN' Blue"},
5706+
{f: "bLuE", t: "oneofci='red green' BLUE"},
5707+
}
5708+
5709+
for _, spec := range passSpecs {
5710+
t.Logf("%#v", spec)
5711+
errs := validate.Var(spec.f, spec.t)
5712+
Equal(t, errs, nil)
5713+
}
5714+
5715+
failSpecs := []struct {
5716+
f interface{}
5717+
t string
5718+
}{
5719+
{f: "", t: "oneofci=red green"},
5720+
{f: "yellow", t: "oneofci=red green"},
5721+
{f: "green", t: "oneofci='red green' blue"},
5722+
}
5723+
5724+
for _, spec := range failSpecs {
5725+
t.Logf("%#v", spec)
5726+
errs := validate.Var(spec.f, spec.t)
5727+
AssertError(t, errs, "", "", "", "", "oneofci")
5728+
}
5729+
5730+
panicSpecs := []struct {
5731+
f interface{}
5732+
t string
5733+
}{
5734+
{f: 3.14, t: "oneofci=red green"},
5735+
{f: 5, t: "oneofci=red green"},
5736+
{f: uint(6), t: "oneofci=7"},
5737+
{f: int8(5), t: "oneofci=red green"},
5738+
{f: int16(5), t: "oneofci=red green"},
5739+
{f: int32(5), t: "oneofci=red green"},
5740+
{f: int64(5), t: "oneofci=red green"},
5741+
{f: uint(5), t: "oneofci=red green"},
5742+
{f: uint8(5), t: "oneofci=red green"},
5743+
{f: uint16(5), t: "oneofci=red green"},
5744+
{f: uint32(5), t: "oneofci=red green"},
5745+
{f: uint64(5), t: "oneofci=red green"},
5746+
}
5747+
5748+
panicCount := 0
5749+
for _, spec := range panicSpecs {
5750+
t.Logf("%#v", spec)
5751+
PanicMatches(t, func() {
5752+
_ = validate.Var(spec.f, spec.t)
5753+
}, fmt.Sprintf("Bad field type %T", spec.f))
5754+
panicCount++
5755+
}
5756+
Equal(t, panicCount, len(panicSpecs))
5757+
}
5758+
56835759
func TestBase32Validation(t *testing.T) {
56845760
validate := New()
56855761

@@ -12002,6 +12078,25 @@ func TestExcludedIf(t *testing.T) {
1200212078
errs = validate.Struct(test10)
1200312079
Equal(t, errs, nil)
1200412080

12081+
test11 := struct {
12082+
Field1 bool
12083+
Field2 *string `validate:"excluded_if=Field1 false"`
12084+
}{
12085+
Field1: false,
12086+
Field2: nil,
12087+
}
12088+
errs = validate.Struct(test11)
12089+
Equal(t, errs, nil)
12090+
12091+
test12 := struct {
12092+
Field1 bool
12093+
Field2 *string `validate:"excluded_if=Field1 !Field1"`
12094+
}{
12095+
Field1: true,
12096+
Field2: nil,
12097+
}
12098+
errs = validate.Struct(test12)
12099+
Equal(t, errs, nil)
1200512100
// Checks number of params in struct tag is correct
1200612101
defer func() {
1200712102
if r := recover(); r == nil {
@@ -12377,6 +12472,32 @@ func Test_hostnameport_validator(t *testing.T) {
1237712472
}
1237812473
}
1237912474

12475+
func Test_port_validator(t *testing.T) {
12476+
type Host struct {
12477+
Port uint32 `validate:"port"`
12478+
}
12479+
12480+
type testInput struct {
12481+
data uint32
12482+
expected bool
12483+
}
12484+
testData := []testInput{
12485+
{0, false},
12486+
{1, true},
12487+
{65535, true},
12488+
{65536, false},
12489+
{65538, false},
12490+
}
12491+
for _, td := range testData {
12492+
h := Host{Port: td.data}
12493+
v := New()
12494+
err := v.Struct(h)
12495+
if td.expected != (err == nil) {
12496+
t.Fatalf("Test failed for data: %v Error: %v", td.data, err)
12497+
}
12498+
}
12499+
}
12500+
1238012501
func TestLowercaseValidation(t *testing.T) {
1238112502
tests := []struct {
1238212503
param string
@@ -13637,6 +13758,7 @@ func TestCronExpressionValidation(t *testing.T) {
1363713758
{"*/20 * * * *", "cron", true},
1363813759
{"0 15 10 ? * MON-FRI", "cron", true},
1363913760
{"0 15 10 ? * 6#3", "cron", true},
13761+
{"0 */15 * * *", "cron", true},
1364013762
{"wrong", "cron", false},
1364113763
}
1364213764

0 commit comments

Comments
 (0)