You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
7.1 KiB

  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. // 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. // 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. // returns the namespace for the field error, with the tag
  76. // name taking precedence over the fields 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. // returns the namespace for the field error, with the fields
  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 it's name
  92. StructNamespace() string
  93. // returns the fields name with the tag name taking precedence over the
  94. // fields actual name.
  95. //
  96. // eq. JSON name "fname"
  97. // see StructField for comparison
  98. Field() string
  99. // returns the fields actual name from the struct, when able to determine.
  100. //
  101. // eq. "FirstName"
  102. // see Field for comparison
  103. StructField() string
  104. // returns the actual fields value in case needed for creating the error
  105. // message
  106. Value() interface{}
  107. // 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. // returns the FieldError's translated error
  119. // from the provided 'ut.Translator' and registered 'TranslationFunc'
  120. //
  121. // NOTE: is not registered translation can be found it returns the same
  122. // as calling fe.Error()
  123. Translate(ut ut.Translator) 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 fields actual name.
  155. func (fe *fieldError) Namespace() string {
  156. return fe.ns
  157. }
  158. // StructNamespace returns the namespace for the field error, with the fields
  159. // actual name.
  160. func (fe *fieldError) StructNamespace() string {
  161. return fe.structNs
  162. }
  163. // Field returns the fields name with the tag name taking precedence over the
  164. // fields 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. // returns the fields 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 fields 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: is not registered translation can be found it returns the same
  206. // as calling fe.Error()
  207. func (fe *fieldError) Translate(ut ut.Translator) string {
  208. m, ok := fe.v.transTagFunc[ut]
  209. if !ok {
  210. return fe.Error()
  211. }
  212. fn, ok := m[fe.tag]
  213. if !ok {
  214. return fe.Error()
  215. }
  216. return fn(ut, fe)
  217. }