Merge pull request #496 from shihanng/unique_struct_field

Extending unique to ensure uniqueness of a specific field in a slices of struct
v9
Dean Karn 5 years ago committed by GitHub
commit 6720e73db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      baked_in.go
  2. 6
      doc.go
  3. 43
      validator_test.go

@ -225,16 +225,30 @@ func isOneOf(fl FieldLevel) bool {
func isUnique(fl FieldLevel) bool { func isUnique(fl FieldLevel) bool {
field := fl.Field() field := fl.Field()
param := fl.Param()
v := reflect.ValueOf(struct{}{}) v := reflect.ValueOf(struct{}{})
switch field.Kind() { switch field.Kind() {
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if param == "" {
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) m := reflect.MakeMap(reflect.MapOf(field.Type().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(field.Index(i), v)
} }
return field.Len() == m.Len() 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).FieldByName(param), v)
}
return field.Len() == m.Len()
case reflect.Map: case reflect.Map:
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))

@ -585,9 +585,15 @@ Unique
For arrays & slices, unique will ensure that there are no duplicates. For arrays & slices, unique will ensure that there are no duplicates.
For maps, unique will ensure that there are no duplicate values. 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 Usage: unique
// For slices of struct:
Usage: unique=field
Alpha Only Alpha Only
This validates that a string value contains ASCII alpha characters only This validates that a string value contains ASCII alpha characters only

@ -8160,6 +8160,49 @@ func TestUniqueValidation(t *testing.T) {
PanicMatches(t, func() { _ = validate.Var(1.0, "unique") }, "Bad field type float64") 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) { func TestHTMLValidation(t *testing.T) {
tests := []struct { tests := []struct {
param string param string

Loading…
Cancel
Save