struct.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package valid
  2. import (
  3. "context"
  4. "reflect"
  5. "strings"
  6. )
  7. // ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules.
  8. // Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid.
  9. // Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which
  10. // should be specified as a pointer to the field. A field can be associated with multiple rules.
  11. // For example,
  12. //
  13. // value := struct {
  14. // Name string
  15. // Value string
  16. // }{"name", "demo"}
  17. // err := validation.ValidateStruct(&value,
  18. // validation.Field(&a.Name, validation.Required),
  19. // validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
  20. // )
  21. // fmt.Println(err)
  22. // // Value: the length must be between 5 and 10.
  23. //
  24. // An error will be returned if validation fails.
  25. func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
  26. return ValidateStructWithContext(nil, structPtr, fields...)
  27. }
  28. // ValidateStructWithContext validates a struct with the given context.
  29. // The only difference between ValidateStructWithContext and ValidateStruct is that the former will
  30. // validate struct fields with the provided context.
  31. // Please refer to ValidateStruct for the detailed instructions on how to use this function.
  32. func ValidateStructWithContext(ctx context.Context, structPtr interface{}, fields ...*FieldRules) error {
  33. value := reflect.ValueOf(structPtr)
  34. if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct {
  35. // must be a pointer to a struct
  36. return NewInternalError(ErrStructPointer)
  37. }
  38. if value.IsNil() {
  39. // treat a nil struct pointer as valid
  40. return nil
  41. }
  42. value = value.Elem()
  43. errs := Errors{}
  44. for i, fr := range fields {
  45. fv := reflect.ValueOf(fr.fieldPtr)
  46. if fv.Kind() != reflect.Ptr {
  47. return NewInternalError(ErrFieldPointer(i))
  48. }
  49. ft := findStructField(value, fv)
  50. if ft == nil {
  51. return NewInternalError(ErrFieldNotFound(i))
  52. }
  53. var err error
  54. if ctx == nil {
  55. err = Validate(fv.Elem().Interface(), fr.rules...)
  56. } else {
  57. err = ValidateWithContext(ctx, fv.Elem().Interface(), fr.rules...)
  58. }
  59. if err != nil {
  60. if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
  61. return err
  62. }
  63. if ft.Anonymous {
  64. // merge errors from anonymous struct field
  65. if es, ok := err.(Errors); ok {
  66. for name, value := range es {
  67. errs[name] = value
  68. }
  69. continue
  70. }
  71. }
  72. errs[getErrorFieldName(ft)] = err
  73. }
  74. }
  75. if len(errs) > 0 {
  76. return errs
  77. }
  78. return nil
  79. }
  80. // FieldRules represents a rule set associated with a struct field.
  81. type FieldRules struct {
  82. fieldPtr interface{}
  83. rules []Rule
  84. }
  85. // Field specifies a struct field and the corresponding validation rules.
  86. // The struct field must be specified as a pointer to it.
  87. func Field(fieldPtr interface{}, rules ...Rule) *FieldRules {
  88. return &FieldRules{
  89. fieldPtr: fieldPtr,
  90. rules: rules,
  91. }
  92. }
  93. // findStructField looks for a field in the given struct.
  94. // The field being looked for should be a pointer to the actual struct field.
  95. // If found, the field info will be returned. Otherwise, nil will be returned.
  96. func findStructField(structValue reflect.Value, fieldValue reflect.Value) *reflect.StructField {
  97. ptr := fieldValue.Pointer()
  98. for i := structValue.NumField() - 1; i >= 0; i-- {
  99. sf := structValue.Type().Field(i)
  100. if ptr == structValue.Field(i).UnsafeAddr() {
  101. // do additional type comparison because it's possible that the address of
  102. // an embedded struct is the same as the first field of the embedded struct
  103. if sf.Type == fieldValue.Elem().Type() {
  104. return &sf
  105. }
  106. }
  107. if sf.Anonymous {
  108. // delve into anonymous struct to look for the field
  109. fi := structValue.Field(i)
  110. if sf.Type.Kind() == reflect.Ptr {
  111. fi = fi.Elem()
  112. }
  113. if fi.Kind() == reflect.Struct {
  114. if f := findStructField(fi, fieldValue); f != nil {
  115. return f
  116. }
  117. }
  118. }
  119. }
  120. return nil
  121. }
  122. // getErrorFieldName returns the name that should be used to represent the validation error of a struct field.
  123. func getErrorFieldName(f *reflect.StructField) string {
  124. if tag := f.Tag.Get(ErrorTag); tag != "" && tag != "-" {
  125. if cps := strings.SplitN(tag, ",", 2); cps[0] != "" {
  126. return cps[0]
  127. }
  128. }
  129. return f.Name
  130. }