diff --git a/validation/util.go b/validation/util.go index 3eeabab0..cbdb1fa3 100644 --- a/validation/util.go +++ b/validation/util.go @@ -48,6 +48,11 @@ type ValidFunc struct { type Funcs map[string]reflect.Value func (f Funcs) Call(name string, params ...interface{}) (result []reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + err = r.(error) + } + }() if _, ok := f[name]; !ok { err = fmt.Errorf("%s does not exist", name) return @@ -109,7 +114,7 @@ func parseFunc(vfunc string) (v ValidFunc, err error) { err = fmt.Errorf("%s require %d parameters", vfunc, num) return } - v = ValidFunc{Name: vfunc} + v = ValidFunc{vfunc, []interface{}{vfunc}} return } @@ -145,12 +150,13 @@ func numIn(name string) (num int, err error) { err = fmt.Errorf("doesn't exsits %s valid function", name) return } + // sub *Validation obj and key num = fn.Type().NumIn() - 3 return } func trim(name string, s []string) (ts []interface{}, err error) { - ts = make([]interface{}, len(s)) + ts = make([]interface{}, len(s), len(s)+1) fn, ok := funcs[name] if !ok { err = fmt.Errorf("doesn't exsits %s valid function", name) @@ -158,14 +164,17 @@ func trim(name string, s []string) (ts []interface{}, err error) { } for i := 0; i < len(s); i++ { var param interface{} + // skip *Validation and obj params if param, err = magic(fn.Type().In(i+2), strings.TrimSpace(s[i])); err != nil { return } ts[i] = param } + ts = append(ts, name) return } +// modify the parameters's type to adapt the function input parameters' type func magic(t reflect.Type, s string) (i interface{}, err error) { switch t.Kind() { case reflect.Int: @@ -174,10 +183,16 @@ func magic(t reflect.Type, s string) (i interface{}, err error) { i = s case reflect.Ptr: if t.Elem().String() != "regexp.Regexp" { - err = fmt.Errorf("%s does not support", t.Elem().String()) + err = fmt.Errorf("does not support %s", t.Elem().String()) return } i, err = regexp.Compile(s) + default: + err = fmt.Errorf("does not support %s", t.Kind().String()) } return } + +func mergeParam(v *Validation, obj interface{}, params []interface{}) []interface{} { + return append([]interface{}{v, obj}, params...) +} diff --git a/validation/util_test.go b/validation/util_test.go index a76a81a8..e181871f 100644 --- a/validation/util_test.go +++ b/validation/util_test.go @@ -67,7 +67,10 @@ func TestCall(t *testing.T) { t.Fatal(err) } valid := &Validation{} - funcs.Call(vfs[1].Name, valid, u.Age, vfs[1].Params[0], vfs[1].Params[1], vfs[1].Name) + vfs[1].Params = append([]interface{}{valid, u.Age}, vfs[1].Params...) + if _, err = funcs.Call(vfs[1].Name, vfs[1].Params...); err != nil { + t.Fatal(err) + } if len(valid.Errors) != 1 { t.Error("age out of range should be has an error") } diff --git a/validation/validation.go b/validation/validation.go index af5df744..46251716 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -179,26 +179,29 @@ func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResu // the obj parameter must be a struct or a struct pointer func (v *Validation) Valid(obj interface{}) (b bool, err error) { - t := reflect.TypeOf(obj) + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) switch { - case isStruct(t): - case isStructPtr(t): - t = t.Elem() + case isStruct(objT): + case isStructPtr(objT): + objT = objT.Elem() + objV = objV.Elem() default: err = fmt.Errorf("%v must be a struct or a struct pointer", obj) return } - // tv := reflect.TypeOf(v) - // for i := 0; i < t.NumField(); i++ { - // f := t.Field(i) - // var vfs []ValidFunc - // if vfs, err = getValidFuncs(f); err != nil { - // return - // } - // for _, vf := range vfs { - // m, _ := tv.MethodByName(vf.Name) - // m.Func - // } - // } - return + + for i := 0; i < objT.NumField(); i++ { + var vfs []ValidFunc + if vfs, err = getValidFuncs(objT.Field(i)); err != nil { + return + } + for _, vf := range vfs { + if _, err = funcs.Call(vf.Name, + mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil { + return + } + } + } + return !v.HasErrors(), nil } diff --git a/validation/validation_test.go b/validation/validation_test.go index 1d6b18dd..d9f35922 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -57,16 +57,16 @@ func TestMax(t *testing.T) { } } -// func TestRange(t *testing.T) { -// valid := Validation{} +func TestRange(t *testing.T) { + valid := Validation{} -// if valid.Range(-1, 0, 1, "range0_1").Ok { -// t.Error("-1 is bettween 0 and 1 should be false") -// } -// if !valid.Range(1, 0, 1, "range0_1").Ok { -// t.Error("1 is bettween 0 and 1 should be true") -// } -// } + if valid.Range(-1, 0, 1, "range0_1").Ok { + t.Error("-1 is bettween 0 and 1 should be false") + } + if !valid.Range(1, 0, 1, "range0_1").Ok { + t.Error("1 is bettween 0 and 1 should be true") + } +} func TestMinSize(t *testing.T) { valid := Validation{} @@ -217,3 +217,30 @@ func TestBase64(t *testing.T) { t.Error("\"c3VjaHVhbmdqaUBnbWFpbC5jb20=\" are a valid base64 characters should be true") } } + +func TestValid(t *testing.T) { + type user struct { + Id int + Name string `valid:"Required"` + Age int `valid:"Required;Range(1, 140)"` + } + valid := Validation{} + + u := user{Name: "test", Age: 40} + b, err := valid.Valid(u) + if err != nil { + t.Fatal(err) + } + if !b { + t.Error("validation should be passed") + } + + uptr := &user{Name: "test", Age: 180} + b, err = valid.Valid(uptr) + if err != nil { + t.Fatal(err) + } + if b { + t.Error("validation should not be passed") + } +}