Advertisement
Guest User

Untitled

a guest
Apr 20th, 2019
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.58 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "reflect"
  6. "regexp"
  7. "strings"
  8. )
  9.  
  10. // Name of the struct tag used in examples.
  11. const tagName = "validate"
  12.  
  13. // Regular expression to validate email address.
  14. var mailRe = regexp.MustCompile(`\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z`)
  15.  
  16. // Generic data validator.
  17. type Validator interface {
  18. // Validate method performs validation and returns result and optional error.
  19. Validate(interface{}) (bool, error)
  20. }
  21.  
  22. // DefaultValidator does not perform any validations.
  23. type DefaultValidator struct {
  24. }
  25.  
  26. func (v DefaultValidator) Validate(val interface{}) (bool, error) {
  27. return true, nil
  28. }
  29.  
  30. // StringValidator validates string presence and/or its length.
  31. type StringValidator struct {
  32. Min int
  33. Max int
  34. }
  35.  
  36. func (v StringValidator) Validate(val interface{}) (bool, error) {
  37. l := len(val.(string))
  38.  
  39. if l == 0 {
  40. return false, fmt.Errorf("cannot be blank")
  41. }
  42.  
  43. if l < v.Min {
  44. return false, fmt.Errorf("should be at least %v chars long", v.Min)
  45. }
  46.  
  47. if v.Max >= v.Min && l > v.Max {
  48. return false, fmt.Errorf("should be less than %v chars long", v.Max)
  49. }
  50.  
  51. return true, nil
  52. }
  53.  
  54. // NumberValidator performs numerical value validation.
  55. // Its limited to int type for simplicity.
  56. type NumberValidator struct {
  57. Min int
  58. Max int
  59. }
  60.  
  61. func (v NumberValidator) Validate(val interface{}) (bool, error) {
  62. num := val.(int)
  63.  
  64. if num < v.Min {
  65. return false, fmt.Errorf("should be greater than %v", v.Min)
  66. }
  67.  
  68. if v.Max >= v.Min && num > v.Max {
  69. return false, fmt.Errorf("should be less than %v", v.Max)
  70. }
  71.  
  72. return true, nil
  73. }
  74.  
  75. // EmailValidator checks if string is a valid email address.
  76. type EmailValidator struct {
  77. }
  78.  
  79. func (v EmailValidator) Validate(val interface{}) (bool, error) {
  80. if !mailRe.MatchString(val.(string)) {
  81. return false, fmt.Errorf("is not a valid email address")
  82. }
  83. return true, nil
  84. }
  85.  
  86. // Returns validator struct corresponding to validation type
  87. func getValidatorFromTag(tag string) Validator {
  88. args := strings.Split(tag, ",")
  89.  
  90. switch args[0] {
  91. case "number":
  92. validator := NumberValidator{}
  93. fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max)
  94. return validator
  95. case "string":
  96. validator := StringValidator{}
  97. fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max)
  98. return validator
  99. case "email":
  100. return EmailValidator{}
  101. }
  102.  
  103. return DefaultValidator{}
  104. }
  105.  
  106. // Performs actual data validation using validator definitions on the struct
  107. func validateStruct(s interface{}) []error {
  108. errs := []error{}
  109.  
  110. // ValueOf returns a Value representing the run-time data
  111. v := reflect.ValueOf(s)
  112.  
  113. for i := 0; i < v.NumField(); i++ {
  114. // Get the field tag value
  115. tag := v.Type().Field(i).Tag.Get(tagName)
  116.  
  117. // Skip if tag is not defined or ignored
  118. if tag == "" || tag == "-" {
  119. continue
  120. }
  121.  
  122. // Get a validator that corresponds to a tag
  123. validator := getValidatorFromTag(tag)
  124.  
  125. // Perform validation
  126. valid, err := validator.Validate(v.Field(i).Interface())
  127.  
  128. // Append error to results
  129. if !valid && err != nil {
  130. errs = append(errs, fmt.Errorf("%s %s", v.Type().Field(i).Name, err.Error()))
  131. }
  132. }
  133.  
  134. return errs
  135. }
  136.  
  137. type User struct {
  138. Id int `validate:"number,min=1,max=1000"`
  139. Name string `validate:"string,min=2,max=10"`
  140. Bio string `validate:"string"`
  141. Email string `validate:"email"`
  142. }
  143.  
  144. func main() {
  145. user := User{
  146. Id: 0,
  147. Name: "superlongstring",
  148. Bio: "",
  149. Email: "foobar",
  150. }
  151.  
  152. fmt.Println("Errors:")
  153. for i, err := range validateStruct(user) {
  154. fmt.Printf("\t%d. %s\n", i+1, err.Error())
  155. }
  156. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement