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 }