From 385e64cc52da378377ddaa196d72142e006e94c8 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Wed, 13 Aug 2025 14:01:24 +0800 Subject: [PATCH] feat: add IsPassport --- validator/validator.go | 38 +++++++++++++++++++++++++---- validator/validator_example_test.go | 18 ++++++++++++++ validator/validator_test.go | 30 +++++++++++++++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/validator/validator.go b/validator/validator.go index 1362eb8..b7abd1f 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -39,10 +39,26 @@ var ( visaMatcher *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`) masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`) americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`) - unionPay *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$") - chinaUnionPay *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`) + unionPayMatcher *regexp.Regexp = regexp.MustCompile(`^62[0-5]\\d{13,16}$`) + chinaUnionPayMatcher *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`) ) +var passportMatcher = map[string]*regexp.Regexp{ + "CN": regexp.MustCompile(`^P\d{9}$`), + "US": regexp.MustCompile(`^\d{9}$`), + "GB": regexp.MustCompile(`^[A-Z0-9]{9}$`), + "RU": regexp.MustCompile(`^[A-Z]{2}\d{7}$`), + "DE": regexp.MustCompile(`^\d{9}$`), + "FR": regexp.MustCompile(`^[A-Z]{2}\d{7}$`), + "JP": regexp.MustCompile(`^\d{8}$`), + "IT": regexp.MustCompile(`^\d{8}$`), + "AU": regexp.MustCompile(`^[A-Z]{1}\d{8}$`), + "BR": regexp.MustCompile(`^\d{9}$`), + "IN": regexp.MustCompile(`^[A-Z]{1,2}\d{7}$`), + "HK": regexp.MustCompile(`^M\d{8}$`), + "MO": regexp.MustCompile(`^[A-Z]\d{8}$`), +} + var ( // Identity card formula factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} @@ -460,12 +476,13 @@ func IsZeroValue(value any) bool { func IsGBK(data []byte) bool { i := 0 for i < len(data) { - if data[i] <= 0xff { + if data[i] < 0x81 { i++ continue } else { if data[i] >= 0x81 && data[i] <= 0xfe && + i+1 < len(data) && data[i+1] >= 0x40 && data[i+1] <= 0xfe && data[i+1] != 0xf7 { @@ -562,11 +579,22 @@ func IsAmericanExpress(v string) bool { // IsUnionPay check if a give string is a valid union pay nubmer or not. // Play: https://go.dev/play/p/CUHPEwEITDf func IsUnionPay(v string) bool { - return unionPay.MatchString(v) + return unionPayMatcher.MatchString(v) } // IsChinaUnionPay check if a give string is a valid china union pay nubmer or not. // Play: https://go.dev/play/p/yafpdxLiymu func IsChinaUnionPay(v string) bool { - return chinaUnionPay.MatchString(v) + return chinaUnionPayMatcher.MatchString(v) +} + +// IsPassport checks if the passport number is valid for a given country. +// country is a two-letter country code (ISO 3166-1 alpha-2). +// Play: todo +func IsPassport(passport, country string) bool { + if matcher, ok := passportMatcher[country]; ok { + return matcher.MatchString(passport) + } + + return false } diff --git a/validator/validator_example_test.go b/validator/validator_example_test.go index 088cb86..1000dbd 100644 --- a/validator/validator_example_test.go +++ b/validator/validator_example_test.go @@ -683,3 +683,21 @@ func ExampleIsAlphaNumeric() { // true // false } + +func ExampleIsPassport() { + result1 := IsPassport("P123456789", "CN") + result2 := IsPassport("123456789", "US") + result3 := IsPassport("AB1234567", "RU") + result4 := IsPassport("123456789", "CN") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} diff --git a/validator/validator_test.go b/validator/validator_test.go index 0842097..7f35088 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -924,3 +924,33 @@ func TestIsAlphaNumeric(t *testing.T) { assert.Equal(tt.expected, IsAlphaNumeric(tt.input)) } } + +func TestIsPassport(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsPassport") + + tests := []struct { + passport string + countryCode string + expected bool + }{ + {"P123456789", "CN", true}, + {"123456789", "US", true}, + {"A12345678", "GB", true}, + {"AB1234567", "FR", true}, + {"12345678", "JP", true}, + {"M12345678", "HK", true}, + {"A12345678", "MO", true}, + {"A1234567", "IN", true}, + {"12345678", "IT", true}, + {"A12345678", "AU", true}, + {"123456789", "BR", true}, + {"AB1234567", "RU", true}, + {"123456789", "CN", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsPassport(tt.passport, tt.countryCode)) + } +}