Skip to content

Commit cc25246

Browse files
author
Dean Karn
committed
fix required_without_*
1 parent 51fcc30 commit cc25246

File tree

6 files changed

+164
-128
lines changed

6 files changed

+164
-128
lines changed

baked_in.go

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,103 +1301,91 @@ func isDefault(fl FieldLevel) bool {
13011301

13021302
// HasValue is the validation function for validating if the current field's value is not the default static value.
13031303
func hasValue(fl FieldLevel) bool {
1304-
return requireCheckFieldKind(fl, "")
1304+
field := fl.Field()
1305+
switch field.Kind() {
1306+
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1307+
return !field.IsNil()
1308+
default:
1309+
if fl.(*validate).fldIsPointer && field.Interface() != nil {
1310+
return true
1311+
}
1312+
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
1313+
}
13051314
}
13061315

13071316
// requireCheckField is a func for check field kind
13081317
func requireCheckFieldKind(fl FieldLevel, param string) bool {
13091318
field := fl.Field()
1319+
var ok bool
1320+
kind := field.Kind()
13101321
if len(param) > 0 {
1311-
if fl.Parent().Kind() == reflect.Ptr {
1312-
field = fl.Parent().Elem().FieldByName(param)
1313-
} else {
1314-
field = fl.Parent().FieldByName(param)
1322+
field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param)
1323+
if !ok {
1324+
return true
13151325
}
13161326
}
1317-
switch field.Kind() {
1327+
switch kind {
1328+
case reflect.Invalid:
1329+
return true
13181330
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
13191331
return !field.IsNil()
13201332
default:
1321-
if fl.(*validate).fldIsPointer && field.Interface() != nil {
1322-
return true
1323-
}
13241333
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
13251334
}
13261335
}
13271336

13281337
// RequiredWith is the validation function
13291338
// The field under validation must be present and not empty only if any of the other specified fields are present.
13301339
func requiredWith(fl FieldLevel) bool {
1331-
13321340
params := parseOneOfParam2(fl.Param())
13331341
for _, param := range params {
1334-
13351342
if requireCheckFieldKind(fl, param) {
13361343
return requireCheckFieldKind(fl, "")
13371344
}
13381345
}
1339-
13401346
return true
13411347
}
13421348

13431349
// RequiredWithAll is the validation function
13441350
// The field under validation must be present and not empty only if all of the other specified fields are present.
13451351
func requiredWithAll(fl FieldLevel) bool {
1346-
13471352
isValidateCurrentField := true
13481353
params := parseOneOfParam2(fl.Param())
13491354
for _, param := range params {
13501355

13511356
if !requireCheckFieldKind(fl, param) {
13521357
isValidateCurrentField = false
1358+
break
13531359
}
13541360
}
1355-
13561361
if isValidateCurrentField {
13571362
return requireCheckFieldKind(fl, "")
13581363
}
1359-
13601364
return true
13611365
}
13621366

13631367
// RequiredWithout is the validation function
13641368
// The field under validation must be present and not empty only when any of the other specified fields are not present.
13651369
func requiredWithout(fl FieldLevel) bool {
1366-
1367-
isValidateCurrentField := false
13681370
params := parseOneOfParam2(fl.Param())
13691371
for _, param := range params {
1370-
1371-
if requireCheckFieldKind(fl, param) {
1372-
isValidateCurrentField = true
1372+
if !requireCheckFieldKind(fl, param) {
1373+
return hasValue(fl)
13731374
}
13741375
}
1375-
1376-
if !isValidateCurrentField {
1377-
return requireCheckFieldKind(fl, "")
1378-
}
1379-
13801376
return true
13811377
}
13821378

13831379
// RequiredWithoutAll is the validation function
13841380
// The field under validation must be present and not empty only when all of the other specified fields are not present.
13851381
func requiredWithoutAll(fl FieldLevel) bool {
1386-
1387-
isValidateCurrentField := true
13881382
params := parseOneOfParam2(fl.Param())
13891383
for _, param := range params {
1390-
13911384
if requireCheckFieldKind(fl, param) {
1392-
isValidateCurrentField = false
1385+
return true
13931386
}
13941387
}
1395-
1396-
if isValidateCurrentField {
1397-
return requireCheckFieldKind(fl, "")
1398-
}
1399-
1400-
return true
1388+
return hasValue(fl)
14011389
}
14021390

14031391
// 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.

cache.go

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,19 @@ type cField struct {
8787
}
8888

8989
type cTag struct {
90-
tag string
91-
aliasTag string
92-
actualAliasTag string
93-
param string
94-
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
95-
next *cTag
96-
fn FuncCtx
97-
typeof tagType
98-
hasTag bool
99-
hasAlias bool
100-
hasParam bool // true if parameter used eg. eq= where the equal sign has been set
101-
isBlockEnd bool // indicates the current tag represents the last validation in the block
90+
tag string
91+
aliasTag string
92+
actualAliasTag string
93+
param string
94+
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
95+
next *cTag
96+
fn FuncCtx
97+
typeof tagType
98+
hasTag bool
99+
hasAlias bool
100+
hasParam bool // true if parameter used eg. eq= where the equal sign has been set
101+
isBlockEnd bool // indicates the current tag represents the last validation in the block
102+
runValidationWhenNil bool
102103
}
103104

104105
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
@@ -141,9 +142,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
141142
customName = fld.Name
142143

143144
if v.hasTagNameFunc {
144-
145145
name := v.tagNameFunc(fld)
146-
147146
if len(name) > 0 {
148147
customName = name
149148
}
@@ -168,16 +167,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
168167
namesEqual: fld.Name == customName,
169168
})
170169
}
171-
172170
v.structCache.Set(typ, cs)
173-
174171
return cs
175172
}
176173

177174
func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
178175

179176
var t string
180-
var ok bool
181177
noAlias := len(alias) == 0
182178
tags := strings.Split(tag, tagSeparator)
183179

@@ -270,11 +266,9 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
270266
continue
271267

272268
default:
273-
274269
if t == isdefault {
275270
current.typeof = typeIsDefault
276271
}
277-
278272
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
279273
orVals := strings.Split(t, orSeparator)
280274

@@ -300,7 +294,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
300294
panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
301295
}
302296

303-
if current.fn, ok = v.validations[current.tag]; !ok {
297+
if wrapper, ok := v.validations[current.tag]; ok {
298+
current.fn = wrapper.fn
299+
current.runValidationWhenNil = wrapper.runValidatinOnNil
300+
} else {
304301
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
305302
}
306303

field_level.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ type FieldLevel interface {
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.
4040
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
41+
42+
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
43+
// the field and namespace allowing more extensibility for validators.
44+
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
4145
}
4246

4347
var _ FieldLevel = new(validate)
@@ -67,3 +71,9 @@ func (v *validate) Param() string {
6771
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
6872
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
6973
}
74+
75+
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
76+
// the field and namespace allowing more extensibility for validators.
77+
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
78+
return v.getStructFieldOKInternal(val, namespace)
79+
}

validator.go

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
9494

9595
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
9696
func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
97-
9897
var typ reflect.Type
9998
var kind reflect.Kind
10099

@@ -112,16 +111,36 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
112111
}
113112

114113
if ct.hasTag {
114+
if kind == reflect.Invalid {
115+
v.str1 = string(append(ns, cf.altName...))
116+
if v.v.hasTagNameFunc {
117+
v.str2 = string(append(structNs, cf.name...))
118+
} else {
119+
v.str2 = v.str1
120+
}
121+
v.errs = append(v.errs,
122+
&fieldError{
123+
v: v.v,
124+
tag: ct.aliasTag,
125+
actualTag: ct.tag,
126+
ns: v.str1,
127+
structNs: v.str2,
128+
fieldLen: uint8(len(cf.altName)),
129+
structfieldLen: uint8(len(cf.name)),
130+
param: ct.param,
131+
kind: kind,
132+
},
133+
)
134+
return
135+
}
115136

116137
v.str1 = string(append(ns, cf.altName...))
117-
118138
if v.v.hasTagNameFunc {
119139
v.str2 = string(append(structNs, cf.name...))
120140
} else {
121141
v.str2 = v.str1
122142
}
123-
124-
if kind == reflect.Invalid {
143+
if !ct.runValidationWhenNil {
125144
v.errs = append(v.errs,
126145
&fieldError{
127146
v: v.v,
@@ -131,31 +150,14 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
131150
structNs: v.str2,
132151
fieldLen: uint8(len(cf.altName)),
133152
structfieldLen: uint8(len(cf.name)),
153+
value: current.Interface(),
134154
param: ct.param,
135155
kind: kind,
156+
typ: current.Type(),
136157
},
137158
)
138-
139159
return
140160
}
141-
142-
v.errs = append(v.errs,
143-
&fieldError{
144-
v: v.v,
145-
tag: ct.aliasTag,
146-
actualTag: ct.tag,
147-
ns: v.str1,
148-
structNs: v.str2,
149-
fieldLen: uint8(len(cf.altName)),
150-
structfieldLen: uint8(len(cf.name)),
151-
value: current.Interface(),
152-
param: ct.param,
153-
kind: kind,
154-
typ: current.Type(),
155-
},
156-
)
157-
158-
return
159161
}
160162

161163
case reflect.Struct:

0 commit comments

Comments
 (0)