diff --git a/README.md b/README.md index 335012d..1a78353 100644 --- a/README.md +++ b/README.md @@ -65,61 +65,61 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa Benchmarks ------ -###### Run on Dell XPS 15 i7-7700HQ 32GB Go version go1.8.3 linux/amd64 +###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.1 linux/amd64 ```go -go test -run=XXX -bench=. -benchmem=true -BenchmarkFieldSuccess-8 20000000 88.3 ns/op 0 B/op 0 allocs/op -BenchmarkFieldSuccessParallel-8 50000000 30.4 ns/op 0 B/op 0 allocs/op -BenchmarkFieldFailure-8 3000000 428 ns/op 208 B/op 4 allocs/op -BenchmarkFieldFailureParallel-8 20000000 96.0 ns/op 208 B/op 4 allocs/op -BenchmarkFieldDiveSuccess-8 2000000 695 ns/op 201 B/op 11 allocs/op -BenchmarkFieldDiveSuccessParallel-8 10000000 205 ns/op 201 B/op 11 allocs/op -BenchmarkFieldDiveFailure-8 1000000 1083 ns/op 412 B/op 16 allocs/op -BenchmarkFieldDiveFailureParallel-8 5000000 278 ns/op 413 B/op 16 allocs/op -BenchmarkFieldCustomTypeSuccess-8 10000000 229 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeSuccessParallel-8 20000000 72.4 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeFailure-8 5000000 377 ns/op 208 B/op 4 allocs/op -BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.0 ns/op 208 B/op 4 allocs/op -BenchmarkFieldOrTagSuccess-8 2000000 767 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagSuccessParallel-8 3000000 425 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagFailure-8 2000000 548 ns/op 224 B/op 5 allocs/op -BenchmarkFieldOrTagFailureParallel-8 3000000 411 ns/op 224 B/op 5 allocs/op -BenchmarkStructLevelValidationSuccess-8 10000000 219 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationSuccessParallel-8 20000000 69.2 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationFailure-8 2000000 628 ns/op 304 B/op 8 allocs/op -BenchmarkStructLevelValidationFailureParallel-8 10000000 165 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCustomTypeSuccess-8 3000000 411 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 122 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeFailure-8 1000000 1022 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 228 ns/op 440 B/op 10 allocs/op -BenchmarkStructFilteredSuccess-8 2000000 737 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredSuccessParallel-8 10000000 192 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredFailure-8 3000000 583 ns/op 256 B/op 7 allocs/op -BenchmarkStructFilteredFailureParallel-8 10000000 152 ns/op 256 B/op 7 allocs/op -BenchmarkStructPartialSuccess-8 2000000 731 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialSuccessParallel-8 10000000 173 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialFailure-8 1000000 1164 ns/op 480 B/op 11 allocs/op -BenchmarkStructPartialFailureParallel-8 5000000 253 ns/op 480 B/op 11 allocs/op -BenchmarkStructExceptSuccess-8 1000000 1337 ns/op 496 B/op 12 allocs/op -BenchmarkStructExceptSuccessParallel-8 10000000 153 ns/op 240 B/op 5 allocs/op -BenchmarkStructExceptFailure-8 2000000 954 ns/op 464 B/op 10 allocs/op -BenchmarkStructExceptFailureParallel-8 5000000 234 ns/op 464 B/op 10 allocs/op -BenchmarkStructSimpleCrossFieldSuccess-8 3000000 420 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 125 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldFailure-8 2000000 790 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 205 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 611 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 172 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1112 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 258 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleSuccess-8 5000000 263 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleSuccessParallel-8 20000000 83.1 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleFailure-8 2000000 964 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleFailureParallel-8 10000000 212 ns/op 424 B/op 9 allocs/op -BenchmarkStructComplexSuccess-8 1000000 1504 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexFailure-8 300000 7585 ns/op 3041 B/op 53 allocs/op -BenchmarkStructComplexFailureParallel-8 1000000 1387 ns/op 3041 B/op 53 allocs/op +go test -bench=. -benchmem=true +BenchmarkFieldSuccess-8 20000000 87.2 ns/op 0 B/op 0 allocs/op +BenchmarkFieldSuccessParallel-8 50000000 26.1 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 5000000 299 ns/op 208 B/op 4 allocs/op +BenchmarkFieldFailureParallel-8 20000000 100 ns/op 208 B/op 4 allocs/op +BenchmarkFieldDiveSuccess-8 2000000 645 ns/op 201 B/op 11 allocs/op +BenchmarkFieldDiveSuccessParallel-8 10000000 198 ns/op 201 B/op 11 allocs/op +BenchmarkFieldDiveFailure-8 2000000 876 ns/op 412 B/op 16 allocs/op +BenchmarkFieldDiveFailureParallel-8 5000000 268 ns/op 413 B/op 16 allocs/op +BenchmarkFieldCustomTypeSuccess-8 10000000 228 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeSuccessParallel-8 20000000 70.0 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 5000000 286 ns/op 208 B/op 4 allocs/op +BenchmarkFieldCustomTypeFailureParallel-8 20000000 95.6 ns/op 208 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 2000000 857 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagSuccessParallel-8 3000000 397 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 3000000 495 ns/op 224 B/op 5 allocs/op +BenchmarkFieldOrTagFailureParallel-8 5000000 376 ns/op 224 B/op 5 allocs/op +BenchmarkStructLevelValidationSuccess-8 10000000 226 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationSuccessParallel-8 20000000 68.4 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 497 ns/op 304 B/op 8 allocs/op +BenchmarkStructLevelValidationFailureParallel-8 10000000 170 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 3000000 420 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 124 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 2000000 681 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 244 ns/op 440 B/op 10 allocs/op +BenchmarkStructFilteredSuccess-8 2000000 659 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredSuccessParallel-8 10000000 211 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredFailure-8 3000000 482 ns/op 256 B/op 7 allocs/op +BenchmarkStructFilteredFailureParallel-8 10000000 162 ns/op 256 B/op 7 allocs/op +BenchmarkStructPartialSuccess-8 3000000 564 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialSuccessParallel-8 10000000 180 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialFailure-8 2000000 779 ns/op 480 B/op 11 allocs/op +BenchmarkStructPartialFailureParallel-8 5000000 268 ns/op 480 B/op 11 allocs/op +BenchmarkStructExceptSuccess-8 2000000 879 ns/op 496 B/op 12 allocs/op +BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op +BenchmarkStructExceptFailure-8 2000000 734 ns/op 464 B/op 10 allocs/op +BenchmarkStructExceptFailureParallel-8 5000000 259 ns/op 464 B/op 10 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 3000000 432 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 129 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 2000000 671 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 229 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 628 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 182 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 872 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 267 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleSuccess-8 5000000 274 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleSuccessParallel-8 20000000 79.0 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleFailure-8 2000000 647 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleFailureParallel-8 10000000 224 ns/op 424 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 1000000 1557 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexSuccessParallel-8 3000000 473 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexFailure-8 300000 4373 ns/op 3041 B/op 53 allocs/op +BenchmarkStructComplexFailureParallel-8 1000000 1554 ns/op 3041 B/op 53 allocs/op ``` Complementary Software @@ -128,7 +128,7 @@ Complementary Software Here is a list of software that complements using this library either pre or post validation. * [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support. -* [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags. +* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects How to Contribute ------ diff --git a/baked_in.go b/baked_in.go index 5ac7311..af407d1 100644 --- a/baked_in.go +++ b/baked_in.go @@ -131,9 +131,29 @@ var ( "mac": isMAC, "hostname": isHostname, "fqdn": isFQDN, + "unique": isUnique, } ) +// isUnique is the validation function for validating if each array|slice element is unique +func isUnique(fl FieldLevel) bool { + + field := fl.Field() + v := reflect.ValueOf(struct{}{}) + + switch field.Kind() { + case reflect.Slice, reflect.Array: + m := reflect.MakeMap(reflect.MapOf(fl.Field().Type().Elem(), v.Type())) + + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(field.Index(i), v) + } + return field.Len() == m.Len() + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } +} + // IsMAC is the validation function for validating if the field's value is a valid MAC address. func isMAC(fl FieldLevel) bool { diff --git a/doc.go b/doc.go index 4945925..c9af494 100644 --- a/doc.go +++ b/doc.go @@ -468,6 +468,12 @@ to the top level struct. Usage: ltecsfield=InnerStructField.Field +Unique + +For arrays & slices, unique will ensure that there are no duplicates. + + Usage: unique + Alpha Only This validates that a string value contains ASCII alpha characters only diff --git a/validator_test.go b/validator_test.go index 6f57671..8c8d9bc 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7272,3 +7272,44 @@ func TestIsDefault(t *testing.T) { Equal(t, fe.Namespace(), "Test2.inner") Equal(t, fe.Tag(), "isdefault") } + +func TestUniqueValidation(t *testing.T) { + tests := []struct { + param interface{} + expected bool + }{ + {[]string{"a", "b"}, true}, + {[]int{1, 2}, true}, + {[]float64{1, 2}, true}, + {[]interface{}{"a", "b"}, true}, + {[]interface{}{"a", 1}, true}, + {[]float64{1, 1}, false}, + {[]int{1, 1}, false}, + {[]string{"a", "a"}, false}, + {[]interface{}{"a", "a"}, false}, + {[]interface{}{"a", 1, "b", 1}, false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "unique") + + 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(1.0, "unique") }, "Bad field type float64") +}