github.com/astaxie/beego@v1.12.3/validation/util.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validation
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"regexp"
    21  	"strconv"
    22  	"strings"
    23  )
    24  
    25  const (
    26  	// ValidTag struct tag
    27  	ValidTag = "valid"
    28  
    29  	LabelTag = "label"
    30  
    31  	wordsize = 32 << (^uint(0) >> 32 & 1)
    32  )
    33  
    34  var (
    35  	// key: function name
    36  	// value: the number of parameters
    37  	funcs = make(Funcs)
    38  
    39  	// doesn't belong to validation functions
    40  	unFuncs = map[string]bool{
    41  		"Clear":     true,
    42  		"HasErrors": true,
    43  		"ErrorMap":  true,
    44  		"Error":     true,
    45  		"apply":     true,
    46  		"Check":     true,
    47  		"Valid":     true,
    48  		"NoMatch":   true,
    49  	}
    50  	// ErrInt64On32 show 32 bit platform not support int64
    51  	ErrInt64On32 = fmt.Errorf("not support int64 on 32-bit platform")
    52  )
    53  
    54  func init() {
    55  	v := &Validation{}
    56  	t := reflect.TypeOf(v)
    57  	for i := 0; i < t.NumMethod(); i++ {
    58  		m := t.Method(i)
    59  		if !unFuncs[m.Name] {
    60  			funcs[m.Name] = m.Func
    61  		}
    62  	}
    63  }
    64  
    65  // CustomFunc is for custom validate function
    66  type CustomFunc func(v *Validation, obj interface{}, key string)
    67  
    68  // AddCustomFunc Add a custom function to validation
    69  // The name can not be:
    70  //   Clear
    71  //   HasErrors
    72  //   ErrorMap
    73  //   Error
    74  //   Check
    75  //   Valid
    76  //   NoMatch
    77  // If the name is same with exists function, it will replace the origin valid function
    78  func AddCustomFunc(name string, f CustomFunc) error {
    79  	if unFuncs[name] {
    80  		return fmt.Errorf("invalid function name: %s", name)
    81  	}
    82  
    83  	funcs[name] = reflect.ValueOf(f)
    84  	return nil
    85  }
    86  
    87  // ValidFunc Valid function type
    88  type ValidFunc struct {
    89  	Name   string
    90  	Params []interface{}
    91  }
    92  
    93  // Funcs Validate function map
    94  type Funcs map[string]reflect.Value
    95  
    96  // Call validate values with named type string
    97  func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) {
    98  	defer func() {
    99  		if r := recover(); r != nil {
   100  			err = fmt.Errorf("%v", r)
   101  		}
   102  	}()
   103  	if _, ok := f[name]; !ok {
   104  		err = fmt.Errorf("%s does not exist", name)
   105  		return
   106  	}
   107  	if len(params) != f[name].Type().NumIn() {
   108  		err = fmt.Errorf("The number of params is not adapted")
   109  		return
   110  	}
   111  	in := make([]reflect.Value, len(params))
   112  	for k, param := range params {
   113  		in[k] = reflect.ValueOf(param)
   114  	}
   115  	result = f[name].Call(in)
   116  	return
   117  }
   118  
   119  func isStruct(t reflect.Type) bool {
   120  	return t.Kind() == reflect.Struct
   121  }
   122  
   123  func isStructPtr(t reflect.Type) bool {
   124  	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
   125  }
   126  
   127  func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) {
   128  	tag := f.Tag.Get(ValidTag)
   129  	label := f.Tag.Get(LabelTag)
   130  	if len(tag) == 0 {
   131  		return
   132  	}
   133  	if vfs, tag, err = getRegFuncs(tag, f.Name); err != nil {
   134  		return
   135  	}
   136  	fs := strings.Split(tag, ";")
   137  	for _, vfunc := range fs {
   138  		var vf ValidFunc
   139  		if len(vfunc) == 0 {
   140  			continue
   141  		}
   142  		vf, err = parseFunc(vfunc, f.Name, label)
   143  		if err != nil {
   144  			return
   145  		}
   146  		vfs = append(vfs, vf)
   147  	}
   148  	return
   149  }
   150  
   151  // Get Match function
   152  // May be get NoMatch function in the future
   153  func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) {
   154  	tag = strings.TrimSpace(tag)
   155  	index := strings.Index(tag, "Match(/")
   156  	if index == -1 {
   157  		str = tag
   158  		return
   159  	}
   160  	end := strings.LastIndex(tag, "/)")
   161  	if end < index {
   162  		err = fmt.Errorf("invalid Match function")
   163  		return
   164  	}
   165  	reg, err := regexp.Compile(tag[index+len("Match(/") : end])
   166  	if err != nil {
   167  		return
   168  	}
   169  	vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}}
   170  	str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):])
   171  	return
   172  }
   173  
   174  func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
   175  	defer func() {
   176  		if r := recover(); r != nil {
   177  			err = fmt.Errorf("%v", r)
   178  		}
   179  	}()
   180  
   181  	vfunc = strings.TrimSpace(vfunc)
   182  	start := strings.Index(vfunc, "(")
   183  	var num int
   184  
   185  	// doesn't need parameter valid function
   186  	if start == -1 {
   187  		if num, err = numIn(vfunc); err != nil {
   188  			return
   189  		}
   190  		if num != 0 {
   191  			err = fmt.Errorf("%s require %d parameters", vfunc, num)
   192  			return
   193  		}
   194  		v = ValidFunc{vfunc, []interface{}{key + "." + vfunc + "." + label}}
   195  		return
   196  	}
   197  
   198  	end := strings.Index(vfunc, ")")
   199  	if end == -1 {
   200  		err = fmt.Errorf("invalid valid function")
   201  		return
   202  	}
   203  
   204  	name := strings.TrimSpace(vfunc[:start])
   205  	if num, err = numIn(name); err != nil {
   206  		return
   207  	}
   208  
   209  	params := strings.Split(vfunc[start+1:end], ",")
   210  	// the num of param must be equal
   211  	if num != len(params) {
   212  		err = fmt.Errorf("%s require %d parameters", name, num)
   213  		return
   214  	}
   215  
   216  	tParams, err := trim(name, key+"."+name+"."+label, params)
   217  	if err != nil {
   218  		return
   219  	}
   220  	v = ValidFunc{name, tParams}
   221  	return
   222  }
   223  
   224  func numIn(name string) (num int, err error) {
   225  	fn, ok := funcs[name]
   226  	if !ok {
   227  		err = fmt.Errorf("doesn't exists %s valid function", name)
   228  		return
   229  	}
   230  	// sub *Validation obj and key
   231  	num = fn.Type().NumIn() - 3
   232  	return
   233  }
   234  
   235  func trim(name, key string, s []string) (ts []interface{}, err error) {
   236  	ts = make([]interface{}, len(s), len(s)+1)
   237  	fn, ok := funcs[name]
   238  	if !ok {
   239  		err = fmt.Errorf("doesn't exists %s valid function", name)
   240  		return
   241  	}
   242  	for i := 0; i < len(s); i++ {
   243  		var param interface{}
   244  		// skip *Validation and obj params
   245  		if param, err = parseParam(fn.Type().In(i+2), strings.TrimSpace(s[i])); err != nil {
   246  			return
   247  		}
   248  		ts[i] = param
   249  	}
   250  	ts = append(ts, key)
   251  	return
   252  }
   253  
   254  // modify the parameters's type to adapt the function input parameters' type
   255  func parseParam(t reflect.Type, s string) (i interface{}, err error) {
   256  	switch t.Kind() {
   257  	case reflect.Int:
   258  		i, err = strconv.Atoi(s)
   259  	case reflect.Int64:
   260  		if wordsize == 32 {
   261  			return nil, ErrInt64On32
   262  		}
   263  		i, err = strconv.ParseInt(s, 10, 64)
   264  	case reflect.Int32:
   265  		var v int64
   266  		v, err = strconv.ParseInt(s, 10, 32)
   267  		if err == nil {
   268  			i = int32(v)
   269  		}
   270  	case reflect.Int16:
   271  		var v int64
   272  		v, err = strconv.ParseInt(s, 10, 16)
   273  		if err == nil {
   274  			i = int16(v)
   275  		}
   276  	case reflect.Int8:
   277  		var v int64
   278  		v, err = strconv.ParseInt(s, 10, 8)
   279  		if err == nil {
   280  			i = int8(v)
   281  		}
   282  	case reflect.String:
   283  		i = s
   284  	case reflect.Ptr:
   285  		if t.Elem().String() != "regexp.Regexp" {
   286  			err = fmt.Errorf("not support %s", t.Elem().String())
   287  			return
   288  		}
   289  		i, err = regexp.Compile(s)
   290  	default:
   291  		err = fmt.Errorf("not support %s", t.Kind().String())
   292  	}
   293  	return
   294  }
   295  
   296  func mergeParam(v *Validation, obj interface{}, params []interface{}) []interface{} {
   297  	return append([]interface{}{v, obj}, params...)
   298  }