Merge pull request #644 from longit644/bugfix/make-unique-tag-work-with-pointer-fields

Make unique tag work with pointer fields.
pull/671/head
Dean Karn 4 years ago committed by GitHub
commit 57b4fab34b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      baked_in.go
  2. 67
      validator_test.go

@ -247,23 +247,33 @@ func isUnique(fl FieldLevel) bool {
switch field.Kind() { switch field.Kind() {
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
elem := field.Type().Elem()
if elem.Kind() == reflect.Ptr {
elem = elem.Elem()
}
if param == "" { if param == "" {
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
for i := 0; i < field.Len(); i++ { for i := 0; i < field.Len(); i++ {
m.SetMapIndex(field.Index(i), v) m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
} }
return field.Len() == m.Len() return field.Len() == m.Len()
} }
sf, ok := field.Type().Elem().FieldByName(param) sf, ok := elem.FieldByName(param)
if !ok { if !ok {
panic(fmt.Sprintf("Bad field name %s", param)) panic(fmt.Sprintf("Bad field name %s", param))
} }
m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type())) sfTyp := sf.Type
if sfTyp.Kind() == reflect.Ptr {
sfTyp = sfTyp.Elem()
}
m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
for i := 0; i < field.Len(); i++ { for i := 0; i < field.Len(); i++ {
m.SetMapIndex(field.Index(i).FieldByName(param), v) m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v)
} }
return field.Len() == m.Len() return field.Len() == m.Len()
case reflect.Map: case reflect.Map:

@ -346,6 +346,18 @@ func StructLevelInvalidError(sl StructLevel) {
} }
} }
func stringPtr(v string) *string {
return &v
}
func intPtr(v int) *int {
return &v
}
func float64Ptr(v float64) *float64 {
return &v
}
func TestStructLevelInvalidError(t *testing.T) { func TestStructLevelInvalidError(t *testing.T) {
validate := New() validate := New()
@ -8144,6 +8156,12 @@ func TestUniqueValidation(t *testing.T) {
{[2]string{"a", "a"}, false}, {[2]string{"a", "a"}, false},
{[2]interface{}{"a", "a"}, false}, {[2]interface{}{"a", "a"}, false},
{[4]interface{}{"a", 1, "b", 1}, false}, {[4]interface{}{"a", 1, "b", 1}, false},
{[2]*string{stringPtr("a"), stringPtr("b")}, true},
{[2]*int{intPtr(1), intPtr(2)}, true},
{[2]*float64{float64Ptr(1), float64Ptr(2)}, true},
{[2]*string{stringPtr("a"), stringPtr("a")}, false},
{[2]*float64{float64Ptr(1), float64Ptr(1)}, false},
{[2]*int{intPtr(1), intPtr(1)}, false},
// Slices // Slices
{[]string{"a", "b"}, true}, {[]string{"a", "b"}, true},
{[]int{1, 2}, true}, {[]int{1, 2}, true},
@ -8155,6 +8173,12 @@ func TestUniqueValidation(t *testing.T) {
{[]string{"a", "a"}, false}, {[]string{"a", "a"}, false},
{[]interface{}{"a", "a"}, false}, {[]interface{}{"a", "a"}, false},
{[]interface{}{"a", 1, "b", 1}, false}, {[]interface{}{"a", 1, "b", 1}, false},
{[]*string{stringPtr("a"), stringPtr("b")}, true},
{[]*int{intPtr(1), intPtr(2)}, true},
{[]*float64{float64Ptr(1), float64Ptr(2)}, true},
{[]*string{stringPtr("a"), stringPtr("a")}, false},
{[]*float64{float64Ptr(1), float64Ptr(1)}, false},
{[]*int{intPtr(1), intPtr(1)}, false},
// Maps // Maps
{map[string]string{"one": "a", "two": "b"}, true}, {map[string]string{"one": "a", "two": "b"}, true},
{map[string]int{"one": 1, "two": 2}, true}, {map[string]int{"one": 1, "two": 2}, true},
@ -8235,6 +8259,49 @@ func TestUniqueValidationStructSlice(t *testing.T) {
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C") PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
} }
func TestUniqueValidationStructPtrSlice(t *testing.T) {
testStructs := []*struct {
A *string
B *string
}{
{A: stringPtr("one"), B: stringPtr("two")},
{A: stringPtr("one"), B: stringPtr("three")},
}
tests := []struct {
target interface{}
param string
expected bool
}{
{testStructs, "unique", true},
{testStructs, "unique=A", false},
{testStructs, "unique=B", true},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.target, test.param)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "unique" {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
}
}
}
}
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
}
func TestHTMLValidation(t *testing.T) { func TestHTMLValidation(t *testing.T) {
tests := []struct { tests := []struct {
param string param string

Loading…
Cancel
Save