errors.go 7.3 KB

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