1 package validator
2 3 import (
4 "bytes"
5 "fmt"
6 "reflect"
7 "strings"
8 9 ut "github.com/go-playground/universal-translator"
10 )
11 12 const (
13 fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
14 )
15 16 // ValidationErrorsTranslations is the translation return type
17 type ValidationErrorsTranslations map[string]string
18 19 // InvalidValidationError describes an invalid argument passed to
20 // `Struct`, `StructExcept`, StructPartial` or `Field`
21 type InvalidValidationError struct {
22 Type reflect.Type
23 }
24 25 // Error returns InvalidValidationError message
26 func (e *InvalidValidationError) Error() string {
27 28 if e.Type == nil {
29 return "validator: (nil)"
30 }
31 32 return "validator: (nil " + e.Type.String() + ")"
33 }
34 35 // ValidationErrors is an array of FieldError's
36 // for use in custom error messages post validation.
37 type ValidationErrors []FieldError
38 39 // Error is intended for use in development + debugging and not intended to be a production error message.
40 // It allows ValidationErrors to subscribe to the Error interface.
41 // All information to create an error message specific to your application is contained within
42 // the FieldError found within the ValidationErrors array
43 func (ve ValidationErrors) Error() string {
44 45 buff := bytes.NewBufferString("")
46 47 for i := 0; i < len(ve); i++ {
48 49 buff.WriteString(ve[i].Error())
50 buff.WriteString("\n")
51 }
52 53 return strings.TrimSpace(buff.String())
54 }
55 56 // Translate translates all of the ValidationErrors
57 func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
58 59 trans := make(ValidationErrorsTranslations)
60 61 var fe *fieldError
62 63 for i := 0; i < len(ve); i++ {
64 fe = ve[i].(*fieldError)
65 66 // // in case an Anonymous struct was used, ensure that the key
67 // // would be 'Username' instead of ".Username"
68 // if len(fe.ns) > 0 && fe.ns[:1] == "." {
69 // trans[fe.ns[1:]] = fe.Translate(ut)
70 // continue
71 // }
72 73 trans[fe.ns] = fe.Translate(ut)
74 }
75 76 return trans
77 }
78 79 // FieldError contains all functions to get error details
80 type FieldError interface {
81 82 // Tag returns the validation tag that failed. if the
83 // validation was an alias, this will return the
84 // alias name and not the underlying tag that failed.
85 //
86 // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
87 // will return "iscolor"
88 Tag() string
89 90 // ActualTag returns the validation tag that failed, even if an
91 // alias the actual tag within the alias will be returned.
92 // If an 'or' validation fails the entire or will be returned.
93 //
94 // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
95 // will return "hexcolor|rgb|rgba|hsl|hsla"
96 ActualTag() string
97 98 // Namespace returns the namespace for the field error, with the tag
99 // name taking precedence over the field's actual name.
100 //
101 // eg. JSON name "User.fname"
102 //
103 // See StructNamespace() for a version that returns actual names.
104 //
105 // NOTE: this field can be blank when validating a single primitive field
106 // using validate.Field(...) as there is no way to extract it's name
107 Namespace() string
108 109 // StructNamespace returns the namespace for the field error, with the field's
110 // actual name.
111 //
112 // eq. "User.FirstName" see Namespace for comparison
113 //
114 // NOTE: this field can be blank when validating a single primitive field
115 // using validate.Field(...) as there is no way to extract its name
116 StructNamespace() string
117 118 // Field returns the fields name with the tag name taking precedence over the
119 // field's actual name.
120 //
121 // eq. JSON name "fname"
122 // see StructField for comparison
123 Field() string
124 125 // StructField returns the field's actual name from the struct, when able to determine.
126 //
127 // eq. "FirstName"
128 // see Field for comparison
129 StructField() string
130 131 // Value returns the actual field's value in case needed for creating the error
132 // message
133 Value() interface{}
134 135 // Param returns the param value, in string form for comparison; this will also
136 // help with generating an error message
137 Param() string
138 139 // Kind returns the Field's reflect Kind
140 //
141 // eg. time.Time's kind is a struct
142 Kind() reflect.Kind
143 144 // Type returns the Field's reflect Type
145 //
146 // eg. time.Time's type is time.Time
147 Type() reflect.Type
148 149 // Translate returns the FieldError's translated error
150 // from the provided 'ut.Translator' and registered 'TranslationFunc'
151 //
152 // NOTE: if no registered translator can be found it returns the same as
153 // calling fe.Error()
154 Translate(ut ut.Translator) string
155 156 // Error returns the FieldError's message
157 Error() string
158 }
159 160 // compile time interface checks
161 var _ FieldError = new(fieldError)
162 var _ error = new(fieldError)
163 164 // fieldError contains a single field's validation error along
165 // with other properties that may be needed for error message creation
166 // it complies with the FieldError interface
167 type fieldError struct {
168 v *Validate
169 tag string
170 actualTag string
171 ns string
172 structNs string
173 fieldLen uint8
174 structfieldLen uint8
175 value interface{}
176 param string
177 kind reflect.Kind
178 typ reflect.Type
179 }
180 181 // Tag returns the validation tag that failed.
182 func (fe *fieldError) Tag() string {
183 return fe.tag
184 }
185 186 // ActualTag returns the validation tag that failed, even if an
187 // alias the actual tag within the alias will be returned.
188 func (fe *fieldError) ActualTag() string {
189 return fe.actualTag
190 }
191 192 // Namespace returns the namespace for the field error, with the tag
193 // name taking precedence over the field's actual name.
194 func (fe *fieldError) Namespace() string {
195 return fe.ns
196 }
197 198 // StructNamespace returns the namespace for the field error, with the field's
199 // actual name.
200 func (fe *fieldError) StructNamespace() string {
201 return fe.structNs
202 }
203 204 // Field returns the field's name with the tag name taking precedence over the
205 // field's actual name.
206 func (fe *fieldError) Field() string {
207 208 return fe.ns[len(fe.ns)-int(fe.fieldLen):]
209 // // return fe.field
210 // fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]
211 212 // log.Println("FLD:", fld)
213 214 // if len(fld) > 0 && fld[:1] == "." {
215 // return fld[1:]
216 // }
217 218 // return fld
219 }
220 221 // StructField returns the field's actual name from the struct, when able to determine.
222 func (fe *fieldError) StructField() string {
223 // return fe.structField
224 return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
225 }
226 227 // Value returns the actual field's value in case needed for creating the error
228 // message
229 func (fe *fieldError) Value() interface{} {
230 return fe.value
231 }
232 233 // Param returns the param value, in string form for comparison; this will
234 // also help with generating an error message
235 func (fe *fieldError) Param() string {
236 return fe.param
237 }
238 239 // Kind returns the Field's reflect Kind
240 func (fe *fieldError) Kind() reflect.Kind {
241 return fe.kind
242 }
243 244 // Type returns the Field's reflect Type
245 func (fe *fieldError) Type() reflect.Type {
246 return fe.typ
247 }
248 249 // Error returns the fieldError's error message
250 func (fe *fieldError) Error() string {
251 return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag)
252 }
253 254 // Translate returns the FieldError's translated error
255 // from the provided 'ut.Translator' and registered 'TranslationFunc'
256 //
257 // NOTE: if no registered translation can be found, it returns the original
258 // untranslated error message.
259 func (fe *fieldError) Translate(ut ut.Translator) string {
260 var fn TranslationFunc
261 262 m, ok := fe.v.transTagFunc[ut]
263 if !ok {
264 return fe.Error()
265 }
266 267 fn, ok = m[fe.tag]
268 if !ok {
269 fn, ok = m[fe.actualTag]
270 if !ok {
271 return fe.Error()
272 }
273 }
274 275 return fn(ut, fe)
276 }
277