From d04a036fb5982f260c62ad949723d193d4e0722a Mon Sep 17 00:00:00 2001 From: Shi Han NG Date: Fri, 2 Aug 2019 17:11:23 +0900 Subject: [PATCH 1/2] Implement unique=FieldName --- baked_in.go | 18 ++++++++++++++++-- validator_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/baked_in.go b/baked_in.go index 338cddd..fcfea88 100644 --- a/baked_in.go +++ b/baked_in.go @@ -224,14 +224,28 @@ func isOneOf(fl FieldLevel) bool { func isUnique(fl FieldLevel) bool { field := fl.Field() + param := fl.Param() v := reflect.ValueOf(struct{}{}) switch field.Kind() { case reflect.Slice, reflect.Array: - m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + if param == "" { + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(field.Index(i), v) + } + return field.Len() == m.Len() + } + + sf, ok := field.Type().Elem().FieldByName(param) + if !ok { + panic(fmt.Sprintf("Bad field name %s", param)) + } + m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type())) for i := 0; i < field.Len(); i++ { - m.SetMapIndex(field.Index(i), v) + m.SetMapIndex(field.Index(i).FieldByName(param), v) } return field.Len() == m.Len() case reflect.Map: diff --git a/validator_test.go b/validator_test.go index a8ccfaa..11fb375 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8160,6 +8160,49 @@ func TestUniqueValidation(t *testing.T) { PanicMatches(t, func() { validate.Var(1.0, "unique") }, "Bad field type float64") } +func TestUniqueValidationStructSlice(t *testing.T) { + testStructs := []struct { + A string + B string + }{ + {A: "one", B: "two"}, + {A: "one", B: "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) { tests := []struct { param string From 8efe08867819de4bc3c0cee8f1d067dfaf4f6f0e Mon Sep 17 00:00:00 2001 From: Shi Han NG Date: Fri, 2 Aug 2019 20:42:47 +0900 Subject: [PATCH 2/2] Update doc for unique=field --- doc.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc.go b/doc.go index e0396cb..3161163 100644 --- a/doc.go +++ b/doc.go @@ -585,9 +585,15 @@ Unique For arrays & slices, unique will ensure that there are no duplicates. For maps, unique will ensure that there are no duplicate values. +For slices of struct, unique will ensure that there are no duplicate values +in a field of the struct specified via a parameter. + // For arrays, slices, and maps: Usage: unique + // For slices of struct: + Usage: unique=field + Alpha Only This validates that a string value contains ASCII alpha characters only