From 8ae3903dd38edd82b65674bdb19d5e76805a294c Mon Sep 17 00:00:00 2001 From: James Service Date: Fri, 4 May 2018 13:20:05 +0100 Subject: [PATCH 1/3] Extend the unique tag to also cover map values. --- baked_in.go | 33 ++++++++++++++++++++------------- validator_test.go | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/baked_in.go b/baked_in.go index 3c83b73..755bf19 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1,7 +1,9 @@ package validator import ( + "bytes" "context" + "crypto/sha256" "fmt" "net" "net/url" @@ -11,8 +13,6 @@ import ( "sync" "time" "unicode/utf8" - "crypto/sha256" - "bytes" ) // Func accepts a FieldLevel interface for all validation needs. The return @@ -187,7 +187,7 @@ func isOneOf(fl FieldLevel) bool { return false } -// isUnique is the validation function for validating if each array|slice element is unique +// isUnique is the validation function for validating if each array|slice|map value is unique func isUnique(fl FieldLevel) bool { field := fl.Field() @@ -195,12 +195,19 @@ func isUnique(fl FieldLevel) bool { switch field.Kind() { case reflect.Slice, reflect.Array: - m := reflect.MakeMap(reflect.MapOf(fl.Field().Type().Elem(), v.Type())) + 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() + case reflect.Map: + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for _, k := range field.MapKeys() { + m.SetMapIndex(field.MapIndex(k), v) + } + return field.Len() == m.Len() default: panic(fmt.Sprintf("Bad field type %T", field.Interface())) } @@ -451,13 +458,13 @@ func isBitcoinAddress(fl FieldLevel) bool { func isBitcoinBech32Address(fl FieldLevel) bool { address := fl.Field().String() - if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address){ + if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { return false } am := len(address) % 8 - if am == 0 || am == 3 || am == 5{ + if am == 0 || am == 3 || am == 5 { return false } @@ -474,7 +481,7 @@ func isBitcoinBech32Address(fl FieldLevel) bool { ver := dp[0] - if ver < 0 || ver > 16{ + if ver < 0 || ver > 16 { return false } @@ -486,16 +493,16 @@ func isBitcoinBech32Address(fl FieldLevel) bool { values := append(hr, dp...) - GEN := []int{ 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3 } + GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} p := 1 for _, v := range values { b := p >> 25 - p = (p & 0x1ffffff) << 5 ^ v + p = (p&0x1ffffff)<<5 ^ v for i := 0; i < 5; i++ { - if (b >> uint(i)) & 1 == 1 { + if (b>>uint(i))&1 == 1 { p ^= GEN[i] } } @@ -510,16 +517,16 @@ func isBitcoinBech32Address(fl FieldLevel) bool { mv := (1 << 5) - 1 sw := []int{} - for _, v := range dp[1:len(dp) - 6]{ + for _, v := range dp[1 : len(dp)-6] { acc = (acc << 5) | v b += 5 - for b >= 8{ + for b >= 8 { b -= 8 sw = append(sw, (acc>>b)&mv) } } - if len(sw) < 2 || len(sw) > 40{ + if len(sw) < 2 || len(sw) > 40 { return false } diff --git a/validator_test.go b/validator_test.go index 2be460c..fd80b8d 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7696,6 +7696,18 @@ func TestUniqueValidation(t *testing.T) { param interface{} expected bool }{ + // Arrays + {[2]string{"a", "b"}, true}, + {[2]int{1, 2}, true}, + {[2]float64{1, 2}, true}, + {[2]interface{}{"a", "b"}, true}, + {[2]interface{}{"a", 1}, true}, + {[2]float64{1, 1}, false}, + {[2]int{1, 1}, false}, + {[2]string{"a", "a"}, false}, + {[2]interface{}{"a", "a"}, false}, + {[4]interface{}{"a", 1, "b", 1}, false}, + // Slices {[]string{"a", "b"}, true}, {[]int{1, 2}, true}, {[]float64{1, 2}, true}, @@ -7706,6 +7718,17 @@ func TestUniqueValidation(t *testing.T) { {[]string{"a", "a"}, false}, {[]interface{}{"a", "a"}, false}, {[]interface{}{"a", 1, "b", 1}, false}, + // Maps + {map[string]string{"one": "a", "two": "b"}, true}, + {map[string]int{"one": 1, "two": 2}, true}, + {map[string]float64{"one": 1, "two": 2}, true}, + {map[string]interface{}{"one": "a", "two": "b"}, true}, + {map[string]interface{}{"one": "a", "two": 1}, true}, + {map[string]float64{"one": 1, "two": 1}, false}, + {map[string]int{"one": 1, "two": 1}, false}, + {map[string]string{"one": "a", "two": "a"}, false}, + {map[string]interface{}{"one": "a", "two": "a"}, false}, + {map[string]interface{}{"one": "a", "two": 1, "three": "b", "four": 1}, false}, } validate := New() From fc5fbce7063ef79ce400c2028d1e478350101f28 Mon Sep 17 00:00:00 2001 From: James Service Date: Fri, 4 May 2018 13:23:26 +0100 Subject: [PATCH 2/3] Add new unique functionality to docstring. --- doc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/doc.go b/doc.go index e8ef618..602fdc8 100644 --- a/doc.go +++ b/doc.go @@ -506,6 +506,7 @@ to the top level struct. Unique For arrays & slices, unique will ensure that there are no duplicates. +For maps, unique will ensure that there are no duplicate values. Usage: unique From d978a4b4218b1728b8f726ca948ac2d6da3ee467 Mon Sep 17 00:00:00 2001 From: James Service Date: Fri, 4 May 2018 13:34:51 +0100 Subject: [PATCH 3/3] Undo whitespace changes from gofmt. --- baked_in.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/baked_in.go b/baked_in.go index 755bf19..d3730ca 100644 --- a/baked_in.go +++ b/baked_in.go @@ -458,13 +458,13 @@ func isBitcoinAddress(fl FieldLevel) bool { func isBitcoinBech32Address(fl FieldLevel) bool { address := fl.Field().String() - if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { + if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address){ return false } am := len(address) % 8 - if am == 0 || am == 3 || am == 5 { + if am == 0 || am == 3 || am == 5{ return false } @@ -481,7 +481,7 @@ func isBitcoinBech32Address(fl FieldLevel) bool { ver := dp[0] - if ver < 0 || ver > 16 { + if ver < 0 || ver > 16{ return false } @@ -493,16 +493,16 @@ func isBitcoinBech32Address(fl FieldLevel) bool { values := append(hr, dp...) - GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + GEN := []int{ 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3 } p := 1 for _, v := range values { b := p >> 25 - p = (p&0x1ffffff)<<5 ^ v + p = (p & 0x1ffffff) << 5 ^ v for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { + if (b >> uint(i)) & 1 == 1 { p ^= GEN[i] } } @@ -517,16 +517,16 @@ func isBitcoinBech32Address(fl FieldLevel) bool { mv := (1 << 5) - 1 sw := []int{} - for _, v := range dp[1 : len(dp)-6] { + for _, v := range dp[1:len(dp) - 6]{ acc = (acc << 5) | v b += 5 - for b >= 8 { + for b >= 8{ b -= 8 sw = append(sw, (acc>>b)&mv) } } - if len(sw) < 2 || len(sw) > 40 { + if len(sw) < 2 || len(sw) > 40{ return false }