diff --git a/README.md b/README.md index 347e7b4..3527a48 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Send request to the server using curl or postman: `curl GET "http://localhost:90 * `not_in:foo,bar` The field under validation must have one value except foo,bar. e.g: `not_in:admin,manager,user` must not contain the values (admin or manager or user) * `email` The field under validation must have a valid email. * `float` The field under validation must have a valid float number. +* `mac_address` The field under validation must have be a valid Mac Address. * `min:numeric` The field under validation must have a min length of characters for string, items length for slice/map, value for integer or float. e.g: `min:3` may contains characters minimum length of 3 like `"john", "jane", "jane321"` but not `"mr", "xy"` * `max:numeric` The field under validation must have a max length of characters for string, items length for slice/map, value for integer or float. diff --git a/doc/BENCHMARK.md b/doc/BENCHMARK.md index ab378b4..144991f 100644 --- a/doc/BENCHMARK.md +++ b/doc/BENCHMARK.md @@ -1,36 +1,37 @@ Benchmarks =================== -Machine: Mac Book Pro-2015 2.7GHz 8GB -Go version: go1.8.1 darwin/amd64 +Machine: XPS 13 9370 (07E6) +Go version: go version go1.12.6 linux/amd64 -| ➜ go test -run=XXX -bench=. -benchmem=true | | | | | -|--------------------------------------------|-----------|------------|-----------|--------------| -| Benchmark_IsAlpha-4 | 5000000 | 323 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsAlphaDash-4 | 3000000 | 415 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsAlphaNumeric-4 | 5000000 | 338 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsBoolean-4 | 100000000 | 10.6 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsCreditCard-4 | 3000000 | 543 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsCoordinate-4 | 2000000 | 950 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsCSSColor-4 | 5000000 | 300 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsDate-4 | 2000000 | 719 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsDateDDMMYY-4 | 3000000 | 481 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsEmail-4 | 1000000 | 1172 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsFloat-4 | 3000000 | 432 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsIn-4 | 200000000 | 7.34 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsJSON-4 | 1000000 | 1595 ns/op | 768 B/op | 12 allocs/op | -| Benchmark_IsNumeric-4 | 10000000 | 195 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsLatitude-4 | 3000000 | 523 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsLongitude-4 | 3000000 | 516 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsIP-4 | 1000000 | 1073 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsIPV4-4 | 3000000 | 580 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsIPV6-4 | 1000000 | 1288 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsMatchedRegex-4 | 200000 | 7133 ns/op | 5400 B/op | 66 allocs/op | -| Benchmark_IsURL-4 | 1000000 | 1159 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsUUID-4 | 2000000 | 832 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsUUID3-4 | 2000000 | 783 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsUUID4-4 | 2000000 | 899 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_IsUUID5-4 | 2000000 | 828 ns/op | 0 B/op | 0 allocs/op | -| BenchmarkRoller_Start-4 | 200000 | 6869 ns/op | 2467 B/op | 28 allocs/op | -| Benchmark_isContainRequiredField-4 | 300000000 | 4.23 ns/op | 0 B/op | 0 allocs/op | -| Benchmark_Validate-4 | 200000 | 9347 ns/op | 664 B/op | 28 allocs/op | +| ➜ go test -run=XXX -bench=. -benchmem=true | | | | | +|--------------------------------------------|------------|--------------|--------------|--------------| +|Benchmark_IsAlpha-8 | 10000000 | 205 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsAlphaDash-8 | 5000000 | 268 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsAlphaNumeric-8 | 10000000 | 182 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsBoolean-8 | 200000000 | 6.84 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsCreditCard-8 | 10000000 | 243 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsCoordinate-8 | 3000000 | 482 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsCSSColor-8 | 10000000 | 160 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsDate-8 | 3000000 | 531 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsDateDDMMYY-8 | 5000000 | 246 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsEmail-8 | 3000000 | 549 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsFloat-8 | 10000000 | 199 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsIn-8 | 5000000 | 3.77 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsJSON-8 | 2000000 | 956 ns/op | 640 B/op | 12 allocs/op | +|Benchmark_IsMacAddress-8 | 5000000 | 277 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsNumeric-8 | 20000000 | 110 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsLatitude-8 | 5000000 | 249 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsLongitude-8 | 5000000 | 250 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsIP-8 | 3000000 | 578 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsIPV4-8 | 5000000 | 286 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsIPV6-8 | 2000000 | 931 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsMatchedRegex-8 | 200000 | 5786 ns/op | 4465 B/op | 57 allocs/op | +|Benchmark_IsURL-8 | 2000000 | 866 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsUUID-8 | 3000000 | 455 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsUUID3-8 | 3000000 | 536 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsUUID4-8 | 3000000 | 411 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_IsUUID5-8 | 3000000 | 443 ns/op | 0 B/op | 0 allocs/op | +|BenchmarkRoller_Start-8 | 300000 | 4659 ns/op | 2468 B/op | 28 allocs/op | +|Benchmark_isContainRequiredField-8 | 1000000000 | 2.69 ns/op | 0 B/op | 0 allocs/op | +|Benchmark_Validate-8 | 200000 | 6742 ns/op | 727 B/op | 29 allocs/op | diff --git a/helper.go b/helper.go index 9cb02b8..2780458 100644 --- a/helper.go +++ b/helper.go @@ -97,6 +97,11 @@ func isNumeric(str string) bool { return regexNumeric.MatchString(str) } +// isMacAddres check the provided string is valid Mac Address or not +func isMacAddress(str string) bool { + return regexMacAddress.MatchString(str) +} + // isLatitude check the provided input string is a valid latitude or not func isLatitude(str string) bool { return regexLatitude.MatchString(str) diff --git a/helper_test.go b/helper_test.go index 474194c..87b7137 100644 --- a/helper_test.go +++ b/helper_test.go @@ -81,6 +81,13 @@ var ( _roleList = []string{"admin", "manager", "supervisor"} _validJSONString = `{"FirstName": "Bob", "LastName": "Smith"}` _invalidJSONString = `{"invalid json"}` + _macaddressList = inputs{ + "fc:40:2e:f1:d3:6f": true, + "87:7a:45:f6:8b:ed": true, + "a5:91:91:80:d2:fd": true, + "1f:ce:44:46:24:b4": true, + "00:02:x2:34:72:a5": false, + } _numericStringList = inputs{"12": true, "09": true, "878": true, "100": true, "a": false, "xyz": false, "1000000000000": true} _latList = inputs{"30.297018": true, "40.044438": true, "a": false, "xyz": false} _lonList = inputs{"-78.486328": true, "-104.0625": true, "a": false, "xyz": false} @@ -301,6 +308,20 @@ func Benchmark_IsJSON(b *testing.B) { } } +func Test_IsMacAddress(t *testing.T) { + for n, s := range _macaddressList { + if isMacAddress(n) != s { + t.Error("IsMacAddress failed!") + } + } +} + +func Benchmark_IsMacAddress(b *testing.B) { + for n := 0; n < b.N; n++ { + isMacAddress("00:02:02:34:72:a5") + } +} + func Test_IsNumeric(t *testing.T) { for n, s := range _numericStringList { if isNumeric(n) != s { diff --git a/regex_patterns.go b/regex_patterns.go index cffd92d..7db879f 100644 --- a/regex_patterns.go +++ b/regex_patterns.go @@ -39,6 +39,8 @@ const ( Latitude string = "^(\\+|-)?(?:90(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\\.[0-9]{1,6})?))$" // Longitude represents longitude regular expression Longitude string = "^(\\+|-)?(?:180(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\\.[0-9]{1,6})?))$" + // MacAddress represents regular expression for mac address + MacAddress string = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" // Numeric represents regular expression for numeric Numeric string = "^-?[0-9]+$" // URL represents regular expression for url @@ -66,6 +68,7 @@ var ( regexDigits = regexp.MustCompile(Digits) regexEmail = regexp.MustCompile(Email) regexFloat = regexp.MustCompile(Float) + regexMacAddress = regexp.MustCompile(MacAddress) regexNumeric = regexp.MustCompile(Numeric) regexLatitude = regexp.MustCompile(Latitude) regexLongitude = regexp.MustCompile(Longitude) diff --git a/rules.go b/rules.go index 3b94488..9d1dfca 100644 --- a/rules.go +++ b/rules.go @@ -852,6 +852,19 @@ func init() { return nil }) + // Numeric check if the value of the field is Numeric + AddCustomRule("mac_address", func(field string, rule string, message string, value interface{}) error { + str := toString(value) + err := fmt.Errorf("The %s field must be a valid Mac Address", field) + if message != "" { + err = errors.New(message) + } + if !isMacAddress(str) { + return err + } + return nil + }) + // Numeric check if the value of the field is Numeric AddCustomRule("numeric", func(field string, rule string, message string, value interface{}) error { str := toString(value) diff --git a/rules_test.go b/rules_test.go index cd7593e..f350c3e 100644 --- a/rules_test.go +++ b/rules_test.go @@ -1332,6 +1332,73 @@ func Test_Len_message(t *testing.T) { } } +func Test_MacAddress(t *testing.T) { + type user struct { + MacAddress string `json:"mac_address"` + } + + postUser := user{MacAddress: "e4:2b:e8:d3:41:0f"} + var userObj user + + body, _ := json.Marshal(postUser) + req, _ := http.NewRequest("POST", "http://www.example.com", bytes.NewReader(body)) + + rules := MapData{ + "mac_address": []string{"mac_address"}, + } + + messages := MapData{ + "mac_address": []string{"mac_address:custom_message"}, + } + + opts := Options{ + Request: req, + Data: &userObj, + Rules: rules, + Messages: messages, + } + + vd := New(opts) + validationErr := vd.ValidateJSON() + if len(validationErr) != 0 { + t.Error("Valid Mac Address validation failed!") + } +} + +func Test_MacAddress_message(t *testing.T) { + type user struct { + MacAddress string `json:"mac_address"` + } + + postUser := user{MacAddress: "invalid_mac_address"} + var userObj user + + body, _ := json.Marshal(postUser) + req, _ := http.NewRequest("POST", "http://www.example.com", bytes.NewReader(body)) + + rules := MapData{ + "mac_address": []string{"mac_address"}, + } + + messages := MapData{ + "mac_address": []string{"mac_address:custom_message"}, + } + + opts := Options{ + Request: req, + Data: &userObj, + Rules: rules, + Messages: messages, + } + + vd := New(opts) + validationErr := vd.ValidateJSON() + if validationErr.Get("mac_address") != "custom_message" { + t.Error("Mac Address custom message failed!") + } +} + + func Test_Numeric(t *testing.T) { type user struct { NID string `json:"nid"`