Skip to content

Commit 6963d3c

Browse files
author
Dean Karn
authored
Merge pull request #544 from go-playground/backport-required-with-fixes
fix required_*
2 parents cd1bd58 + 38bfb46 commit 6963d3c

File tree

5 files changed

+129
-47
lines changed

5 files changed

+129
-47
lines changed

baked_in.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,21 +1316,24 @@ func hasValue(fl FieldLevel) bool {
13161316
// requireCheckField is a func for check field kind
13171317
func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
13181318
field := fl.Field()
1319-
var ok bool
13201319
kind := field.Kind()
1320+
var nullable, found bool
13211321
if len(param) > 0 {
1322-
field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param)
1323-
if !ok {
1322+
field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
1323+
if !found {
13241324
return defaultNotFoundValue
13251325
}
13261326
}
13271327
switch kind {
13281328
case reflect.Invalid:
13291329
return defaultNotFoundValue
13301330
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1331-
return !field.IsNil()
1331+
return field.IsNil()
13321332
default:
1333-
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
1333+
if nullable && field.Interface() != nil {
1334+
return false
1335+
}
1336+
return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface()
13341337
}
13351338
}
13361339

@@ -1339,7 +1342,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
13391342
func requiredWith(fl FieldLevel) bool {
13401343
params := parseOneOfParam2(fl.Param())
13411344
for _, param := range params {
1342-
if requireCheckFieldKind(fl, param, false) {
1345+
if !requireCheckFieldKind(fl, param, true) {
13431346
return hasValue(fl)
13441347
}
13451348
}
@@ -1351,7 +1354,7 @@ func requiredWith(fl FieldLevel) bool {
13511354
func requiredWithAll(fl FieldLevel) bool {
13521355
params := parseOneOfParam2(fl.Param())
13531356
for _, param := range params {
1354-
if !requireCheckFieldKind(fl, param, false) {
1357+
if requireCheckFieldKind(fl, param, true) {
13551358
return true
13561359
}
13571360
}
@@ -1361,11 +1364,8 @@ func requiredWithAll(fl FieldLevel) bool {
13611364
// RequiredWithout is the validation function
13621365
// The field under validation must be present and not empty only when any of the other specified fields are not present.
13631366
func requiredWithout(fl FieldLevel) bool {
1364-
params := parseOneOfParam2(fl.Param())
1365-
for _, param := range params {
1366-
if !requireCheckFieldKind(fl, param, true) {
1367-
return hasValue(fl)
1368-
}
1367+
if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
1368+
return hasValue(fl)
13691369
}
13701370
return true
13711371
}
@@ -1375,7 +1375,7 @@ func requiredWithout(fl FieldLevel) bool {
13751375
func requiredWithoutAll(fl FieldLevel) bool {
13761376
params := parseOneOfParam2(fl.Param())
13771377
for _, param := range params {
1378-
if requireCheckFieldKind(fl, param, true) {
1378+
if !requireCheckFieldKind(fl, param, true) {
13791379
return true
13801380
}
13811381
}

field_level.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,27 @@ type FieldLevel interface {
3737
//
3838
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
3939
// could not be retrieved because it didn't exist.
40+
//
41+
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
4042
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
4143

4244
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
4345
// the field and namespace allowing more extensibility for validators.
46+
//
47+
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
4448
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
49+
50+
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
51+
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
52+
// the field at all.
53+
//
54+
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
55+
// could not be retrieved because it didn't exist.
56+
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
57+
58+
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
59+
// the field and namespace allowing more extensibility for validators.
60+
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
4561
}
4662

4763
var _ FieldLevel = new(validate)
@@ -52,7 +68,7 @@ func (v *validate) Field() reflect.Value {
5268
}
5369

5470
// FieldName returns the field's name with the tag
55-
// name takeing precedence over the fields actual name.
71+
// name taking precedence over the fields actual name.
5672
func (v *validate) FieldName() string {
5773
return v.cf.altName
5874
}
@@ -68,12 +84,29 @@ func (v *validate) Param() string {
6884
}
6985

7086
// GetStructFieldOK returns Param returns param for validation against current field
87+
//
88+
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
7189
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
72-
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
90+
current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
91+
return current, kind, found
7392
}
7493

7594
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
7695
// the field and namespace allowing more extensibility for validators.
96+
//
97+
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
7798
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
99+
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
100+
return current, kind, found
101+
}
102+
103+
// GetStructFieldOK returns Param returns param for validation against current field
104+
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
105+
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
106+
}
107+
108+
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
109+
// the field and namespace allowing more extensibility for validators.
110+
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
78111
return v.getStructFieldOKInternal(val, namespace)
79112
}

util.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,10 @@ BEGIN:
5757
//
5858
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
5959
// could not be retrieved because it didn't exist.
60-
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) {
60+
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
6161

6262
BEGIN:
63-
current, kind, _ = v.ExtractType(val)
64-
63+
current, kind, nullable = v.ExtractType(val)
6564
if kind == reflect.Invalid {
6665
return
6766
}
@@ -112,7 +111,7 @@ BEGIN:
112111
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
113112

114113
if arrIdx >= current.Len() {
115-
return current, kind, false
114+
return
116115
}
117116

118117
startIdx := idx2 + 1

validator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strconv"
88
)
99

10-
// per validate contruct
10+
// per validate construct
1111
type validate struct {
1212
v *Validate
1313
top reflect.Value

validator_test.go

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,125 +1640,125 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
16401640
v: vd,
16411641
}
16421642

1643-
current, kind, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
1643+
current, kind, _, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
16441644
Equal(t, ok, true)
16451645
Equal(t, kind, reflect.Struct)
16461646
tm, ok := current.Interface().(time.Time)
16471647
Equal(t, ok, true)
16481648
Equal(t, tm, now)
16491649

1650-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
1650+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
16511651
Equal(t, ok, true)
16521652
Equal(t, kind, reflect.String)
16531653
Equal(t, current.String(), "val2")
16541654

1655-
current, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
1655+
current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
16561656
Equal(t, ok, false)
16571657

1658-
current, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
1658+
current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
16591659
Equal(t, ok, false)
16601660

1661-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
1661+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
16621662
Equal(t, ok, true)
16631663
Equal(t, kind, reflect.String)
16641664
Equal(t, current.String(), "val3")
16651665

1666-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
1666+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
16671667
Equal(t, ok, true)
16681668
Equal(t, kind, reflect.String)
16691669
Equal(t, current.String(), "val2")
16701670

1671-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
1671+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
16721672
Equal(t, ok, true)
16731673
Equal(t, kind, reflect.String)
16741674
Equal(t, current.String(), "name2")
16751675

1676-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
1676+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
16771677
Equal(t, ok, true)
16781678
Equal(t, kind, reflect.String)
16791679
Equal(t, current.String(), "name3")
16801680

1681-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
1681+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
16821682
Equal(t, ok, true)
16831683
Equal(t, kind, reflect.String)
16841684
Equal(t, current.String(), "7")
16851685

1686-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
1686+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
16871687
Equal(t, ok, true)
16881688
Equal(t, kind, reflect.String)
16891689
Equal(t, current.String(), "name8")
16901690

1691-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
1691+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
16921692
Equal(t, ok, true)
16931693
Equal(t, kind, reflect.String)
16941694
Equal(t, current.String(), "val5")
16951695

1696-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
1696+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
16971697
Equal(t, ok, true)
16981698
Equal(t, kind, reflect.String)
16991699
Equal(t, current.String(), "9")
17001700

1701-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
1701+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
17021702
Equal(t, ok, true)
17031703
Equal(t, kind, reflect.String)
17041704
Equal(t, current.String(), "val2")
17051705

1706-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
1706+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
17071707
Equal(t, ok, true)
17081708
Equal(t, kind, reflect.String)
17091709
Equal(t, current.String(), "val2")
17101710

1711-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
1711+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
17121712
Equal(t, ok, true)
17131713
Equal(t, kind, reflect.String)
17141714
Equal(t, current.String(), "val2")
17151715

1716-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
1716+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
17171717
Equal(t, ok, true)
17181718
Equal(t, kind, reflect.String)
17191719
Equal(t, current.String(), "val2")
17201720

1721-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
1721+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
17221722
Equal(t, ok, true)
17231723
Equal(t, kind, reflect.String)
17241724
Equal(t, current.String(), "val2")
17251725

1726-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
1726+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
17271727
Equal(t, ok, true)
17281728
Equal(t, kind, reflect.String)
17291729
Equal(t, current.String(), "val2")
17301730

1731-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
1731+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
17321732
Equal(t, ok, true)
17331733
Equal(t, kind, reflect.String)
17341734
Equal(t, current.String(), "val2")
17351735

1736-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
1736+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
17371737
Equal(t, ok, true)
17381738
Equal(t, kind, reflect.String)
17391739
Equal(t, current.String(), "val2")
17401740

1741-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
1741+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
17421742
Equal(t, ok, true)
17431743
Equal(t, kind, reflect.String)
17441744
Equal(t, current.String(), "val2")
17451745

1746-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
1746+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
17471747
Equal(t, ok, true)
17481748
Equal(t, kind, reflect.String)
17491749
Equal(t, current.String(), "val2")
17501750

1751-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
1751+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
17521752
Equal(t, ok, true)
17531753
Equal(t, kind, reflect.String)
17541754
Equal(t, current.String(), "val3")
17551755

1756-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
1756+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
17571757
Equal(t, ok, true)
17581758
Equal(t, kind, reflect.String)
17591759
Equal(t, current.String(), "val2")
17601760

1761-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
1761+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
17621762
Equal(t, ok, true)
17631763
Equal(t, kind, reflect.String)
17641764
Equal(t, current.String(), "val1")
@@ -1784,13 +1784,13 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
17841784

17851785
val = reflect.ValueOf(test)
17861786

1787-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
1787+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
17881788
Equal(t, ok, true)
17891789
Equal(t, kind, reflect.Ptr)
17901790
Equal(t, current.String(), "<*validator.SliceStruct Value>")
17911791
Equal(t, current.IsNil(), true)
17921792

1793-
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
1793+
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
17941794
Equal(t, ok, false)
17951795
Equal(t, kind, reflect.Ptr)
17961796
Equal(t, current.String(), "<*validator.SliceStruct Value>")
@@ -8864,3 +8864,53 @@ func TestAbilityToValidateNils(t *testing.T) {
88648864
errs = val.Struct(ts)
88658865
NotEqual(t, errs, nil)
88668866
}
8867+
8868+
func TestRequiredWithoutPointers(t *testing.T) {
8869+
type Lookup struct {
8870+
FieldA *bool `json:"fieldA,omitempty" validate:"required_without=FieldB"`
8871+
FieldB *bool `json:"fieldB,omitempty" validate:"required_without=FieldA"`
8872+
}
8873+
8874+
b := true
8875+
lookup := Lookup{
8876+
FieldA: &b,
8877+
FieldB: nil,
8878+
}
8879+
8880+
val := New()
8881+
errs := val.Struct(lookup)
8882+
Equal(t, errs, nil)
8883+
8884+
b = false
8885+
lookup = Lookup{
8886+
FieldA: &b,
8887+
FieldB: nil,
8888+
}
8889+
errs = val.Struct(lookup)
8890+
Equal(t, errs, nil)
8891+
}
8892+
8893+
func TestRequiredWithoutAllPointers(t *testing.T) {
8894+
type Lookup struct {
8895+
FieldA *bool `json:"fieldA,omitempty" validate:"required_without_all=FieldB"`
8896+
FieldB *bool `json:"fieldB,omitempty" validate:"required_without_all=FieldA"`
8897+
}
8898+
8899+
b := true
8900+
lookup := Lookup{
8901+
FieldA: &b,
8902+
FieldB: nil,
8903+
}
8904+
8905+
val := New()
8906+
errs := val.Struct(lookup)
8907+
Equal(t, errs, nil)
8908+
8909+
b = false
8910+
lookup = Lookup{
8911+
FieldA: &b,
8912+
FieldB: nil,
8913+
}
8914+
errs = val.Struct(lookup)
8915+
Equal(t, errs, nil)
8916+
}

0 commit comments

Comments
 (0)