forked from mccoyst/validate
-
Notifications
You must be signed in to change notification settings - Fork 2
/
v.go
136 lines (112 loc) · 2.98 KB
/
v.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// © 2013 Steve McCoy under the MIT license.
/*
Package validate provides a type for automatically validating the fields of structs.
Any fields tagged with the key "validate" will be validated via a user-defined list of functions.
For example:
type X struct {
A string `validate:"long"`
B string `validate:"short"`
C string `validate:"long,proper"`
D string
}
Multiple validators can be named in the tag by separating their names with commas.
The validators are defined in a map like so:
vd := make(validate.V)
vd["long"] = func(i interface{}) error {
…
}
vd["short"] = func(i interface{}) error {
…
}
…
When present in a field's tag, the Validate method passes to these functions the value in the field
and should return an error when the value is deemed invalid.
There is a reserved tag, "struct", which can be used to automatically validate a
struct field, either named or embedded. This may be combined with user-defined validators.
Reflection is used to access the tags and fields, so the usual caveats and limitations apply.
*/
package validate
import (
"fmt"
"reflect"
"strings"
)
type ValueValidator interface {
ValidateValue() interface{}
}
type ValueMapper interface {
MapValue() interface{}
}
type ValidatorFn func(interface{}) interface{}
// V is a map of tag names to validators.
type V map[string]ValidatorFn
// Validate accepts a struct (or a pointer) and returns a list of errors for all
// fields that are invalid. If all fields are valid, or s is not a struct type,
// Validate returns nil.
//
// Fields that are not tagged or cannot be interfaced via reflection
// are skipped.
func (v V) Validate(s interface{}) map[string]interface{} {
errors := make(map[string]interface{})
v.validate(errors, s)
if len(errors) > 0 {
return errors
}
return nil
}
func (v V) validate(errs map[string]interface{}, s interface{}) {
val := reflect.ValueOf(s)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
t := val.Type()
if t == nil || t.Kind() != reflect.Struct {
return
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fv := val.Field(i)
if !fv.CanInterface() {
continue
}
val := fv.Interface()
fieldName := f.Name
if jsonTag := f.Tag.Get("json"); jsonTag != "" {
fieldName = strings.SplitN(jsonTag, ",", 2)[0]
}
if validator, ok := val.(ValueValidator); ok {
if errs2 := validator.ValidateValue(); errs2 != nil {
errs[fieldName] = errs2
}
continue
}
if vmapper, ok := val.(ValueMapper); ok {
val = vmapper.MapValue()
}
tag := f.Tag.Get("validate")
if tag == "" {
continue
}
vts := strings.Split(tag, ",")
for _, vt := range vts {
if vt == "struct" {
errs2 := v.Validate(val)
if errs2 != nil {
/* A field validation has failed */
errs[fieldName] = errs2
break
}
continue
}
vf := v[vt]
if vf == nil {
errs[fieldName] = fmt.Errorf("undefined validator: %q", vt)
break
}
if err := vf(val); err != nil {
errs[fieldName] = err
break
}
}
}
}