Skip to content

Commit 5ca3cff

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #91 from bluesuncorp/v5
merge latest changes from v5
2 parents 7ce6ffc + d8e0c4d commit 5ca3cff

File tree

3 files changed

+628
-32
lines changed

3 files changed

+628
-32
lines changed

doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ Here is a list of the current built in validators:
176176
dive
177177
This tells the validator to dive into a slice, array or map and validate that
178178
level of the slice, array or map with the validation tags that follow.
179-
Multidimensional nesting is also supported, each level you with to dive will
179+
Multidimensional nesting is also supported, each level you wish to dive will
180180
require another dive tag. (Usage: dive)
181181
Example: [][]string with validation tag "gt=0,dive,len=1,dive,required"
182182
gt=0 will be applied to []

validator.go

Lines changed: 224 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,19 @@ import (
2020
)
2121

2222
const (
23-
utf8HexComma = "0x2C"
24-
tagSeparator = ","
25-
orSeparator = "|"
26-
noValidationTag = "-"
27-
tagKeySeparator = "="
28-
structOnlyTag = "structonly"
29-
omitempty = "omitempty"
30-
required = "required"
31-
fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
32-
sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
33-
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
34-
structErrMsg = "Struct:%s\n"
35-
diveTag = "dive"
36-
// diveSplit = "," + diveTag
23+
utf8HexComma = "0x2C"
24+
tagSeparator = ","
25+
orSeparator = "|"
26+
noValidationTag = "-"
27+
tagKeySeparator = "="
28+
structOnlyTag = "structonly"
29+
omitempty = "omitempty"
30+
required = "required"
31+
fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
32+
sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
33+
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
34+
structErrMsg = "Struct:%s\n"
35+
diveTag = "dive"
3736
arrayIndexFieldName = "%s[%d]"
3837
mapIndexFieldName = "%s[%v]"
3938
)
@@ -193,6 +192,82 @@ func (e *FieldError) Error() string {
193192
return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
194193
}
195194

195+
// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name
196+
// for those that want/need it.
197+
// This is now needed because of the new dive functionality
198+
func (e *FieldError) Flatten() map[string]*FieldError {
199+
200+
errs := map[string]*FieldError{}
201+
202+
if e.IsPlaceholderErr {
203+
204+
if e.IsSliceOrArray {
205+
for key, err := range e.SliceOrArrayErrs {
206+
207+
fe, ok := err.(*FieldError)
208+
209+
if ok {
210+
211+
if flat := fe.Flatten(); flat != nil && len(flat) > 0 {
212+
for k, v := range flat {
213+
if fe.IsPlaceholderErr {
214+
errs[fmt.Sprintf("[%#v]%s", key, k)] = v
215+
} else {
216+
errs[fmt.Sprintf("[%#v]", key)] = v
217+
}
218+
219+
}
220+
}
221+
} else {
222+
223+
se := err.(*StructErrors)
224+
225+
if flat := se.Flatten(); flat != nil && len(flat) > 0 {
226+
for k, v := range flat {
227+
errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
228+
}
229+
}
230+
}
231+
}
232+
}
233+
234+
if e.IsMap {
235+
for key, err := range e.MapErrs {
236+
237+
fe, ok := err.(*FieldError)
238+
239+
if ok {
240+
241+
if flat := fe.Flatten(); flat != nil && len(flat) > 0 {
242+
for k, v := range flat {
243+
if fe.IsPlaceholderErr {
244+
errs[fmt.Sprintf("[%#v]%s", key, k)] = v
245+
} else {
246+
errs[fmt.Sprintf("[%#v]", key)] = v
247+
}
248+
}
249+
}
250+
} else {
251+
252+
se := err.(*StructErrors)
253+
254+
if flat := se.Flatten(); flat != nil && len(flat) > 0 {
255+
for k, v := range flat {
256+
errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
257+
}
258+
}
259+
}
260+
}
261+
}
262+
263+
return errs
264+
}
265+
266+
errs[e.Field] = e
267+
268+
return errs
269+
}
270+
196271
// StructErrors is hierarchical list of field and struct validation errors
197272
// for a non hierarchical representation please see the Flatten method for StructErrors
198273
type StructErrors struct {
@@ -234,7 +309,17 @@ func (e *StructErrors) Flatten() map[string]*FieldError {
234309

235310
for _, f := range e.Errors {
236311

237-
errs[f.Field] = f
312+
if flat := f.Flatten(); flat != nil && len(flat) > 0 {
313+
314+
for k, fe := range flat {
315+
316+
if f.IsPlaceholderErr {
317+
errs[f.Field+k] = fe
318+
} else {
319+
errs[k] = fe
320+
}
321+
}
322+
}
238323
}
239324

240325
for key, val := range e.StructErrors {
@@ -353,7 +438,6 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
353438
structName = structType.Name()
354439
numFields = structValue.NumField()
355440
cs = &cachedStruct{name: structName, children: numFields}
356-
structCache.Set(structType, cs)
357441
}
358442

359443
validationErrors := structPool.Borrow()
@@ -429,24 +513,62 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
429513
continue
430514
}
431515

432-
if valueField.Kind() == reflect.Ptr && valueField.IsNil() {
516+
if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
433517

434518
if strings.Contains(cField.tag, omitempty) {
435-
continue
519+
goto CACHEFIELD
436520
}
437521

438-
if strings.Contains(cField.tag, required) {
522+
tags := strings.Split(cField.tag, tagSeparator)
523+
524+
if len(tags) > 0 {
525+
526+
var param string
527+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
528+
529+
if len(vals) > 1 {
530+
param = vals[1]
531+
}
439532

440533
validationErrors.Errors[cField.name] = &FieldError{
441534
Field: cField.name,
442-
Tag: required,
535+
Tag: vals[0],
536+
Param: param,
443537
Value: valueField.Interface(),
538+
Kind: valueField.Kind(),
539+
Type: valueField.Type(),
444540
}
445541

446-
continue
542+
goto CACHEFIELD
543+
}
544+
}
545+
546+
// if we get here, the field is interface and could be a struct or a field
547+
// and we need to check the inner type and validate
548+
if cField.kind == reflect.Interface {
549+
550+
valueField = valueField.Elem()
551+
552+
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
553+
valueField = valueField.Elem()
447554
}
555+
556+
if valueField.Kind() == reflect.Struct {
557+
goto VALIDATESTRUCT
558+
}
559+
560+
// sending nil for cField as it was type interface and could be anything
561+
// each time and so must be calculated each time and can't be cached reliably
562+
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
563+
validationErrors.Errors[fieldError.Field] = fieldError
564+
// free up memory reference
565+
fieldError = nil
566+
}
567+
568+
goto CACHEFIELD
448569
}
449570

571+
VALIDATESTRUCT:
450572
if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
451573
validationErrors.StructErrors[cField.name] = structErrors
452574
// free up memory map no longer needed
@@ -486,11 +608,14 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
486608
}
487609
}
488610

611+
CACHEFIELD:
489612
if !isCached {
490613
cs.fields = append(cs.fields, cField)
491614
}
492615
}
493616

617+
structCache.Set(structType, cs)
618+
494619
if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
495620
structPool.Return(validationErrors)
496621
return nil
@@ -709,26 +834,60 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
709834
continue
710835
}
711836

712-
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
837+
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
713838

714-
if strings.Contains(cField.tag, omitempty) {
839+
if strings.Contains(cField.diveTag, omitempty) {
715840
continue
716841
}
717842

718-
if strings.Contains(cField.tag, required) {
843+
tags := strings.Split(cField.diveTag, tagSeparator)
844+
845+
if len(tags) > 0 {
846+
847+
var param string
848+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
849+
850+
if len(vals) > 1 {
851+
param = vals[1]
852+
}
719853

720854
errs[key.Interface()] = &FieldError{
721855
Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()),
722-
Tag: required,
856+
Tag: vals[0],
857+
Param: param,
723858
Value: idxField.Interface(),
724-
Kind: reflect.Ptr,
859+
Kind: idxField.Kind(),
725860
Type: cField.mapSubtype,
726861
}
727862
}
728863

729864
continue
730865
}
731866

867+
// if we get here, the field is interface and could be a struct or a field
868+
// and we need to check the inner type and validate
869+
if idxField.Kind() == reflect.Interface {
870+
871+
idxField = idxField.Elem()
872+
873+
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
874+
idxField = idxField.Elem()
875+
}
876+
877+
if idxField.Kind() == reflect.Struct {
878+
goto VALIDATESTRUCT
879+
}
880+
881+
// sending nil for cField as it was type interface and could be anything
882+
// each time and so must be calculated each time and can't be cached reliably
883+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
884+
errs[key.Interface()] = fieldError
885+
}
886+
887+
continue
888+
}
889+
890+
VALIDATESTRUCT:
732891
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
733892
errs[key.Interface()] = structErrors
734893
}
@@ -768,26 +927,60 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
768927
continue
769928
}
770929

771-
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
930+
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
772931

773-
if strings.Contains(cField.tag, omitempty) {
932+
if strings.Contains(cField.diveTag, omitempty) {
774933
continue
775934
}
776935

777-
if strings.Contains(cField.tag, required) {
936+
tags := strings.Split(cField.diveTag, tagSeparator)
937+
938+
if len(tags) > 0 {
939+
940+
var param string
941+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
942+
943+
if len(vals) > 1 {
944+
param = vals[1]
945+
}
778946

779947
errs[i] = &FieldError{
780948
Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i),
781-
Tag: required,
949+
Tag: vals[0],
950+
Param: param,
782951
Value: idxField.Interface(),
783-
Kind: reflect.Ptr,
952+
Kind: idxField.Kind(),
784953
Type: cField.sliceSubtype,
785954
}
786955
}
787956

788957
continue
789958
}
790959

960+
// if we get here, the field is interface and could be a struct or a field
961+
// and we need to check the inner type and validate
962+
if idxField.Kind() == reflect.Interface {
963+
964+
idxField = idxField.Elem()
965+
966+
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
967+
idxField = idxField.Elem()
968+
}
969+
970+
if idxField.Kind() == reflect.Struct {
971+
goto VALIDATESTRUCT
972+
}
973+
974+
// sending nil for cField as it was type interface and could be anything
975+
// each time and so must be calculated each time and can't be cached reliably
976+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
977+
errs[i] = fieldError
978+
}
979+
980+
continue
981+
}
982+
983+
VALIDATESTRUCT:
791984
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
792985
errs[i] = structErrors
793986
}

0 commit comments

Comments
 (0)