Merge pull request #113 from miraclesu/valid
Support Match validate function for tag
Showing
6 changed files
with
96 additions
and
45 deletions
| ... | @@ -55,9 +55,10 @@ Struct Tag Use: | ... | @@ -55,9 +55,10 @@ Struct Tag Use: |
| 55 | // validation function follow with "valid" tag | 55 | // validation function follow with "valid" tag |
| 56 | // functions divide with ";" | 56 | // functions divide with ";" |
| 57 | // parameters in parentheses "()" and divide with "," | 57 | // parameters in parentheses "()" and divide with "," |
| 58 | // Match function's pattern string must in "//" | ||
| 58 | type user struct { | 59 | type user struct { |
| 59 | Id int | 60 | Id int |
| 60 | Name string `valid:"Required"` | 61 | Name string `valid:"Required;Match(/^(test)?\\w*@;com$/)"` |
| 61 | Age int `valid:"Required;Range(1, 140)"` | 62 | Age int `valid:"Required;Range(1, 140)"` |
| 62 | } | 63 | } |
| 63 | 64 | ||
| ... | @@ -86,8 +87,7 @@ Struct Tag Functions: | ... | @@ -86,8 +87,7 @@ Struct Tag Functions: |
| 86 | Alpha | 87 | Alpha |
| 87 | Numeric | 88 | Numeric |
| 88 | AlphaNumeric | 89 | AlphaNumeric |
| 89 | Match(regexp string) // does not support yet | 90 | Match(pattern string) |
| 90 | NoMatch(regexp string) // does not support yet | ||
| 91 | AlphaDash | 91 | AlphaDash |
| 92 | 92 | ||
| 93 | IP | 93 | IP | ... | ... |
| ... | @@ -26,6 +26,7 @@ var ( | ... | @@ -26,6 +26,7 @@ var ( |
| 26 | "apply": true, | 26 | "apply": true, |
| 27 | "Check": true, | 27 | "Check": true, |
| 28 | "Valid": true, | 28 | "Valid": true, |
| 29 | "NoMatch": true, | ||
| 29 | } | 30 | } |
| 30 | ) | 31 | ) |
| 31 | 32 | ||
| ... | @@ -50,7 +51,7 @@ type Funcs map[string]reflect.Value | ... | @@ -50,7 +51,7 @@ type Funcs map[string]reflect.Value |
| 50 | func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) { | 51 | func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) { |
| 51 | defer func() { | 52 | defer func() { |
| 52 | if r := recover(); r != nil { | 53 | if r := recover(); r != nil { |
| 53 | err = r.(error) | 54 | err = fmt.Errorf("%v", r) |
| 54 | } | 55 | } |
| 55 | }() | 56 | }() |
| 56 | if _, ok := f[name]; !ok { | 57 | if _, ok := f[name]; !ok { |
| ... | @@ -82,10 +83,17 @@ func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) { | ... | @@ -82,10 +83,17 @@ func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) { |
| 82 | if len(tag) == 0 { | 83 | if len(tag) == 0 { |
| 83 | return | 84 | return |
| 84 | } | 85 | } |
| 86 | if vfs, tag, err = getRegFuncs(tag, f.Name); err != nil { | ||
| 87 | fmt.Printf("%+v\n", err) | ||
| 88 | return | ||
| 89 | } | ||
| 85 | fs := strings.Split(tag, ";") | 90 | fs := strings.Split(tag, ";") |
| 86 | for _, vfunc := range fs { | 91 | for _, vfunc := range fs { |
| 87 | var vf ValidFunc | 92 | var vf ValidFunc |
| 88 | vf, err = parseFunc(vfunc) | 93 | if len(vfunc) == 0 { |
| 94 | continue | ||
| 95 | } | ||
| 96 | vf, err = parseFunc(vfunc, f.Name) | ||
| 89 | if err != nil { | 97 | if err != nil { |
| 90 | return | 98 | return |
| 91 | } | 99 | } |
| ... | @@ -94,10 +102,33 @@ func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) { | ... | @@ -94,10 +102,33 @@ func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) { |
| 94 | return | 102 | return |
| 95 | } | 103 | } |
| 96 | 104 | ||
| 97 | func parseFunc(vfunc string) (v ValidFunc, err error) { | 105 | // Get Match function |
| 106 | // May be get NoMatch function in the future | ||
| 107 | func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) { | ||
| 108 | tag = strings.TrimSpace(tag) | ||
| 109 | index := strings.Index(tag, "Match(/") | ||
| 110 | if index == -1 { | ||
| 111 | str = tag | ||
| 112 | return | ||
| 113 | } | ||
| 114 | end := strings.LastIndex(tag, "/)") | ||
| 115 | if end < index { | ||
| 116 | err = fmt.Errorf("invalid Match function") | ||
| 117 | return | ||
| 118 | } | ||
| 119 | reg, err := regexp.Compile(tag[index+len("Match(/") : end]) | ||
| 120 | if err != nil { | ||
| 121 | return | ||
| 122 | } | ||
| 123 | vfs = []ValidFunc{ValidFunc{"Match", []interface{}{reg, key}}} | ||
| 124 | str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):]) | ||
| 125 | return | ||
| 126 | } | ||
| 127 | |||
| 128 | func parseFunc(vfunc, key string) (v ValidFunc, err error) { | ||
| 98 | defer func() { | 129 | defer func() { |
| 99 | if r := recover(); r != nil { | 130 | if r := recover(); r != nil { |
| 100 | err = r.(error) | 131 | err = fmt.Errorf("%v", r) |
| 101 | } | 132 | } |
| 102 | }() | 133 | }() |
| 103 | 134 | ||
| ... | @@ -114,7 +145,7 @@ func parseFunc(vfunc string) (v ValidFunc, err error) { | ... | @@ -114,7 +145,7 @@ func parseFunc(vfunc string) (v ValidFunc, err error) { |
| 114 | err = fmt.Errorf("%s require %d parameters", vfunc, num) | 145 | err = fmt.Errorf("%s require %d parameters", vfunc, num) |
| 115 | return | 146 | return |
| 116 | } | 147 | } |
| 117 | v = ValidFunc{vfunc, []interface{}{vfunc}} | 148 | v = ValidFunc{vfunc, []interface{}{key}} |
| 118 | return | 149 | return |
| 119 | } | 150 | } |
| 120 | 151 | ||
| ... | @@ -136,7 +167,7 @@ func parseFunc(vfunc string) (v ValidFunc, err error) { | ... | @@ -136,7 +167,7 @@ func parseFunc(vfunc string) (v ValidFunc, err error) { |
| 136 | return | 167 | return |
| 137 | } | 168 | } |
| 138 | 169 | ||
| 139 | tParams, err := trim(name, params) | 170 | tParams, err := trim(name, key, params) |
| 140 | if err != nil { | 171 | if err != nil { |
| 141 | return | 172 | return |
| 142 | } | 173 | } |
| ... | @@ -155,7 +186,7 @@ func numIn(name string) (num int, err error) { | ... | @@ -155,7 +186,7 @@ func numIn(name string) (num int, err error) { |
| 155 | return | 186 | return |
| 156 | } | 187 | } |
| 157 | 188 | ||
| 158 | func trim(name string, s []string) (ts []interface{}, err error) { | 189 | func trim(name, key string, s []string) (ts []interface{}, err error) { |
| 159 | ts = make([]interface{}, len(s), len(s)+1) | 190 | ts = make([]interface{}, len(s), len(s)+1) |
| 160 | fn, ok := funcs[name] | 191 | fn, ok := funcs[name] |
| 161 | if !ok { | 192 | if !ok { |
| ... | @@ -170,7 +201,7 @@ func trim(name string, s []string) (ts []interface{}, err error) { | ... | @@ -170,7 +201,7 @@ func trim(name string, s []string) (ts []interface{}, err error) { |
| 170 | } | 201 | } |
| 171 | ts[i] = param | 202 | ts[i] = param |
| 172 | } | 203 | } |
| 173 | ts = append(ts, name) | 204 | ts = append(ts, key) |
| 174 | return | 205 | return |
| 175 | } | 206 | } |
| 176 | 207 | ... | ... |
| ... | @@ -8,8 +8,9 @@ import ( | ... | @@ -8,8 +8,9 @@ import ( |
| 8 | type user struct { | 8 | type user struct { |
| 9 | Id int | 9 | Id int |
| 10 | Tag string `valid:"Maxx(aa)"` | 10 | Tag string `valid:"Maxx(aa)"` |
| 11 | Name string `valid:"Required"` | 11 | Name string `valid:"Required;"` |
| 12 | Age int `valid:"Required;Range(1, 140)"` | 12 | Age int `valid:"Required;Range(1, 140)"` |
| 13 | match string `valid:"Required; Match(/^(test)?\\w*@(/test/);com$/);Max(2)"` | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | func TestGetValidFuncs(t *testing.T) { | 16 | func TestGetValidFuncs(t *testing.T) { |
| ... | @@ -55,6 +56,14 @@ func TestGetValidFuncs(t *testing.T) { | ... | @@ -55,6 +56,14 @@ func TestGetValidFuncs(t *testing.T) { |
| 55 | if vfs[1].Name != "Range" && len(vfs[1].Params) != 2 { | 56 | if vfs[1].Name != "Range" && len(vfs[1].Params) != 2 { |
| 56 | t.Error("Range funcs should be got") | 57 | t.Error("Range funcs should be got") |
| 57 | } | 58 | } |
| 59 | |||
| 60 | f, _ = tf.FieldByName("match") | ||
| 61 | if vfs, err = getValidFuncs(f); err != nil { | ||
| 62 | t.Fatal(err) | ||
| 63 | } | ||
| 64 | if len(vfs) != 3 { | ||
| 65 | t.Fatal("should get 3 ValidFunc but now is", len(vfs)) | ||
| 66 | } | ||
| 58 | } | 67 | } |
| 59 | 68 | ||
| 60 | func TestCall(t *testing.T) { | 69 | func TestCall(t *testing.T) { | ... | ... |
| ... | @@ -84,16 +84,19 @@ func (v *Validation) Required(obj interface{}, key string) *ValidationResult { | ... | @@ -84,16 +84,19 @@ func (v *Validation) Required(obj interface{}, key string) *ValidationResult { |
| 84 | return v.apply(Required{key}, obj) | 84 | return v.apply(Required{key}, obj) |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | func (v *Validation) Min(n int, min int, key string) *ValidationResult { | 87 | // Test that the obj is greater than min if obj's type is int |
| 88 | return v.apply(Min{min, key}, n) | 88 | func (v *Validation) Min(obj interface{}, min int, key string) *ValidationResult { |
| 89 | return v.apply(Min{min, key}, obj) | ||
| 89 | } | 90 | } |
| 90 | 91 | ||
| 91 | func (v *Validation) Max(n int, max int, key string) *ValidationResult { | 92 | // Test that the obj is less than max if obj's type is int |
| 92 | return v.apply(Max{max, key}, n) | 93 | func (v *Validation) Max(obj interface{}, max int, key string) *ValidationResult { |
| 94 | return v.apply(Max{max, key}, obj) | ||
| 93 | } | 95 | } |
| 94 | 96 | ||
| 95 | func (v *Validation) Range(n, min, max int, key string) *ValidationResult { | 97 | // Test that the obj is between mni and max if obj's type is int |
| 96 | return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, n) | 98 | func (v *Validation) Range(obj interface{}, min, max int, key string) *ValidationResult { |
| 99 | return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj) | ||
| 97 | } | 100 | } |
| 98 | 101 | ||
| 99 | func (v *Validation) MinSize(obj interface{}, min int, key string) *ValidationResult { | 102 | func (v *Validation) MinSize(obj interface{}, min int, key string) *ValidationResult { |
| ... | @@ -120,45 +123,45 @@ func (v *Validation) AlphaNumeric(obj interface{}, key string) *ValidationResult | ... | @@ -120,45 +123,45 @@ func (v *Validation) AlphaNumeric(obj interface{}, key string) *ValidationResult |
| 120 | return v.apply(AlphaNumeric{key}, obj) | 123 | return v.apply(AlphaNumeric{key}, obj) |
| 121 | } | 124 | } |
| 122 | 125 | ||
| 123 | func (v *Validation) Match(str string, regex *regexp.Regexp, key string) *ValidationResult { | 126 | func (v *Validation) Match(obj interface{}, regex *regexp.Regexp, key string) *ValidationResult { |
| 124 | return v.apply(Match{regex, key}, str) | 127 | return v.apply(Match{regex, key}, obj) |
| 125 | } | 128 | } |
| 126 | 129 | ||
| 127 | func (v *Validation) NoMatch(str string, regex *regexp.Regexp, key string) *ValidationResult { | 130 | func (v *Validation) NoMatch(obj interface{}, regex *regexp.Regexp, key string) *ValidationResult { |
| 128 | return v.apply(NoMatch{Match{Regexp: regex}, key}, str) | 131 | return v.apply(NoMatch{Match{Regexp: regex}, key}, obj) |
| 129 | } | 132 | } |
| 130 | 133 | ||
| 131 | func (v *Validation) AlphaDash(str string, key string) *ValidationResult { | 134 | func (v *Validation) AlphaDash(obj interface{}, key string) *ValidationResult { |
| 132 | return v.apply(AlphaDash{NoMatch{Match: Match{Regexp: alphaDashPattern}}, key}, str) | 135 | return v.apply(AlphaDash{NoMatch{Match: Match{Regexp: alphaDashPattern}}, key}, obj) |
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | func (v *Validation) Email(str string, key string) *ValidationResult { | 138 | func (v *Validation) Email(obj interface{}, key string) *ValidationResult { |
| 136 | return v.apply(Email{Match{Regexp: emailPattern}, key}, str) | 139 | return v.apply(Email{Match{Regexp: emailPattern}, key}, obj) |
| 137 | } | 140 | } |
| 138 | 141 | ||
| 139 | func (v *Validation) IP(str string, key string) *ValidationResult { | 142 | func (v *Validation) IP(obj interface{}, key string) *ValidationResult { |
| 140 | return v.apply(IP{Match{Regexp: ipPattern}, key}, str) | 143 | return v.apply(IP{Match{Regexp: ipPattern}, key}, obj) |
| 141 | } | 144 | } |
| 142 | 145 | ||
| 143 | func (v *Validation) Base64(str string, key string) *ValidationResult { | 146 | func (v *Validation) Base64(obj interface{}, key string) *ValidationResult { |
| 144 | return v.apply(Base64{Match{Regexp: base64Pattern}, key}, str) | 147 | return v.apply(Base64{Match{Regexp: base64Pattern}, key}, obj) |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 147 | func (v *Validation) Mobile(str string, key string) *ValidationResult { | 150 | func (v *Validation) Mobile(obj interface{}, key string) *ValidationResult { |
| 148 | return v.apply(Mobile{Match{Regexp: mobilePattern}, key}, str) | 151 | return v.apply(Mobile{Match{Regexp: mobilePattern}, key}, obj) |
| 149 | } | 152 | } |
| 150 | 153 | ||
| 151 | func (v *Validation) Tel(str string, key string) *ValidationResult { | 154 | func (v *Validation) Tel(obj interface{}, key string) *ValidationResult { |
| 152 | return v.apply(Tel{Match{Regexp: telPattern}, key}, str) | 155 | return v.apply(Tel{Match{Regexp: telPattern}, key}, obj) |
| 153 | } | 156 | } |
| 154 | 157 | ||
| 155 | func (v *Validation) Phone(str string, key string) *ValidationResult { | 158 | func (v *Validation) Phone(obj interface{}, key string) *ValidationResult { |
| 156 | return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}}, | 159 | return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}}, |
| 157 | Tel{Match: Match{Regexp: telPattern}}, key}, str) | 160 | Tel{Match: Match{Regexp: telPattern}}, key}, obj) |
| 158 | } | 161 | } |
| 159 | 162 | ||
| 160 | func (v *Validation) ZipCode(str string, key string) *ValidationResult { | 163 | func (v *Validation) ZipCode(obj interface{}, key string) *ValidationResult { |
| 161 | return v.apply(ZipCode{Match{Regexp: zipCodePattern}, key}, str) | 164 | return v.apply(ZipCode{Match{Regexp: zipCodePattern}, key}, obj) |
| 162 | } | 165 | } |
| 163 | 166 | ||
| 164 | func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult { | 167 | func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult { | ... | ... |
| ... | @@ -61,10 +61,10 @@ func TestRange(t *testing.T) { | ... | @@ -61,10 +61,10 @@ func TestRange(t *testing.T) { |
| 61 | valid := Validation{} | 61 | valid := Validation{} |
| 62 | 62 | ||
| 63 | if valid.Range(-1, 0, 1, "range0_1").Ok { | 63 | if valid.Range(-1, 0, 1, "range0_1").Ok { |
| 64 | t.Error("-1 is bettween 0 and 1 should be false") | 64 | t.Error("-1 is between 0 and 1 should be false") |
| 65 | } | 65 | } |
| 66 | if !valid.Range(1, 0, 1, "range0_1").Ok { | 66 | if !valid.Range(1, 0, 1, "range0_1").Ok { |
| 67 | t.Error("1 is bettween 0 and 1 should be true") | 67 | t.Error("1 is between 0 and 1 should be true") |
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | 70 | ||
| ... | @@ -283,12 +283,12 @@ func TestZipCode(t *testing.T) { | ... | @@ -283,12 +283,12 @@ func TestZipCode(t *testing.T) { |
| 283 | func TestValid(t *testing.T) { | 283 | func TestValid(t *testing.T) { |
| 284 | type user struct { | 284 | type user struct { |
| 285 | Id int | 285 | Id int |
| 286 | Name string `valid:"Required"` | 286 | Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"` |
| 287 | Age int `valid:"Required;Range(1, 140)"` | 287 | Age int `valid:"Required;Range(1, 140)"` |
| 288 | } | 288 | } |
| 289 | valid := Validation{} | 289 | valid := Validation{} |
| 290 | 290 | ||
| 291 | u := user{Name: "test", Age: 40} | 291 | u := user{Name: "test@/test/;com", Age: 40} |
| 292 | b, err := valid.Valid(u) | 292 | b, err := valid.Valid(u) |
| 293 | if err != nil { | 293 | if err != nil { |
| 294 | t.Fatal(err) | 294 | t.Fatal(err) |
| ... | @@ -297,7 +297,7 @@ func TestValid(t *testing.T) { | ... | @@ -297,7 +297,7 @@ func TestValid(t *testing.T) { |
| 297 | t.Error("validation should be passed") | 297 | t.Error("validation should be passed") |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | uptr := &user{Name: "test", Age: 180} | 300 | uptr := &user{Name: "test", Age: 40} |
| 301 | b, err = valid.Valid(uptr) | 301 | b, err = valid.Valid(uptr) |
| 302 | if err != nil { | 302 | if err != nil { |
| 303 | t.Fatal(err) | 303 | t.Fatal(err) |
| ... | @@ -305,4 +305,13 @@ func TestValid(t *testing.T) { | ... | @@ -305,4 +305,13 @@ func TestValid(t *testing.T) { |
| 305 | if b { | 305 | if b { |
| 306 | t.Error("validation should not be passed") | 306 | t.Error("validation should not be passed") |
| 307 | } | 307 | } |
| 308 | |||
| 309 | u = user{Name: "test@/test/;com", Age: 180} | ||
| 310 | b, err = valid.Valid(u) | ||
| 311 | if err != nil { | ||
| 312 | t.Fatal(err) | ||
| 313 | } | ||
| 314 | if b { | ||
| 315 | t.Error("validation should not be passed") | ||
| 316 | } | ||
| 308 | } | 317 | } | ... | ... |
| ... | @@ -264,8 +264,7 @@ type Match struct { | ... | @@ -264,8 +264,7 @@ type Match struct { |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | func (m Match) IsSatisfied(obj interface{}) bool { | 266 | func (m Match) IsSatisfied(obj interface{}) bool { |
| 267 | str := obj.(string) | 267 | return m.Regexp.MatchString(fmt.Sprintf("%v", obj)) |
| 268 | return m.Regexp.MatchString(str) | ||
| 269 | } | 268 | } |
| 270 | 269 | ||
| 271 | func (m Match) DefaultMessage() string { | 270 | func (m Match) DefaultMessage() string { | ... | ... |
-
Please register or sign in to post a comment