SHARE
TWEET

Untitled

a guest Apr 20th, 2019 108 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top