Skip to content

Commit 11e5ebe

Browse files
author
Dean Karn
authored
Merge pull request #317 from go-playground/add-unique
Add unique
2 parents 946d444 + 34b8495 commit 11e5ebe

File tree

4 files changed

+122
-55
lines changed

4 files changed

+122
-55
lines changed

README.md

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -65,61 +65,61 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa
6565

6666
Benchmarks
6767
------
68-
###### Run on Dell XPS 15 i7-7700HQ 32GB Go version go1.8.3 linux/amd64
68+
###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.1 linux/amd64
6969
```go
70-
go test -run=XXX -bench=. -benchmem=true
71-
BenchmarkFieldSuccess-8 20000000 88.3 ns/op 0 B/op 0 allocs/op
72-
BenchmarkFieldSuccessParallel-8 50000000 30.4 ns/op 0 B/op 0 allocs/op
73-
BenchmarkFieldFailure-8 3000000 428 ns/op 208 B/op 4 allocs/op
74-
BenchmarkFieldFailureParallel-8 20000000 96.0 ns/op 208 B/op 4 allocs/op
75-
BenchmarkFieldDiveSuccess-8 2000000 695 ns/op 201 B/op 11 allocs/op
76-
BenchmarkFieldDiveSuccessParallel-8 10000000 205 ns/op 201 B/op 11 allocs/op
77-
BenchmarkFieldDiveFailure-8 1000000 1083 ns/op 412 B/op 16 allocs/op
78-
BenchmarkFieldDiveFailureParallel-8 5000000 278 ns/op 413 B/op 16 allocs/op
79-
BenchmarkFieldCustomTypeSuccess-8 10000000 229 ns/op 32 B/op 2 allocs/op
80-
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 72.4 ns/op 32 B/op 2 allocs/op
81-
BenchmarkFieldCustomTypeFailure-8 5000000 377 ns/op 208 B/op 4 allocs/op
82-
BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.0 ns/op 208 B/op 4 allocs/op
83-
BenchmarkFieldOrTagSuccess-8 2000000 767 ns/op 16 B/op 1 allocs/op
84-
BenchmarkFieldOrTagSuccessParallel-8 3000000 425 ns/op 16 B/op 1 allocs/op
85-
BenchmarkFieldOrTagFailure-8 2000000 548 ns/op 224 B/op 5 allocs/op
86-
BenchmarkFieldOrTagFailureParallel-8 3000000 411 ns/op 224 B/op 5 allocs/op
87-
BenchmarkStructLevelValidationSuccess-8 10000000 219 ns/op 32 B/op 2 allocs/op
88-
BenchmarkStructLevelValidationSuccessParallel-8 20000000 69.2 ns/op 32 B/op 2 allocs/op
89-
BenchmarkStructLevelValidationFailure-8 2000000 628 ns/op 304 B/op 8 allocs/op
90-
BenchmarkStructLevelValidationFailureParallel-8 10000000 165 ns/op 304 B/op 8 allocs/op
91-
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 411 ns/op 32 B/op 2 allocs/op
92-
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 122 ns/op 32 B/op 2 allocs/op
93-
BenchmarkStructSimpleCustomTypeFailure-8 1000000 1022 ns/op 424 B/op 9 allocs/op
94-
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 228 ns/op 440 B/op 10 allocs/op
95-
BenchmarkStructFilteredSuccess-8 2000000 737 ns/op 288 B/op 9 allocs/op
96-
BenchmarkStructFilteredSuccessParallel-8 10000000 192 ns/op 288 B/op 9 allocs/op
97-
BenchmarkStructFilteredFailure-8 3000000 583 ns/op 256 B/op 7 allocs/op
98-
BenchmarkStructFilteredFailureParallel-8 10000000 152 ns/op 256 B/op 7 allocs/op
99-
BenchmarkStructPartialSuccess-8 2000000 731 ns/op 256 B/op 6 allocs/op
100-
BenchmarkStructPartialSuccessParallel-8 10000000 173 ns/op 256 B/op 6 allocs/op
101-
BenchmarkStructPartialFailure-8 1000000 1164 ns/op 480 B/op 11 allocs/op
102-
BenchmarkStructPartialFailureParallel-8 5000000 253 ns/op 480 B/op 11 allocs/op
103-
BenchmarkStructExceptSuccess-8 1000000 1337 ns/op 496 B/op 12 allocs/op
104-
BenchmarkStructExceptSuccessParallel-8 10000000 153 ns/op 240 B/op 5 allocs/op
105-
BenchmarkStructExceptFailure-8 2000000 954 ns/op 464 B/op 10 allocs/op
106-
BenchmarkStructExceptFailureParallel-8 5000000 234 ns/op 464 B/op 10 allocs/op
107-
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 420 ns/op 72 B/op 3 allocs/op
108-
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 125 ns/op 72 B/op 3 allocs/op
109-
BenchmarkStructSimpleCrossFieldFailure-8 2000000 790 ns/op 304 B/op 8 allocs/op
110-
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 205 ns/op 304 B/op 8 allocs/op
111-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 611 ns/op 80 B/op 4 allocs/op
112-
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 172 ns/op 80 B/op 4 allocs/op
113-
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1112 ns/op 320 B/op 9 allocs/op
114-
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 258 ns/op 320 B/op 9 allocs/op
115-
BenchmarkStructSimpleSuccess-8 5000000 263 ns/op 0 B/op 0 allocs/op
116-
BenchmarkStructSimpleSuccessParallel-8 20000000 83.1 ns/op 0 B/op 0 allocs/op
117-
BenchmarkStructSimpleFailure-8 2000000 964 ns/op 424 B/op 9 allocs/op
118-
BenchmarkStructSimpleFailureParallel-8 10000000 212 ns/op 424 B/op 9 allocs/op
119-
BenchmarkStructComplexSuccess-8 1000000 1504 ns/op 128 B/op 8 allocs/op
120-
BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op
121-
BenchmarkStructComplexFailure-8 300000 7585 ns/op 3041 B/op 53 allocs/op
122-
BenchmarkStructComplexFailureParallel-8 1000000 1387 ns/op 3041 B/op 53 allocs/op
70+
go test -bench=. -benchmem=true
71+
BenchmarkFieldSuccess-8 20000000 87.2 ns/op 0 B/op 0 allocs/op
72+
BenchmarkFieldSuccessParallel-8 50000000 26.1 ns/op 0 B/op 0 allocs/op
73+
BenchmarkFieldFailure-8 5000000 299 ns/op 208 B/op 4 allocs/op
74+
BenchmarkFieldFailureParallel-8 20000000 100 ns/op 208 B/op 4 allocs/op
75+
BenchmarkFieldDiveSuccess-8 2000000 645 ns/op 201 B/op 11 allocs/op
76+
BenchmarkFieldDiveSuccessParallel-8 10000000 198 ns/op 201 B/op 11 allocs/op
77+
BenchmarkFieldDiveFailure-8 2000000 876 ns/op 412 B/op 16 allocs/op
78+
BenchmarkFieldDiveFailureParallel-8 5000000 268 ns/op 413 B/op 16 allocs/op
79+
BenchmarkFieldCustomTypeSuccess-8 10000000 228 ns/op 32 B/op 2 allocs/op
80+
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 70.0 ns/op 32 B/op 2 allocs/op
81+
BenchmarkFieldCustomTypeFailure-8 5000000 286 ns/op 208 B/op 4 allocs/op
82+
BenchmarkFieldCustomTypeFailureParallel-8 20000000 95.6 ns/op 208 B/op 4 allocs/op
83+
BenchmarkFieldOrTagSuccess-8 2000000 857 ns/op 16 B/op 1 allocs/op
84+
BenchmarkFieldOrTagSuccessParallel-8 3000000 397 ns/op 16 B/op 1 allocs/op
85+
BenchmarkFieldOrTagFailure-8 3000000 495 ns/op 224 B/op 5 allocs/op
86+
BenchmarkFieldOrTagFailureParallel-8 5000000 376 ns/op 224 B/op 5 allocs/op
87+
BenchmarkStructLevelValidationSuccess-8 10000000 226 ns/op 32 B/op 2 allocs/op
88+
BenchmarkStructLevelValidationSuccessParallel-8 20000000 68.4 ns/op 32 B/op 2 allocs/op
89+
BenchmarkStructLevelValidationFailure-8 3000000 497 ns/op 304 B/op 8 allocs/op
90+
BenchmarkStructLevelValidationFailureParallel-8 10000000 170 ns/op 304 B/op 8 allocs/op
91+
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 420 ns/op 32 B/op 2 allocs/op
92+
BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 124 ns/op 32 B/op 2 allocs/op
93+
BenchmarkStructSimpleCustomTypeFailure-8 2000000 681 ns/op 424 B/op 9 allocs/op
94+
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 244 ns/op 440 B/op 10 allocs/op
95+
BenchmarkStructFilteredSuccess-8 2000000 659 ns/op 288 B/op 9 allocs/op
96+
BenchmarkStructFilteredSuccessParallel-8 10000000 211 ns/op 288 B/op 9 allocs/op
97+
BenchmarkStructFilteredFailure-8 3000000 482 ns/op 256 B/op 7 allocs/op
98+
BenchmarkStructFilteredFailureParallel-8 10000000 162 ns/op 256 B/op 7 allocs/op
99+
BenchmarkStructPartialSuccess-8 3000000 564 ns/op 256 B/op 6 allocs/op
100+
BenchmarkStructPartialSuccessParallel-8 10000000 180 ns/op 256 B/op 6 allocs/op
101+
BenchmarkStructPartialFailure-8 2000000 779 ns/op 480 B/op 11 allocs/op
102+
BenchmarkStructPartialFailureParallel-8 5000000 268 ns/op 480 B/op 11 allocs/op
103+
BenchmarkStructExceptSuccess-8 2000000 879 ns/op 496 B/op 12 allocs/op
104+
BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op
105+
BenchmarkStructExceptFailure-8 2000000 734 ns/op 464 B/op 10 allocs/op
106+
BenchmarkStructExceptFailureParallel-8 5000000 259 ns/op 464 B/op 10 allocs/op
107+
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 432 ns/op 72 B/op 3 allocs/op
108+
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 129 ns/op 72 B/op 3 allocs/op
109+
BenchmarkStructSimpleCrossFieldFailure-8 2000000 671 ns/op 304 B/op 8 allocs/op
110+
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 229 ns/op 304 B/op 8 allocs/op
111+
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 628 ns/op 80 B/op 4 allocs/op
112+
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 182 ns/op 80 B/op 4 allocs/op
113+
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 872 ns/op 320 B/op 9 allocs/op
114+
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 267 ns/op 320 B/op 9 allocs/op
115+
BenchmarkStructSimpleSuccess-8 5000000 274 ns/op 0 B/op 0 allocs/op
116+
BenchmarkStructSimpleSuccessParallel-8 20000000 79.0 ns/op 0 B/op 0 allocs/op
117+
BenchmarkStructSimpleFailure-8 2000000 647 ns/op 424 B/op 9 allocs/op
118+
BenchmarkStructSimpleFailureParallel-8 10000000 224 ns/op 424 B/op 9 allocs/op
119+
BenchmarkStructComplexSuccess-8 1000000 1557 ns/op 128 B/op 8 allocs/op
120+
BenchmarkStructComplexSuccessParallel-8 3000000 473 ns/op 128 B/op 8 allocs/op
121+
BenchmarkStructComplexFailure-8 300000 4373 ns/op 3041 B/op 53 allocs/op
122+
BenchmarkStructComplexFailureParallel-8 1000000 1554 ns/op 3041 B/op 53 allocs/op
123123
```
124124

125125
Complementary Software
@@ -128,7 +128,7 @@ Complementary Software
128128
Here is a list of software that complements using this library either pre or post validation.
129129

130130
* [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.
131-
* [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags.
131+
* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects
132132

133133
How to Contribute
134134
------

baked_in.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,29 @@ var (
131131
"mac": isMAC,
132132
"hostname": isHostname,
133133
"fqdn": isFQDN,
134+
"unique": isUnique,
134135
}
135136
)
136137

138+
// isUnique is the validation function for validating if each array|slice element is unique
139+
func isUnique(fl FieldLevel) bool {
140+
141+
field := fl.Field()
142+
v := reflect.ValueOf(struct{}{})
143+
144+
switch field.Kind() {
145+
case reflect.Slice, reflect.Array:
146+
m := reflect.MakeMap(reflect.MapOf(fl.Field().Type().Elem(), v.Type()))
147+
148+
for i := 0; i < field.Len(); i++ {
149+
m.SetMapIndex(field.Index(i), v)
150+
}
151+
return field.Len() == m.Len()
152+
default:
153+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
154+
}
155+
}
156+
137157
// IsMAC is the validation function for validating if the field's value is a valid MAC address.
138158
func isMAC(fl FieldLevel) bool {
139159

doc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,12 @@ to the top level struct.
468468
469469
Usage: ltecsfield=InnerStructField.Field
470470
471+
Unique
472+
473+
For arrays & slices, unique will ensure that there are no duplicates.
474+
475+
Usage: unique
476+
471477
Alpha Only
472478
473479
This validates that a string value contains ASCII alpha characters only

validator_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7272,3 +7272,44 @@ func TestIsDefault(t *testing.T) {
72727272
Equal(t, fe.Namespace(), "Test2.inner")
72737273
Equal(t, fe.Tag(), "isdefault")
72747274
}
7275+
7276+
func TestUniqueValidation(t *testing.T) {
7277+
tests := []struct {
7278+
param interface{}
7279+
expected bool
7280+
}{
7281+
{[]string{"a", "b"}, true},
7282+
{[]int{1, 2}, true},
7283+
{[]float64{1, 2}, true},
7284+
{[]interface{}{"a", "b"}, true},
7285+
{[]interface{}{"a", 1}, true},
7286+
{[]float64{1, 1}, false},
7287+
{[]int{1, 1}, false},
7288+
{[]string{"a", "a"}, false},
7289+
{[]interface{}{"a", "a"}, false},
7290+
{[]interface{}{"a", 1, "b", 1}, false},
7291+
}
7292+
7293+
validate := New()
7294+
7295+
for i, test := range tests {
7296+
7297+
errs := validate.Var(test.param, "unique")
7298+
7299+
if test.expected {
7300+
if !IsEqual(errs, nil) {
7301+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
7302+
}
7303+
} else {
7304+
if IsEqual(errs, nil) {
7305+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
7306+
} else {
7307+
val := getError(errs, "", "")
7308+
if val.Tag() != "unique" {
7309+
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
7310+
}
7311+
}
7312+
}
7313+
}
7314+
PanicMatches(t, func() { validate.Var(1.0, "unique") }, "Bad field type float64")
7315+
}

0 commit comments

Comments
 (0)