github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/valid/structtags.go (about)

     1  package valid
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"reflect"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // SUGGESTED STRUCT TAG EXAMPLES SHOWING FORMAT:
    13  //
    14  // Field1 string      `valid:"required,minlen=6"`
    15  // Field2 interface{} `valid:"type=int,minval=10,maxval=1000"`
    16  
    17  // type StructTagRuleFunc func(fieldName string, part string) (Rules, error)
    18  
    19  // // StructTagInfo is a package global var so if an application wants to register
    20  // // it's own struct tag parsing rules it can
    21  // var StructTagInfo []StructTagRuleFunc
    22  
    23  var DefaultTagRuleGenerators []TagRuleGenerator
    24  
    25  func init() {
    26  
    27  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    28  		if len(tagValues["notnil"]) > 0 {
    29  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    30  			return Rules{NewNotNilRule(fieldName)}, nil
    31  		}
    32  		return nil, nil
    33  	}))
    34  
    35  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    36  		i, _ := strconv.Atoi(tagValues.Get("minlen"))
    37  		if i > 0 {
    38  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    39  			return Rules{NewMinLenRule(fieldName, i)}, nil
    40  		}
    41  		return nil, nil
    42  	}))
    43  
    44  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    45  		i, _ := strconv.Atoi(tagValues.Get("maxlen"))
    46  		if i > 0 {
    47  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    48  			return Rules{NewMaxLenRule(fieldName, i)}, nil
    49  		}
    50  		return nil, nil
    51  	}))
    52  
    53  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    54  		re := tagValues.Get("regexp")
    55  		if re != "" {
    56  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    57  			rec, err := regexp.Compile(re)
    58  			if err != nil {
    59  				return nil, err
    60  			}
    61  			return Rules{NewRegexpRule(fieldName, rec)}, nil
    62  		}
    63  		return nil, nil
    64  	}))
    65  
    66  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    67  		_, ok := tagValues["email"]
    68  		if ok {
    69  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    70  			return Rules{NewEmailRule(fieldName)}, nil
    71  		}
    72  		return nil, nil
    73  	}))
    74  
    75  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    76  
    77  		mvstr, ok := tagValues["minval"]
    78  		if ok && len(mvstr) > 0 {
    79  
    80  			var mv float64
    81  			_, err := fmt.Sscanf(mvstr[0], "%f", &mv)
    82  			if err != nil {
    83  				return nil, fmt.Errorf("error parsing minval %q: %v", mvstr[0], err)
    84  			}
    85  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
    86  			return Rules{NewMinValRule(fieldName, mv)}, nil
    87  
    88  		}
    89  
    90  		return nil, nil
    91  	}))
    92  
    93  	DefaultTagRuleGenerators = append(DefaultTagRuleGenerators, TagRuleGeneratorFunc(func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
    94  
    95  		mvstr, ok := tagValues["maxval"]
    96  		if ok && len(mvstr) > 0 {
    97  
    98  			var mv float64
    99  			_, err := fmt.Sscanf(mvstr[0], "%f", &mv)
   100  			if err != nil {
   101  				return nil, fmt.Errorf("error parsing maxval %q: %v", mvstr[0], err)
   102  			}
   103  			fieldName := MakeRuleFieldName(goFieldName, tagValues)
   104  			return Rules{NewMaxValRule(fieldName, mv)}, nil
   105  
   106  		}
   107  
   108  		return nil, nil
   109  	}))
   110  
   111  }
   112  
   113  type TagRuleGenerator interface {
   114  	// Generate one or more rules from the Go struct field name, the name inside
   115  	// the 'valid' part of the struct tag and it's value(s).
   116  	TagRuleGenerate(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error)
   117  }
   118  
   119  type TagRuleGeneratorFunc func(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error)
   120  
   121  func (f TagRuleGeneratorFunc) TagRuleGenerate(t reflect.Type, goFieldName string, tagValues url.Values) (Rules, error) {
   122  	return f(t, goFieldName, tagValues)
   123  }
   124  
   125  func StructRules(t reflect.Type, gens []TagRuleGenerator) (Rules, error) {
   126  
   127  	if gens == nil {
   128  		gens = DefaultTagRuleGenerators
   129  	}
   130  
   131  	var rules Rules
   132  
   133  	for t.Kind() == reflect.Ptr {
   134  		t = t.Elem()
   135  	}
   136  
   137  	for i := 0; i < t.NumField(); i++ {
   138  		sf := t.Field(i)
   139  		vstr := sf.Tag.Get("valid")
   140  		vvalues := StructTagToValues(vstr)
   141  
   142  		for _, gen := range gens {
   143  			rs, err := gen.TagRuleGenerate(t, sf.Name, vvalues)
   144  			if err != nil {
   145  				return nil, err
   146  			}
   147  			rules = append(rules, rs...)
   148  		}
   149  
   150  	}
   151  
   152  	return rules, nil
   153  }
   154  
   155  func MakeRuleFieldName(goFieldName string, tagValues url.Values) string {
   156  	name := tagValues.Get("name")
   157  	if name != "" {
   158  		return name
   159  	}
   160  	ret := goFieldName // use the go field name as-is
   161  	// ret := ToSnake(goFieldName)
   162  	// log.Printf("MakeRuleFieldName(goFieldName=%q) is returning %q", goFieldName, ret)
   163  	return ret
   164  }
   165  
   166  func StructTagToValues(st string) url.Values {
   167  
   168  	ret := make(url.Values)
   169  
   170  	parts := strings.Split(st, ",")
   171  
   172  	for _, part := range parts {
   173  		kvparts := strings.SplitN(part, "=", 2)
   174  		if len(kvparts) < 2 {
   175  			ret.Set(kvparts[0], "")
   176  		} else {
   177  			ret.Set(kvparts[0], kvparts[1])
   178  		}
   179  	}
   180  
   181  	return ret
   182  }