errors.go 7.3 KB

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