Skip to content

Commit 57b4fab

Browse files
author
Dean Karn
authored
Merge pull request #644 from longit644/bugfix/make-unique-tag-work-with-pointer-fields
Make unique tag work with pointer fields.
2 parents d9e95d5 + 67c4fdf commit 57b4fab

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

baked_in.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,23 +247,33 @@ func isUnique(fl FieldLevel) bool {
247247

248248
switch field.Kind() {
249249
case reflect.Slice, reflect.Array:
250+
elem := field.Type().Elem()
251+
if elem.Kind() == reflect.Ptr {
252+
elem = elem.Elem()
253+
}
254+
250255
if param == "" {
251-
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
256+
m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
252257

253258
for i := 0; i < field.Len(); i++ {
254-
m.SetMapIndex(field.Index(i), v)
259+
m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
255260
}
256261
return field.Len() == m.Len()
257262
}
258263

259-
sf, ok := field.Type().Elem().FieldByName(param)
264+
sf, ok := elem.FieldByName(param)
260265
if !ok {
261266
panic(fmt.Sprintf("Bad field name %s", param))
262267
}
263268

264-
m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type()))
269+
sfTyp := sf.Type
270+
if sfTyp.Kind() == reflect.Ptr {
271+
sfTyp = sfTyp.Elem()
272+
}
273+
274+
m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
265275
for i := 0; i < field.Len(); i++ {
266-
m.SetMapIndex(field.Index(i).FieldByName(param), v)
276+
m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v)
267277
}
268278
return field.Len() == m.Len()
269279
case reflect.Map:

validator_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,18 @@ func StructLevelInvalidError(sl StructLevel) {
346346
}
347347
}
348348

349+
func stringPtr(v string) *string {
350+
return &v
351+
}
352+
353+
func intPtr(v int) *int {
354+
return &v
355+
}
356+
357+
func float64Ptr(v float64) *float64 {
358+
return &v
359+
}
360+
349361
func TestStructLevelInvalidError(t *testing.T) {
350362

351363
validate := New()
@@ -8144,6 +8156,12 @@ func TestUniqueValidation(t *testing.T) {
81448156
{[2]string{"a", "a"}, false},
81458157
{[2]interface{}{"a", "a"}, false},
81468158
{[4]interface{}{"a", 1, "b", 1}, false},
8159+
{[2]*string{stringPtr("a"), stringPtr("b")}, true},
8160+
{[2]*int{intPtr(1), intPtr(2)}, true},
8161+
{[2]*float64{float64Ptr(1), float64Ptr(2)}, true},
8162+
{[2]*string{stringPtr("a"), stringPtr("a")}, false},
8163+
{[2]*float64{float64Ptr(1), float64Ptr(1)}, false},
8164+
{[2]*int{intPtr(1), intPtr(1)}, false},
81478165
// Slices
81488166
{[]string{"a", "b"}, true},
81498167
{[]int{1, 2}, true},
@@ -8155,6 +8173,12 @@ func TestUniqueValidation(t *testing.T) {
81558173
{[]string{"a", "a"}, false},
81568174
{[]interface{}{"a", "a"}, false},
81578175
{[]interface{}{"a", 1, "b", 1}, false},
8176+
{[]*string{stringPtr("a"), stringPtr("b")}, true},
8177+
{[]*int{intPtr(1), intPtr(2)}, true},
8178+
{[]*float64{float64Ptr(1), float64Ptr(2)}, true},
8179+
{[]*string{stringPtr("a"), stringPtr("a")}, false},
8180+
{[]*float64{float64Ptr(1), float64Ptr(1)}, false},
8181+
{[]*int{intPtr(1), intPtr(1)}, false},
81588182
// Maps
81598183
{map[string]string{"one": "a", "two": "b"}, true},
81608184
{map[string]int{"one": 1, "two": 2}, true},
@@ -8235,6 +8259,49 @@ func TestUniqueValidationStructSlice(t *testing.T) {
82358259
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
82368260
}
82378261

8262+
func TestUniqueValidationStructPtrSlice(t *testing.T) {
8263+
testStructs := []*struct {
8264+
A *string
8265+
B *string
8266+
}{
8267+
{A: stringPtr("one"), B: stringPtr("two")},
8268+
{A: stringPtr("one"), B: stringPtr("three")},
8269+
}
8270+
8271+
tests := []struct {
8272+
target interface{}
8273+
param string
8274+
expected bool
8275+
}{
8276+
{testStructs, "unique", true},
8277+
{testStructs, "unique=A", false},
8278+
{testStructs, "unique=B", true},
8279+
}
8280+
8281+
validate := New()
8282+
8283+
for i, test := range tests {
8284+
8285+
errs := validate.Var(test.target, test.param)
8286+
8287+
if test.expected {
8288+
if !IsEqual(errs, nil) {
8289+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
8290+
}
8291+
} else {
8292+
if IsEqual(errs, nil) {
8293+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
8294+
} else {
8295+
val := getError(errs, "", "")
8296+
if val.Tag() != "unique" {
8297+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
8298+
}
8299+
}
8300+
}
8301+
}
8302+
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
8303+
}
8304+
82388305
func TestHTMLValidation(t *testing.T) {
82398306
tests := []struct {
82408307
param string

0 commit comments

Comments
 (0)