-
Notifications
You must be signed in to change notification settings - Fork 0
/
validate.go
162 lines (159 loc) · 5.61 KB
/
validate.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Package validate is yet another Go struct/object validation package, with a
// focus on simplicity, flexibility, and full control over validation logic.
//
// Interface
//
// To add validation to any type, simply implement the Validatable interface:
//
// type Validatable interface {
// Validate() error
// }
//
// To mark a object as failing validation, the Validate method simply needs to
// return a error.
//
// When validating array, slice, map, and struct types each item and/or field
// that implements Validatable will be validated, meaning deeply nested structs
// can be fully validated, and the nested path to each object is tracked and
// reported back any validation errors.
//
// Multiple Errors
//
// Multiple errors can be reported from the Validate method using one of the
// available Append helper functions which append errors together. Under the
// hood the go.uber.org/multierr package is used to represent multiple errors as
// a single error return type, and you can in fact just directly use multierr in
// the a type's Validate method.
//
// Structs and Field-specific Errors
//
// When validating a struct, you are likely to have multiple errors for multiple
// fields. To specify which field on the struct the error relates to, you have
// to return a *validate.Error instead of a normal Go error type. For example:
//
// type Book struct {
// Title string
// Author string
// }
//
// func (s *Book) Validate() error {
// var errs error
//
// if s.Title == "" {
// errs = validate.Append(errs, &validate.Error{
// Field: "Title", Msg: "is required",
// })
// }
//
// if s.Author == "" {
// // Yields the same result as the Title field check above.
// errs = validate.AppendFieldError(errs, "Author", "is required")
// }
//
// return errs
// }
//
// With the above example, if you validate a empty *Book:
//
// err := validate.Validate(&Book{})
// for _, e := range validate.Errors(err) {
// fmt.Println(e.Error())
// }
//
// The following errors would be printed:
//
// Title: is required
// Kind: is required
//
// Error type
//
// All errors will be wrapped in a *Error before being returned, which is used
// to keep track of the path and field the error relates to. There are various
// helpers available to create Error instances.
//
// Handling Validation Errors
//
// As mentioned above, multiple errors are wrapped up into a single error return
// value using go.uber.org/multierr. You can access all errors individually with
// Errors(), which accepts a single error, and returns []error. The Errors()
// function is just wrapper around multierr.Errors(), so you could use that
// instead if you prefer.
//
// Struct Field Tags
//
// Fields on a struct which customize the name via a json, yaml, or form field
// tag, will automatically have the field name converted to the name in the tag
// in returned *Error types with a non-empty Field value.
//
// You can customize the field name conversion logic by creating a custom
// Validator instance, and calling FieldNameFunc() on it.
//
// Nested Validatable Objects
//
// All items/fields on any structs, maps, slices or arrays which are encountered
// will be validated if they implement the Validatable interface. While
// traversing nested data structures, a path list tracks the location of the
// current object being validation in relation to the top-level object being
// validated. This path is used within the field in the final output errors.
//
// By default path components are joined with a dot, but this can be customized
// when using a custom Validator instance and calling FieldJoinFunc() passing in
// a custom function to handle path joining.
//
// As an example, if our Book struct from above is nested within the following
// structs:
//
// type Order struct {
// Items []*Item `json:"items"`
// }
//
// type Item struct {
// Book *Book `json:"book"`
// }
//
// And we have a Order where the book in the second Item has a empty Author
// field:
//
// err := validate.Validate(&Order{
// Items: []*Item{
// {Book: &Book{Title: "The Traveler", Author: "John Twelve Hawks"}},
// {Book: &Book{Title: "The Firm"}},
// },
// })
// for _, e := range validate.Errors(err) {
// fmt.Println(e.Error())
// }
//
// Then we would get the following error:
//
// items.1.book.Author: is required
//
// Note how both "items" and "book" are lower cased thanks to the json tags on
// the struct fields, while our Book struct does not have a json tag for the
// Author field.
//
// Also note that the error message does not start with "Order". The field path
// is relative to the object being validated, hence the top-level object is not
// part of the returned field path.
package validate
// global is a private instance of Validator to enable the package root-level
// Validate() function.
var global = New()
// Validate will validate the given object. Structs, maps, slices, and arrays
// will have each of their fields/items validated, effectively performing a
// deep-validation.
func Validate(v interface{}) error {
return global.Validate(v)
}
// Validatable is the primary interface that a object needs to implement to be
// validatable with Validator.
//
// Validation errors are reported by returning a error from the Validate
// method. Multiple errors can be combined into a single error to return with
// Append() and related functions, or via go.uber.org/multierr.
//
// For validatable structs, the field the validation error relates to can be
// specified by returning a *Error type with the Field value specified.
type Validatable interface {
Validate() error
}