diff --git a/rules.go b/rules.go index 3656938..685d0d5 100644 --- a/rules.go +++ b/rules.go @@ -90,28 +90,6 @@ var funcs = map[string]listFuncInfo{ }}, // Integers - `gte`: {InputSame, Kinds{ - reflect.Int: func(rv reflect.Value, val interface{}) bool { - return rv.Int() >= val.(int64) - }, - reflect.Uint: func(rv reflect.Value, val interface{}) bool { - return rv.Uint() >= val.(uint64) - }, - reflect.Float64: func(rv reflect.Value, val interface{}) bool { - return rv.Float() >= val.(float64) - }, - }}, - `lte`: {InputSame, Kinds{ - reflect.Int: func(rv reflect.Value, val interface{}) bool { - return rv.Int() <= val.(int64) - }, - reflect.Uint: func(rv reflect.Value, val interface{}) bool { - return rv.Uint() <= val.(uint64) - }, - reflect.Float64: func(rv reflect.Value, val interface{}) bool { - return rv.Float() <= val.(float64) - }, - }}, `gt`: {InputSame, Kinds{ reflect.Int: func(rv reflect.Value, val interface{}) bool { return rv.Int() > val.(int64) diff --git a/rules_test.go b/rules_test.go index 409c8fc..32d632d 100644 --- a/rules_test.go +++ b/rules_test.go @@ -16,9 +16,9 @@ func TestAddRule(t *testing.T) { }, }) - pass := s{`custom`} + var pass = s{`custom`} - fail := s{`somethingelse`} + var fail = s{`somethingelse`} check(t, pass, 0) check(t, fail, 1) @@ -38,9 +38,9 @@ func TestRuleRequired(t *testing.T) { } str := `` - pass := s{&str, make([]int, 1), make([]int, 1), ` `, -1, 1, 0.01, ``, map[int]int{0: 1}} + var pass = s{&str, make([]int, 1), make([]int, 1), ` `, -1, 1, 0.01, ``, map[int]int{0: 1}} - fail := s{nil, nil, make([]int, 0), ``, 0, 0, 0.000, nil, nil} + var fail = s{nil, nil, make([]int, 0), ``, 0, 0, 0.000, nil, nil} check(t, pass, 0) check(t, fail, 9) @@ -52,9 +52,9 @@ func TestRulePrefixSuffix(t *testing.T) { B string `validate:"suffix=@"` } - pass := s{`#a`, `a@`} + var pass = s{`#a`, `a@`} - fail := s{`a#`, `@a`} + var fail = s{`a#`, `@a`} check(t, pass, 0) check(t, fail, 2) @@ -65,12 +65,12 @@ func TestRuleContains(t *testing.T) { A string `validate:"contains=%"` } - pass1 := s{`a%`} - pass2 := s{`%a`} - pass3 := s{`%`} - pass4 := s{`a%a`} + var pass1 = s{`a%`} + var pass2 = s{`%a`} + var pass3 = s{`%`} + var pass4 = s{`a%a`} - fail := s{`aa`} + var fail = s{`aa`} check(t, pass1, 0) check(t, pass2, 0) @@ -84,11 +84,11 @@ func TestRuleRegexp(t *testing.T) { A string `validate:"regexp=^[0-9]$"` } - pass1 := s{`0`} - pass2 := s{`7`} + var pass1 = s{`0`} + var pass2 = s{`7`} - fail1 := s{`A`} - fail2 := s{`11`} + var fail1 = s{`A`} + var fail2 = s{`11`} check(t, pass1, 0) check(t, pass2, 0) @@ -103,46 +103,16 @@ func TestRuleEqGtLt(t *testing.T) { C uint `validate:"lt=1"` } - pass := s{3, 100001, 0} + var pass = s{3, 100001, 0} - fail1 := s{2, 1e5, 1} - fail2 := s{4, 9999, 2} + var fail1 = s{2, 1e5, 1} + var fail2 = s{4, 9999, 2} check(t, pass, 0) check(t, fail1, 3) check(t, fail2, 3) } -func TestRuleGteLte(t *testing.T) { - type s struct { - U uint `validate:"gte=0,lte=10"` - I int `validate:"gte=-10,lte=0"` - F float64 `validate:"gte=0,lte=10"` - } - - pass1 := s{0, -10, 0} - pass2 := s{10, 0, 10} - - // Uint - fail1 := s{11, -10, 0} - - // Int - fail2 := s{0, -11, 0} - fail3 := s{0, 1, 0} - - // Float - fail4 := s{0, -10, -0.0001} - fail5 := s{0, -10, 10.0001} - - check(t, pass1, 0) - check(t, pass2, 0) - check(t, fail1, 1) - check(t, fail2, 1) - check(t, fail3, 1) - check(t, fail4, 1) - check(t, fail5, 1) -} - func TestLenMinMax(t *testing.T) { type s struct { A string `validate:"len=3"` @@ -150,10 +120,10 @@ func TestLenMinMax(t *testing.T) { C map[int]string `validate:"max=1"` } - pass := s{`abc`, []int{1, 2}, nil} + var pass = s{`abc`, []int{1, 2}, nil} - fail1 := s{`ab`, []int{1}, map[int]string{1: `a`, 2: `b`}} - fail2 := s{`abcd`, nil, nil} + var fail1 = s{`ab`, []int{1}, map[int]string{1: `a`, 2: `b`}} + var fail2 = s{`abcd`, nil, nil} check(t, pass, 0) check(t, fail1, 3) @@ -161,7 +131,6 @@ func TestLenMinMax(t *testing.T) { } func check(t *testing.T, c interface{}, errCount int) { - t.Helper() errs := Validate(c) if len(errs) != errCount { t.Errorf(`Case %T(%v) should get %d errors, but got %v`, c, c, errCount, errs) diff --git a/validate.go b/validate.go index 59216c8..f1cc1aa 100644 --- a/validate.go +++ b/validate.go @@ -7,12 +7,6 @@ import ( "sync" ) -type ValidateValuer interface { - ValidateValue() interface{} -} - -var vvType = reflect.TypeOf((*ValidateValuer)(nil)).Elem() - // Validate validates a variable func Validate(v interface{}) []ValidationError { rv := reflect.ValueOf(v) @@ -165,24 +159,12 @@ func getRules(rt reflect.Type) []rule { for i := 0; i < rt.NumField(); i++ { ft := rt.Field(i) - - var valuer bool - if ft.Type.Implements(vvType) { - ft.Type = reflect.TypeOf(reflect.New(ft.Type).Interface().(ValidateValuer).ValidateValue()) - valuer = true - } - kind := simplifyKind(ft.Type.Kind()) - var ptr bool - if kind == reflect.Ptr { - kind = ft.Type.Elem().Kind() - ptr = true - } - kind = simplifyKind(kind) - tags := strings.Split(ft.Tag.Get(`validate`), `,`) - rules = append(rules, getTagFuncs(i, ft, kind, tags, ptr, valuer)...) + rules = append(rules, getTagFuncs(i, ft, kind, tags)...) + + // TODO: Add validator interface switch kind { case reflect.Slice, reflect.Struct, reflect.Interface: @@ -203,7 +185,7 @@ func nest(ft reflect.StructField) func(reflect.Value) ([]ValidationError, bool) } } -func getTagFuncs(i int, ft reflect.StructField, kind reflect.Kind, tags []string, ptr, valuer bool) []rule { +func getTagFuncs(i int, ft reflect.StructField, kind reflect.Kind, tags []string) []rule { var rules []rule for _, v := range tags { if v == `` { @@ -216,42 +198,22 @@ func getTagFuncs(i int, ft reflect.StructField, kind reflect.Kind, tags []string value = parts[1] } - kind := kind - ptr := ptr - if ptr && (tag == `optional` || strings.TrimPrefix(tag, `!`) == `required`) { - kind = reflect.Ptr - ptr = false - } - - var f validateCheck - if tag == `optional` { - f = func(rv reflect.Value) ([]ValidationError, bool) { + rules = append(rules, rule{i, func(rv reflect.Value) ([]ValidationError, bool) { check, _ := getTagFunc(`required`, ``, kind) return nil, check(rv, nil) - } - } else { - var not bool - if strings.HasPrefix(tag, `!`) { - not = true - } - - check, val := getTagFunc(strings.TrimPrefix(tag, `!`), value, kind) - - f = func(rv reflect.Value) ([]ValidationError, bool) { - if check(rv, val) == !not { - return nil, true - } - - return []ValidationError{{Field: []Field{{nil, &ft}}, Check: tag, Value: value}}, false - } + }}) + continue } - if ptr { - f = depointerFunc(f, ft, tag, value) - } - if valuer { - f = valuerFunc(f) + check, val := getTagFunc(tag, value, kind) + + f := func(rv reflect.Value) ([]ValidationError, bool) { + if check(rv, val) { + return nil, true + } + + return []ValidationError{{Field: []Field{{nil, &ft}}, Check: tag, Value: value}}, false } rules = append(rules, rule{i, f}) @@ -260,25 +222,6 @@ func getTagFuncs(i int, ft reflect.StructField, kind reflect.Kind, tags []string return rules } -type validateCheck = func(rv reflect.Value) ([]ValidationError, bool) - -func depointerFunc(f validateCheck, ft reflect.StructField, tag, value string) validateCheck { - return func(rv reflect.Value) ([]ValidationError, bool) { - if rv.IsNil() { - return []ValidationError{{Field: []Field{{nil, &ft}}, Check: tag, Value: value}}, false - } - - return f(rv.Elem()) - } -} - -func valuerFunc(f validateCheck) validateCheck { - return func(rv reflect.Value) ([]ValidationError, bool) { - rv = reflect.ValueOf(rv.Interface().(ValidateValuer).ValidateValue()) - return f(rv) - } -} - func getTagFunc(tag, value string, kind reflect.Kind) (ValidationFunc, interface{}) { tagInfo, ok := funcs[tag] if !ok { diff --git a/validate_test.go b/validate_test.go index 96a83b4..035b0b9 100644 --- a/validate_test.go +++ b/validate_test.go @@ -10,10 +10,10 @@ func TestOptionalMultiple(t *testing.T) { B int `validate:"gt=3,lt=20"` } - pass1 := s{``, 4} - pass2 := s{`a`, 19} + var pass1 = s{``, 4} + var pass2 = s{`a`, 19} - fail := s{`b`, 3} + var fail = s{`b`, 3} check(t, pass1, 0) check(t, pass2, 0) @@ -34,11 +34,11 @@ func TestNesting(t *testing.T) { B sb } - pass := s{[]sa{{`abc`}}, sb{12}} + var pass = s{[]sa{{`abc`}}, sb{12}} - fail1 := s{nil, sb{12}} - fail2 := s{[]sa{{``}}, sb{12}} - fail3 := s{[]sa{{``}}, sb{9}} + var fail1 = s{nil, sb{12}} + var fail2 = s{[]sa{{``}}, sb{12}} + var fail3 = s{[]sa{{``}}, sb{9}} check(t, pass, 0) check(t, fail1, 1) @@ -76,78 +76,3 @@ func TestValidationErrorField(t *testing.T) { t.Fatal(`Expected errors to be A.B.C[0].D and A.B.C[2].D; got`, errs[0].Field, `and`, errs[1].Field) } } - -func TestNot(t *testing.T) { - type s struct { - A string `validate:"!len=3"` - B int `validate:"!eq=3"` - } - - pass1 := s{`ab`, 2} - pass2 := s{`abcd`, 4} - - fail := s{`abc`, 3} - - check(t, pass1, 0) - check(t, pass2, 0) - check(t, fail, 2) - - errs := Validate(fail) - if errs[0].Check != `!len` || errs[1].Check != `!eq` { - t.Errorf(`Checknames missing !, got "%s" and "%s"`, errs[0].Check, errs[1].Check) - } -} - -func TestPtr(t *testing.T) { - type s struct { - A *int `validate:"eq=3"` - B *int `validate:"optional,eq=3"` - } - - two := 2 - three := 3 - - pass1 := s{&three, &three} - pass2 := s{&three, nil} - - fail1 := s{&two, &two} - fail2 := s{nil, nil} - - check(t, pass1, 0) - check(t, pass2, 0) - check(t, fail1, 2) - check(t, fail2, 1) -} - -type val struct { - Int int - Valid bool -} - -func (v val) ValidateValue() interface{} { - if !v.Valid { - return (*int)(nil) - } - - return &v.Int -} - -func TestValidateValuer(t *testing.T) { - type s struct { - A val `validate:"required,lt=2"` - B val `validate:"optional,eq=3"` - } - - pass1 := s{val{Valid: true}, val{}} - pass2 := s{val{Valid: true}, val{Int: 3, Valid: true}} - - fail1 := s{} - fail2 := s{val{Int: 2, Valid: true}, val{}} - fail3 := s{val{Valid: true}, val{Int: 2, Valid: true}} - - check(t, pass1, 0) - check(t, pass2, 0) - check(t, fail1, 1) - check(t, fail2, 1) - check(t, fail3, 1) -}