Beego/validation/util.go

299 lines
6.7 KiB
Go
Raw Normal View History

2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2013-07-23 06:42:14 +00:00
package validation
import (
"fmt"
"reflect"
2013-07-23 17:20:24 +00:00
"regexp"
"strconv"
2013-07-23 06:42:14 +00:00
"strings"
)
const (
2015-09-12 16:13:19 +00:00
// ValidTag struct tag
ValidTag = "valid"
LabelTag = "label"
wordsize = 32 << (^uint(0) >> 32 & 1)
2013-07-23 06:42:14 +00:00
)
var (
// key: function name
// value: the number of parameters
2013-07-23 17:20:24 +00:00
funcs = make(Funcs)
2013-07-23 06:42:14 +00:00
// doesn't belong to validation functions
unFuncs = map[string]bool{
"Clear": true,
"HasErrors": true,
"ErrorMap": true,
"Error": true,
"apply": true,
"Check": true,
"Valid": true,
"NoMatch": true,
2013-07-23 06:42:14 +00:00
}
2017-07-18 16:52:27 +00:00
// ErrInt64On32 show 32 bit platform not support int64
ErrInt64On32 = fmt.Errorf("not support int64 on 32-bit platform")
2013-07-23 06:42:14 +00:00
)
func init() {
v := &Validation{}
t := reflect.TypeOf(v)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
if !unFuncs[m.Name] {
2013-07-23 17:20:24 +00:00
funcs[m.Name] = m.Func
2013-07-23 06:42:14 +00:00
}
}
}
2016-01-17 16:18:21 +00:00
// CustomFunc is for custom validate function
2016-01-07 14:42:04 +00:00
type CustomFunc func(v *Validation, obj interface{}, key string)
2016-01-17 16:18:21 +00:00
// AddCustomFunc Add a custom function to validation
2016-01-07 14:42:04 +00:00
// The name can not be:
// Clear
// HasErrors
// ErrorMap
// Error
// Check
// Valid
// NoMatch
// If the name is same with exists function, it will replace the origin valid function
func AddCustomFunc(name string, f CustomFunc) error {
if unFuncs[name] {
return fmt.Errorf("invalid function name: %s", name)
}
funcs[name] = reflect.ValueOf(f)
return nil
}
2015-09-12 16:13:19 +00:00
// ValidFunc Valid function type
2013-07-23 06:42:14 +00:00
type ValidFunc struct {
Name string
Params []interface{}
}
2015-09-12 16:13:19 +00:00
// Funcs Validate function map
2013-07-23 17:20:24 +00:00
type Funcs map[string]reflect.Value
2015-09-12 16:13:19 +00:00
// Call validate values with named type string
2013-07-23 17:20:24 +00:00
func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) {
2013-07-24 04:20:42 +00:00
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
2013-07-24 04:20:42 +00:00
}
}()
2013-07-23 17:20:24 +00:00
if _, ok := f[name]; !ok {
err = fmt.Errorf("%s does not exist", name)
return
}
if len(params) != f[name].Type().NumIn() {
err = fmt.Errorf("The number of params is not adapted")
return
}
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
result = f[name].Call(in)
return
}
2013-07-23 06:42:14 +00:00
func isStruct(t reflect.Type) bool {
return t.Kind() == reflect.Struct
}
func isStructPtr(t reflect.Type) bool {
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}
func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) {
2015-09-12 16:13:19 +00:00
tag := f.Tag.Get(ValidTag)
label := f.Tag.Get(LabelTag)
2013-07-23 06:42:14 +00:00
if len(tag) == 0 {
return
}
if vfs, tag, err = getRegFuncs(tag, f.Name); err != nil {
return
}
2013-07-23 06:42:14 +00:00
fs := strings.Split(tag, ";")
for _, vfunc := range fs {
var vf ValidFunc
if len(vfunc) == 0 {
continue
}
vf, err = parseFunc(vfunc, f.Name, label)
2013-07-23 06:42:14 +00:00
if err != nil {
return
}
vfs = append(vfs, vf)
}
return
}
// Get Match function
// May be get NoMatch function in the future
func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) {
tag = strings.TrimSpace(tag)
index := strings.Index(tag, "Match(/")
if index == -1 {
str = tag
return
}
end := strings.LastIndex(tag, "/)")
if end < index {
err = fmt.Errorf("invalid Match function")
return
}
reg, err := regexp.Compile(tag[index+len("Match(/") : end])
if err != nil {
return
}
2015-03-20 05:29:01 +00:00
vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}}
str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):])
return
}
func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
2013-07-23 06:42:14 +00:00
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
2013-07-23 06:42:14 +00:00
}
}()
vfunc = strings.TrimSpace(vfunc)
start := strings.Index(vfunc, "(")
var num int
// doesn't need parameter valid function
if start == -1 {
if num, err = numIn(vfunc); err != nil {
return
}
if num != 0 {
err = fmt.Errorf("%s require %d parameters", vfunc, num)
return
}
v = ValidFunc{vfunc, []interface{}{key + "." + vfunc + "." + label}}
2013-07-23 06:42:14 +00:00
return
}
end := strings.Index(vfunc, ")")
if end == -1 {
err = fmt.Errorf("invalid valid function")
return
}
name := strings.TrimSpace(vfunc[:start])
if num, err = numIn(name); err != nil {
return
}
params := strings.Split(vfunc[start+1:end], ",")
// the num of param must be equal
if num != len(params) {
err = fmt.Errorf("%s require %d parameters", name, num)
return
}
tParams, err := trim(name, key+"."+ name + "." + label, params)
2013-07-23 17:20:24 +00:00
if err != nil {
return
}
v = ValidFunc{name, tParams}
2013-07-23 06:42:14 +00:00
return
}
func numIn(name string) (num int, err error) {
2013-07-23 17:20:24 +00:00
fn, ok := funcs[name]
2013-07-23 06:42:14 +00:00
if !ok {
2020-02-14 08:47:47 +00:00
err = fmt.Errorf("doesn't exists %s valid function", name)
2013-07-23 17:20:24 +00:00
return
2013-07-23 06:42:14 +00:00
}
2013-07-24 04:20:42 +00:00
// sub *Validation obj and key
2013-07-23 17:20:24 +00:00
num = fn.Type().NumIn() - 3
2013-07-23 06:42:14 +00:00
return
}
func trim(name, key string, s []string) (ts []interface{}, err error) {
2013-07-24 04:20:42 +00:00
ts = make([]interface{}, len(s), len(s)+1)
2013-07-23 17:20:24 +00:00
fn, ok := funcs[name]
if !ok {
2020-02-14 08:47:47 +00:00
err = fmt.Errorf("doesn't exists %s valid function", name)
2013-07-23 17:20:24 +00:00
return
}
2013-07-23 06:42:14 +00:00
for i := 0; i < len(s); i++ {
2013-07-23 17:20:24 +00:00
var param interface{}
2013-07-24 04:20:42 +00:00
// skip *Validation and obj params
2016-01-07 14:42:04 +00:00
if param, err = parseParam(fn.Type().In(i+2), strings.TrimSpace(s[i])); err != nil {
2013-07-23 17:20:24 +00:00
return
}
ts[i] = param
}
ts = append(ts, key)
2013-07-23 17:20:24 +00:00
return
}
2013-07-24 04:20:42 +00:00
// modify the parameters's type to adapt the function input parameters' type
2016-01-07 14:42:04 +00:00
func parseParam(t reflect.Type, s string) (i interface{}, err error) {
2013-07-23 17:20:24 +00:00
switch t.Kind() {
case reflect.Int:
i, err = strconv.Atoi(s)
case reflect.Int64:
if wordsize == 32 {
2017-07-18 16:52:27 +00:00
return nil, ErrInt64On32
}
i, err = strconv.ParseInt(s, 10, 64)
case reflect.Int32:
var v int64
v, err = strconv.ParseInt(s, 10, 32)
if err == nil {
i = int32(v)
}
case reflect.Int16:
var v int64
v, err = strconv.ParseInt(s, 10, 16)
if err == nil {
i = int16(v)
}
case reflect.Int8:
var v int64
v, err = strconv.ParseInt(s, 10, 8)
if err == nil {
i = int8(v)
}
2013-07-23 17:20:24 +00:00
case reflect.String:
i = s
case reflect.Ptr:
if t.Elem().String() != "regexp.Regexp" {
err = fmt.Errorf("not support %s", t.Elem().String())
2013-07-23 17:20:24 +00:00
return
}
i, err = regexp.Compile(s)
2013-07-24 04:20:42 +00:00
default:
err = fmt.Errorf("not support %s", t.Kind().String())
2013-07-23 06:42:14 +00:00
}
2013-07-23 17:20:24 +00:00
return
2013-07-23 06:42:14 +00:00
}
2013-07-24 04:20:42 +00:00
func mergeParam(v *Validation, obj interface{}, params []interface{}) []interface{} {
return append([]interface{}{v, obj}, params...)
}