each.go raw
1 // Copyright 2016 Qiang Xue. All rights reserved.
2 // Use of this source code is governed by a MIT-style
3 // license that can be found in the LICENSE file.
4
5 package validation
6
7 import (
8 "context"
9 "errors"
10 "reflect"
11 "strconv"
12 )
13
14 // Each returns a validation rule that loops through an iterable (map, slice or array)
15 // and validates each value inside with the provided rules.
16 // An empty iterable is considered valid. Use the Required rule to make sure the iterable is not empty.
17 func Each(rules ...Rule) EachRule {
18 return EachRule{
19 rules: rules,
20 }
21 }
22
23 // EachRule is a validation rule that validates elements in a map/slice/array using the specified list of rules.
24 type EachRule struct {
25 rules []Rule
26 }
27
28 // Validate loops through the given iterable and calls the Ozzo Validate() method for each value.
29 func (r EachRule) Validate(value interface{}) error {
30 return r.ValidateWithContext(nil, value)
31 }
32
33 // ValidateWithContext loops through the given iterable and calls the Ozzo ValidateWithContext() method for each value.
34 func (r EachRule) ValidateWithContext(ctx context.Context, value interface{}) error {
35 errs := Errors{}
36
37 v := reflect.ValueOf(value)
38 switch v.Kind() {
39 case reflect.Map:
40 for _, k := range v.MapKeys() {
41 val := r.getInterface(v.MapIndex(k))
42 var err error
43 if ctx == nil {
44 err = Validate(val, r.rules...)
45 } else {
46 err = ValidateWithContext(ctx, val, r.rules...)
47 }
48 if err != nil {
49 errs[r.getString(k)] = err
50 }
51 }
52 case reflect.Slice, reflect.Array:
53 for i := 0; i < v.Len(); i++ {
54 val := r.getInterface(v.Index(i))
55 var err error
56 if ctx == nil {
57 err = Validate(val, r.rules...)
58 } else {
59 err = ValidateWithContext(ctx, val, r.rules...)
60 }
61 if err != nil {
62 errs[strconv.Itoa(i)] = err
63 }
64 }
65 default:
66 return errors.New("must be an iterable (map, slice or array)")
67 }
68
69 if len(errs) > 0 {
70 return errs
71 }
72 return nil
73 }
74
75 func (r EachRule) getInterface(value reflect.Value) interface{} {
76 switch value.Kind() {
77 case reflect.Ptr, reflect.Interface:
78 if value.IsNil() {
79 return nil
80 }
81 return value.Elem().Interface()
82 default:
83 return value.Interface()
84 }
85 }
86
87 func (r EachRule) getString(value reflect.Value) string {
88 switch value.Kind() {
89 case reflect.Ptr, reflect.Interface:
90 if value.IsNil() {
91 return ""
92 }
93 return value.Elem().String()
94 default:
95 return value.String()
96 }
97 }
98