1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-12 08:42:29 +08:00

Compare commits

...

11 Commits

Author SHA1 Message Date
dudaodong
7d4b9510a2 feat: add IsChineseHMPassport 2025-08-21 14:13:43 +08:00
dudaodong
9f0ad2354a feat: update some test fucntions 2025-08-21 10:52:44 +08:00
dudaodong
55b66dee99 Merge branch 'rc' into v2 2025-08-21 10:08:21 +08:00
Idichekop
4c64a16204 fix(package): [slice] functions with inconsistent return behaviour (#326)
* Some functions modified

* Fixes in all the functions of slice.go

* Re-use of swap function internal.
2025-08-19 10:50:25 +08:00
dudaodong
385e64cc52 feat: add IsPassport 2025-08-13 14:01:24 +08:00
dudaodong
be45a259db Merge branch 'v2' into rc 2025-08-13 11:24:27 +08:00
Idichekop
6f703fe577 fix(package): [slice] Fix RigthPadding and LeftPadding (#322)
* Fixes in  RightPadding and LeftPadding

* Tests cover padding empty, nil, and negative lenght

* Implemented repeat, concat, and grow functionalities as internal.
2025-08-04 10:45:29 +08:00
Idichekop
cb8d93c499 Simple refactor of ForEach functions (#323) 2025-07-30 14:28:27 +08:00
idichekop
ae1014c572 fix(package):[function] Corrected behaviour of Nand predicate (#319) 2025-07-22 10:08:50 +08:00
dudaodong
d5b9e67330 fix: fix go lint issue 2025-07-07 11:18:34 +08:00
dudaodong
a81403766f feat: add ToPointers, FromPointer, FromPointers 2025-07-07 11:16:44 +08:00
15 changed files with 644 additions and 185 deletions

View File

@@ -228,6 +228,42 @@ func ToPointer[T any](value T) *T {
return &value return &value
} }
// ToPointers convert a slice of values to a slice of pointers.
// Play: todo
func ToPointers[T any](values []T) []*T {
result := make([]*T, len(values))
for i := range values {
result[i] = &values[i]
}
return result
}
// FromPointer returns the value pointed to by the pointer.
// Play: todo
func FromPointer[T any](ptr *T) T {
if ptr == nil {
var zeroValue T
return zeroValue
}
return *ptr
}
// FromPointers convert a slice of pointers to a slice of values.
// Play: todo
func FromPointers[T any](pointers []*T) []T {
result := make([]T, len(pointers))
for i, ptr := range pointers {
if ptr == nil {
var zeroValue T
result[i] = zeroValue
} else {
result[i] = *ptr
}
}
return result
}
// ToMap convert a slice of structs to a map based on iteratee function. // ToMap convert a slice of structs to a map based on iteratee function.
// Play: https://go.dev/play/p/tVFy7E-t24l // Play: https://go.dev/play/p/tVFy7E-t24l
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V { func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
@@ -406,15 +442,15 @@ func ToStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return "" return ""
} }
switch value.(type) { switch v := value.(type) {
case []byte: case []byte:
return base64.StdEncoding.EncodeToString(value.([]byte)) return base64.StdEncoding.EncodeToString(v)
case string: case string:
return base64.StdEncoding.EncodeToString([]byte(value.(string))) return base64.StdEncoding.EncodeToString([]byte(v))
case error: case error:
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error())) return base64.StdEncoding.EncodeToString([]byte(v.Error()))
default: default:
marshal, err := json.Marshal(value) marshal, err := json.Marshal(v)
if err != nil { if err != nil {
return "" return ""
} }
@@ -428,15 +464,15 @@ func ToUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return "" return ""
} }
switch value.(type) { switch v := value.(type) {
case []byte: case []byte:
return base64.URLEncoding.EncodeToString(value.([]byte)) return base64.URLEncoding.EncodeToString(v)
case string: case string:
return base64.URLEncoding.EncodeToString([]byte(value.(string))) return base64.URLEncoding.EncodeToString([]byte(v))
case error: case error:
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error())) return base64.URLEncoding.EncodeToString([]byte(v.Error()))
default: default:
marshal, err := json.Marshal(value) marshal, err := json.Marshal(v)
if err != nil { if err != nil {
return "" return ""
} }
@@ -450,7 +486,7 @@ func ToRawStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return "" return ""
} }
switch value.(type) { switch v := value.(type) {
case []byte: case []byte:
return base64.RawStdEncoding.EncodeToString(value.([]byte)) return base64.RawStdEncoding.EncodeToString(value.([]byte))
case string: case string:
@@ -458,7 +494,7 @@ func ToRawStdBase64(value any) string {
case error: case error:
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error())) return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
default: default:
marshal, err := json.Marshal(value) marshal, err := json.Marshal(v)
if err != nil { if err != nil {
return "" return ""
} }
@@ -472,7 +508,7 @@ func ToRawUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return "" return ""
} }
switch value.(type) { switch v := value.(type) {
case []byte: case []byte:
return base64.RawURLEncoding.EncodeToString(value.([]byte)) return base64.RawURLEncoding.EncodeToString(value.([]byte))
case string: case string:
@@ -480,7 +516,7 @@ func ToRawUrlBase64(value any) string {
case error: case error:
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error())) return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
default: default:
marshal, err := json.Marshal(value) marshal, err := json.Marshal(v)
if err != nil { if err != nil {
return "" return ""
} }

View File

@@ -302,6 +302,75 @@ func TestToPointer(t *testing.T) {
assert.Equal(*result, 123) assert.Equal(*result, 123)
} }
func TestToPointers(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToPointers")
intVals := []int{1, 2, 3}
result := ToPointers(intVals)
assert.Equal(3, len(result))
assert.Equal(1, *result[0])
assert.Equal(2, *result[1])
assert.Equal(3, *result[2])
stringVals := []string{"a", "b", "c"}
resultStr := ToPointers(stringVals)
assert.Equal(3, len(resultStr))
assert.Equal("a", *resultStr[0])
assert.Equal("b", *resultStr[1])
assert.Equal("c", *resultStr[2])
}
func TestFromPointer(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFromPointer")
intVal := 123
pointer := &intVal
result := FromPointer(pointer)
assert.Equal(123, result)
stringVal := "abc"
stringPointer := &stringVal
resultStr := FromPointer(stringPointer)
assert.Equal("abc", resultStr)
}
func TestFromPointers(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFromPointers")
intPointers := []*int{new(int), new(int), new(int)}
*intPointers[0] = 1
*intPointers[1] = 2
*intPointers[2] = 3
result := FromPointers(intPointers)
assert.Equal(3, len(result))
assert.Equal(1, result[0])
assert.Equal(2, result[1])
assert.Equal(3, result[2])
stringPointers := []*string{new(string), new(string), new(string)}
*stringPointers[0] = "a"
*stringPointers[1] = "b"
*stringPointers[2] = "c"
resultStr := FromPointers(stringPointers)
assert.Equal(3, len(resultStr))
assert.Equal("a", resultStr[0])
assert.Equal("b", resultStr[1])
assert.Equal("c", resultStr[2])
}
func TestEncodeByte(t *testing.T) { func TestEncodeByte(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -1,51 +1,51 @@
-----BEGIN rsa private key----- -----BEGIN rsa private key-----
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
-----END rsa private key----- -----END rsa private key-----

View File

@@ -1,14 +1,14 @@
-----BEGIN rsa public key----- -----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw0anfgtraA2uaZwoLpLB MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBXtNRZ18uz+ujnKC/CB
vo1EkfYvDBgeXoMQ4WMKbcw6jU8k18E+f3WM52I2RssEk6g0X1vIiarHZU5qyxbv 6bYjOHn0++xX1tbqRoRfP4VP01R/k9d28sI9cqQa9Oqo3TPehKAyR4m3p9ZJ+pOg
2iNoT7EfgizzlXYvx06pM69GNBQrV46+lNhiOv4eeLZcOHBnxAyizlIKyLuwO+C1 NLpe7QEPWSGqIvPILRqS/4NnnjBuKvRHRTqiQwiuzTo/BBTxJCtF028Q7yRKmloE
cX/6BxuXjX3ogw+6IaBFZN/EOMmTWc6sPKrnNCEqgCNiTbPAXb+N8j5Iv8QPs0Aw dtrdFryqsZmTrtMIvaNneTa/Bnx33s4vfAm76wNKF5li0AN7NsmKfWjOG7/f29qp
VTB3jC9LP0mRt2GGD0cu+QIZkoGEhyW9Dd+0tgeIK32uXCF8uDjNpUV1TxCdn5H+ 9rTJEBnCIKImrwTFY9BWrx83LPDAUq8tpn8zevEe0c/42yZNlBprea4FIdZs8A/3
lvgddxfiaIJAdY5UaTx48XRIdULhQrVKXJNyTYWTXbezViYIf1nXOdDI2hsGgKTf gDZE7kltYjdybox0XWjR9znWPLmH3De+1xJ0woJ53f3jYDJ3REwmm8+sX26FIT3W
NVCocDT1LcT5zebijFHVCWfAAKEPRLdzPZomkFIa3rQQgChgePzE8Oqsaxuwx8sU PN/ksVgHsoaj2IZ3MaEkxf4E4GEp9sMSP8b+ans/B5gmcZi2pO8nx/ASVlxCthUS
09NAo3giSq1OmBBwly7h60Lwtm8+XuwmWtEgZoSQy2Hm8UMAJ3guotfYuS8mPQBi aK92CsoKHzqTotaZ+/dt689qT4zuqUWZpnlLIUKd0t1G485S8km15GgPcMY5vXe1
55JZpdqVN0D5SdwuWXhHpO4xDodNYAoMMei1NNgrX2zN3FCS8PVi97wD6cQ8xsWM 3i8dmsxVRIjgYclSClKCnDBTTAV5GUSPnanPU4N0dlKpO0UxzILixaw227yw7zDy
62nByrzIjmPwTfoCb3qP/928FMJMg7b3bYBrUnOrpVWZfAIDs4H7DoP8VLL4rMgB +rQ8ixchQ+ROQ6I4bdced7FuVXfEAa2gVuzm3uZ1J/xDnV6xwLYye2SitlzAL0wA
5PuWNQ13gtX7y5ZOyLhopiKLnar9XiN8GAMANBdKRI2anGEozrfJoelJ5POXZwSa /mFAF6koSa6+MYV+tzdPta2Xkw9xmO3a5PKwhCvOUbEMBJfLI2GPAfTuPh1LH4MZ
tRjbL+nWO3YnBzEunCw1xmMCAwEAAQ== qFUgzYHUiBCI21N3kMVPGAUCAwEAAQ==
-----END rsa public key----- -----END rsa public key-----

View File

@@ -620,7 +620,7 @@ func main() {
### <span id="Nand">Nand</span> ### <span id="Nand">Nand</span>
<p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p> <p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to false only if all predicates evaluate to true for the given value.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -650,7 +650,7 @@ func main() {
// Output: // Output:
// false // false
// false // true
// true // true
} }
``` ```

View File

@@ -18,7 +18,7 @@ func And[T any](predicates ...func(T) bool) func(T) bool {
} }
// Nand returns a composed predicate that represents the logical NAND of a list of predicates. // Nand returns a composed predicate that represents the logical NAND of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value. // It evaluates to false only if all predicates evaluate to true for the given value.
// Play: https://go.dev/play/p/Rb-FdNGpgSO // Play: https://go.dev/play/p/Rb-FdNGpgSO
func Nand[T any](predicates ...func(T) bool) func(T) bool { func Nand[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 { if len(predicates) < 2 {
@@ -26,11 +26,11 @@ func Nand[T any](predicates ...func(T) bool) func(T) bool {
} }
return func(value T) bool { return func(value T) bool {
for _, predicate := range predicates { for _, predicate := range predicates {
if predicate(value) { if !predicate(value) {
return false // Short-circuit on the first true predicate return true // Short-circuit on the first false predicate
} }
} }
return true // True if all predicates are false return false // False if all predicates are true
} }
} }

View File

@@ -65,7 +65,7 @@ func ExampleNand() {
// Output: // Output:
// false // false
// false // true
// true // true
} }

View File

@@ -65,7 +65,7 @@ func TestPredicatesNandPure(t *testing.T) {
) )
assert.ShouldBeFalse(isNumericAndLength5("12345")) assert.ShouldBeFalse(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234")) assert.ShouldBeTrue(isNumericAndLength5("1234"))
assert.ShouldBeTrue(isNumericAndLength5("abcdef")) assert.ShouldBeTrue(isNumericAndLength5("abcdef"))
} }

View File

@@ -507,19 +507,17 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
} }
// ForEach iterates over elements of slice and invokes function for each element. // ForEach iterates over elements of slice and invokes function for each element.
// Play: https://go.dev/play/p/DrPaa4YsHRF
func ForEach[T any](slice []T, iteratee func(index int, item T)) { func ForEach[T any](slice []T, iteratee func(index int, item T)) {
for i := 0; i < len(slice); i++ { for idx, elem := range slice {
iteratee(i, slice[i]) iteratee(idx, elem)
} }
} }
// ForEachWithBreak iterates over elements of slice and invokes function for each element, // ForEachWithBreak iterates over elements of slice and invokes function for each element,
// when iteratee return false, will break the for each loop. // when function return false, will break the for each loop.
// Play: https://go.dev/play/p/qScs39f3D9W
func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) { func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) {
for i := 0; i < len(slice); i++ { for idx, elem := range slice {
if !iteratee(i, slice[i]) { if !iteratee(idx, elem) {
break break
} }
} }
@@ -692,11 +690,12 @@ func IntSlice(slice any) []int {
// DeleteAt delete the element of slice at index. // DeleteAt delete the element of slice at index.
// Play: https://go.dev/play/p/800B1dPBYyd // Play: https://go.dev/play/p/800B1dPBYyd
func DeleteAt[T any](slice []T, index int) []T { func DeleteAt[T any](slice []T, index int) []T {
result := append([]T(nil), slice...)
if index < 0 || index >= len(slice) { if index < 0 || index >= len(slice) {
return slice[:len(slice)-1] return result[:len(slice)-1]
} }
result := append([]T(nil), slice...)
copy(result[index:], result[index+1:]) copy(result[index:], result[index+1:])
// Set the last element to zero value, clean up the memory. // Set the last element to zero value, clean up the memory.
@@ -736,7 +735,8 @@ func Drop[T any](slice []T, n int) []T {
} }
if n <= 0 { if n <= 0 {
return slice result := make([]T, 0, size)
return append(result, slice...)
} }
result := make([]T, 0, size-n) result := make([]T, 0, size-n)
@@ -754,7 +754,8 @@ func DropRight[T any](slice []T, n int) []T {
} }
if n <= 0 { if n <= 0 {
return slice result := make([]T, 0, size)
return append(result, slice...)
} }
result := make([]T, 0, size-n) result := make([]T, 0, size-n)
@@ -800,7 +801,9 @@ func InsertAt[T any](slice []T, index int, value any) []T {
size := len(slice) size := len(slice)
if index < 0 || index > size { if index < 0 || index > size {
return slice result := make([]T, size)
copy(result, slice)
return result
} }
switch v := value.(type) { switch v := value.(type) {
@@ -817,21 +820,21 @@ func InsertAt[T any](slice []T, index int, value any) []T {
copy(result[index+len(v):], slice[index:]) copy(result[index+len(v):], slice[index:])
return result return result
default: default:
return slice result := make([]T, size)
copy(result, slice)
return result
} }
} }
// UpdateAt update the slice element at index. // UpdateAt update the slice element at index.
// Play: https://go.dev/play/p/f3mh2KloWVm // Play: https://go.dev/play/p/f3mh2KloWVm
func UpdateAt[T any](slice []T, index int, value T) []T { func UpdateAt[T any](slice []T, index int, value T) []T {
if index < 0 || index >= len(slice) {
return slice
}
result := make([]T, len(slice)) result := make([]T, len(slice))
copy(result, slice) copy(result, slice)
result[index] = value if index >= 0 && index < len(slice) {
result[index] = value
}
return result return result
} }
@@ -1021,7 +1024,9 @@ func SymmetricDifference[T comparable](slices ...[]T) []T {
return []T{} return []T{}
} }
if len(slices) == 1 { if len(slices) == 1 {
return Unique(slices[0]) result := make([]T, len(slices[0]))
copy(result, slices[0])
return Unique(result)
} }
result := make([]T, 0) result := make([]T, 0)
@@ -1042,6 +1047,7 @@ func SymmetricDifference[T comparable](slices ...[]T) []T {
} }
// Reverse return slice of element order is reversed to the given slice. // Reverse return slice of element order is reversed to the given slice.
// Reverse modifies the slice in place.
// Play: https://go.dev/play/p/8uI8f1lwNrQ // Play: https://go.dev/play/p/8uI8f1lwNrQ
func Reverse[T any](slice []T) { func Reverse[T any](slice []T) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
@@ -1049,7 +1055,8 @@ func Reverse[T any](slice []T) {
} }
} }
// ReverseCopy return a new slice of element order is reversed to the given slice. // ReverseCopy return a new slice of element where the order is reversed to the given
// slice.
// Play: https://go.dev/play/p/c9arEaP7Cg- // Play: https://go.dev/play/p/c9arEaP7Cg-
func ReverseCopy[T any](slice []T) []T { func ReverseCopy[T any](slice []T) []T {
result := make([]T, len(slice)) result := make([]T, len(slice))
@@ -1067,7 +1074,7 @@ func Shuffle[T any](slice []T) []T {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(slice), func(i, j int) { rand.Shuffle(len(slice), func(i, j int) {
slice[i], slice[j] = slice[j], slice[i] swap(slice, i, j)
}) })
return slice return slice
@@ -1240,11 +1247,12 @@ func SortByField[T any](slice []T, field string, sortType ...string) error {
// Without creates a slice excluding all given items. // Without creates a slice excluding all given items.
// Play: https://go.dev/play/p/bwhEXEypThg // Play: https://go.dev/play/p/bwhEXEypThg
func Without[T comparable](slice []T, items ...T) []T { func Without[T comparable](slice []T, items ...T) []T {
result := make([]T, 0, len(slice))
if len(items) == 0 || len(slice) == 0 { if len(items) == 0 || len(slice) == 0 {
return slice return append(result, slice...)
} }
result := make([]T, 0, len(slice))
for _, v := range slice { for _, v := range slice {
if !Contain(items, v) { if !Contain(items, v) {
result = append(result, v) result = append(result, v)
@@ -1465,36 +1473,28 @@ func Random[T any](slice []T) (val T, idx int) {
return slice[idx], idx return slice[idx], idx
} }
// RightPadding adds padding to the right end of a slice. // RightPadding returns a copy of the slice padding the given value to the right end of a slice.
// If paddingLength is zero or less, the function returns a copy of the slice.
// Play: https://go.dev/play/p/0_2rlLEMBXL // Play: https://go.dev/play/p/0_2rlLEMBXL
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T { func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 { suffix := []T{}
return slice if paddingLength > 0 {
suffix = repeat([]T{paddingValue}, paddingLength)
} }
for i := 0; i < paddingLength; i++ { padded := concat(slice, suffix)
slice = append(slice, paddingValue) return padded
}
return slice
} }
// LeftPadding adds padding to the left begin of a slice. // LeftPadding returns a copy of the slice padding the given value to the left begin of a slice.
// If paddingLength is zero or less, the function returns a copy of the slice.
// Play: https://go.dev/play/p/jlQVoelLl2k // Play: https://go.dev/play/p/jlQVoelLl2k
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T { func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 { prefix := []T{}
return slice if paddingLength > 0 {
prefix = repeat([]T{paddingValue}, paddingLength)
} }
padded := concat(prefix, slice)
paddedSlice := make([]T, len(slice)+paddingLength) return padded
i := 0
for ; i < paddingLength; i++ {
paddedSlice[i] = paddingValue
}
for j := 0; j < len(slice); j++ {
paddedSlice[i] = slice[j]
i++
}
return paddedSlice
} }
// Frequency counts the frequency of each element in the slice. // Frequency counts the frequency of each element in the slice.

View File

@@ -831,7 +831,7 @@ func ExampleUniqueByComparator() {
}) })
caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool { caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other) return strings.EqualFold(item, other)
}) })
fmt.Println(uniqueNums) fmt.Println(uniqueNums)

View File

@@ -2,6 +2,7 @@ package slice
import ( import (
"fmt" "fmt"
"math/bits"
"reflect" "reflect"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
@@ -96,3 +97,71 @@ func partitionAnySlice[T any](slice []T, lowIndex, highIndex int, less func(a, b
func swap[T any](slice []T, i, j int) { func swap[T any](slice []T, i, j int) {
slice[i], slice[j] = slice[j], slice[i] slice[i], slice[j] = slice[j], slice[i]
} }
// `repeat` returns a new slice that repeats the provided slice the given number of
// times. The result has length and capacity (len(x) * count). The result is never nil.
// Repeat panics if count is negative or if the result of (len(x) * count) overflows.
//
// repeat has been provided in the standard lib within the package `slices` under the
// name Repeat since GO version 1.21 onwards. As lancet commits to compatibility with GO
// 1.18 onwards, we implement the functionality as an internal function.
func repeat[S ~[]E, E any](x S, count int) S {
if count < 0 {
panic("count cannot be negative")
}
const maxInt = ^uint(0) >> 1
hi, lo := bits.Mul(uint(len(x)), uint(count))
if hi > 0 || lo > maxInt {
panic("the result of (len(x) * count) overflows")
}
newslice := make(S, int(lo)) // lo = len(x) * count
n := copy(newslice, x)
for n < len(newslice) {
n += copy(newslice[n:], newslice[:n])
}
return newslice
}
// concat returns a new slice concatenating the passed in slices.
//
// concat has been provided in the standard lib within the package `slices` under the
// name Concat since GO version 1.21 onwards. As lancet commits to compatibility with GO
// 1.18 onwards, we implement the functionality as an internal function.
func concat[S ~[]E, E any](slices ...S) S {
size := 0
for _, s := range slices {
size += len(s)
if size < 0 {
panic("len out of range")
}
}
// Use Grow, not make, to round up to the size class:
// the extra space is otherwise unused and helps
// callers that append a few elements to the result.
newslice := grow[S](nil, size)
for _, s := range slices {
newslice = append(newslice, s...)
}
return newslice
}
// grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, grow panics.
//
// grow has been provided in the standard lib within the package `slices` under the
// name Grow since GO version 1.21 onwards. As lancet commits to compatibility with GO
// 1.18 onwards, we implement the functionality as an internal function.
func grow[S ~[]E, E any](s S, n int) S {
if n < 0 {
panic("cannot be negative")
}
if n -= cap(s) - len(s); n > 0 {
// This expression allocates only once.
s = append(s[:cap(s)], make([]E, n)...)[:len(s)]
}
return s
}

View File

@@ -1008,7 +1008,7 @@ func TestUniqueByComparator(t *testing.T) {
t.Run("case-insensitive string comparison", func(t *testing.T) { t.Run("case-insensitive string comparison", func(t *testing.T) {
stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"} stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"}
caseInsensitiveComparator := func(item, other string) bool { caseInsensitiveComparator := func(item, other string) bool {
return strings.ToLower(item) == strings.ToLower(other) return strings.EqualFold(item, other)
} }
result := UniqueByComparator(stringSlice, caseInsensitiveComparator) result := UniqueByComparator(stringSlice, caseInsensitiveComparator)
@@ -1756,6 +1756,20 @@ func TestRightPaddingAndLeftPadding(t *testing.T) {
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3) padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded) assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
// Test with negative padding length
paddedNegative := LeftPadding(RightPadding(nums, 0, -3), 0, -3)
assert.Equal([]int{1, 2, 3, 4, 5}, paddedNegative)
// Test with empty slice
empty := []int{}
paddedEmpty := LeftPadding(RightPadding(empty, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 0, 0, 0}, paddedEmpty)
// Test with nil
nilSlice := []int(nil)
paddedNil := LeftPadding(RightPadding(nilSlice, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 0, 0, 0}, paddedNil)
} }
func TestUniqueByConcurrent(t *testing.T) { func TestUniqueByConcurrent(t *testing.T) {

View File

@@ -19,30 +19,48 @@ import (
) )
var ( var (
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`) letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
alphaNumericMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`) alphaNumericMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
numberRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d`) numberRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d`)
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`) intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`) urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`) dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`) // emailMatcher *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`) emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+(-+.\w+)*@\w+(-.\w+)*.\w+(-.\w+)*`)
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`) chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]") chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`([1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx])|([1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}[0-9Xx])`)
chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`) chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`) chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`)
base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`) creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
base64URLMatcher *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`) base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
binMatcher *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`) base64URLMatcher *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`)
hexMatcher *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`) binMatcher *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`)
visaMatcher *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`) hexMatcher *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`)
masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`) visaMatcher *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`)
americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`) masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`)
unionPay *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$") americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`)
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}$`)
chineseHMPassportMatcher *regexp.Regexp = regexp.MustCompile(`^[CM]\d{8}$`)
) )
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 ( var (
// Identity card formula // Identity card formula
factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
@@ -258,21 +276,45 @@ func IsPort(str string) bool {
// IsUrl check if the string is url. // IsUrl check if the string is url.
// Play: https://go.dev/play/p/pbJGa7F98Ka // Play: https://go.dev/play/p/pbJGa7F98Ka
func IsUrl(str string) bool { func IsUrl(str string) bool {
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { if str == "" {
return false
}
u, err := url.Parse(str)
if err != nil {
return false
}
if strings.HasPrefix(u.Host, ".") {
return false
}
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
return false return false
} }
return urlMatcher.MatchString(str) u, err := url.Parse(str)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
allowedSchemes := map[string]struct{}{
"http": {},
"https": {},
"ftp": {},
"ws": {},
"wss": {},
"file": {},
"mailto": {},
"data": {},
}
if _, ok := allowedSchemes[u.Scheme]; !ok {
return false
}
if u.Scheme == "file" || u.Scheme == "mailto" || u.Scheme == "data" {
return true
}
host := u.Hostname()
if !strings.Contains(host, ".") || strings.HasSuffix(host, ".") {
return false
}
// domainRegexp := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9]$`)
if !dnsMatcher.MatchString(host) {
return false
}
return true
} }
// IsDns check if the string is dns. // IsDns check if the string is dns.
@@ -286,8 +328,6 @@ func IsDns(dns string) bool {
func IsEmail(email string) bool { func IsEmail(email string) bool {
_, err := mail.ParseAddress(email) _, err := mail.ParseAddress(email)
return err == nil return err == nil
// return emailMatcher.MatchString(email)
} }
// IsChineseMobile check if the string is chinese mobile number. // IsChineseMobile check if the string is chinese mobile number.
@@ -460,12 +500,13 @@ func IsZeroValue(value any) bool {
func IsGBK(data []byte) bool { func IsGBK(data []byte) bool {
i := 0 i := 0
for i < len(data) { for i < len(data) {
if data[i] <= 0xff { if data[i] < 0x81 {
i++ i++
continue continue
} else { } else {
if data[i] >= 0x81 && if data[i] >= 0x81 &&
data[i] <= 0xfe && data[i] <= 0xfe &&
i+1 < len(data) &&
data[i+1] >= 0x40 && data[i+1] >= 0x40 &&
data[i+1] <= 0xfe && data[i+1] <= 0xfe &&
data[i+1] != 0xf7 { data[i+1] != 0xf7 {
@@ -561,12 +602,71 @@ func IsAmericanExpress(v string) bool {
// IsUnionPay check if a give string is a valid union pay nubmer or not. // IsUnionPay check if a give string is a valid union pay nubmer or not.
// Play: https://go.dev/play/p/CUHPEwEITDf // Play: https://go.dev/play/p/CUHPEwEITDf
func IsUnionPay(v string) bool { func IsUnionPay(cardNo string) bool {
return unionPay.MatchString(v) if len(cardNo) < 16 || len(cardNo) > 19 {
return false
}
matched, _ := regexp.MatchString(`^\d+$`, cardNo)
if !matched {
return false
}
if len(cardNo) < 3 {
return false
}
prefix := cardNo[:3]
prefixNum, err := strconv.Atoi(prefix)
if err != nil {
return false
}
if prefixNum < 620 || prefixNum > 625 {
return false
}
return true
} }
// IsChinaUnionPay check if a give string is a valid china union pay nubmer or not. // IsChinaUnionPay check if a give string is a valid china union pay nubmer or not.
// Play: https://go.dev/play/p/yafpdxLiymu // Play: https://go.dev/play/p/yafpdxLiymu
func IsChinaUnionPay(v string) bool { func IsChinaUnionPay(cardNo string) bool {
return chinaUnionPay.MatchString(v) return chinaUnionPayMatcher.MatchString(cardNo)
}
// luhnCheck checks if the credit card number is valid using the Luhn algorithm.
func luhnCheck(card string) bool {
var sum int
alt := false
for i := len(card) - 1; i >= 0; i-- {
n := int(card[i] - '0')
if alt {
n *= 2
if n > 9 {
n -= 9
}
}
sum += n
alt = !alt
}
return sum%10 == 0
}
// 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
}
// IsChineseHMPassport checks if the string is a valid Chinese Hong Kong and Macau Travel Permit number.
// Chinese Hong Kong and Macau Travel Permit format: C or M + 8 digits (e.g., C12345678, M12345678).
// Play: https://go.dev/play/p/TODO
func IsChineseHMPassport(hmPassport string) bool {
return chineseHMPassportMatcher.MatchString(hmPassport)
} }

View File

@@ -214,7 +214,7 @@ func ExampleIsUrl() {
fmt.Println(result3) fmt.Println(result3)
// Output: // Output:
// true // false
// true // true
// false // false
} }
@@ -683,3 +683,42 @@ func ExampleIsAlphaNumeric() {
// true // true
// false // 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
}
func ExampleIsChineseHMPassport() {
result1 := IsChineseHMPassport("C12345678")
result2 := IsChineseHMPassport("C00000000")
result3 := IsChineseHMPassport("M12345678")
result4 := IsChineseHMPassport("c12345678")
result5 := IsChineseHMPassport("C1234567")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// true
// true
// true
// false
// false
}

View File

@@ -437,10 +437,32 @@ func TestIsUrl(t *testing.T) {
assert := internal.NewAssert(t, "TestIsUrl") assert := internal.NewAssert(t, "TestIsUrl")
assert.Equal(true, IsUrl("http://abc.com")) tests := []struct {
assert.Equal(true, IsUrl("abc.com")) input string
assert.Equal(true, IsUrl("a.b.com")) expected bool
assert.Equal(false, IsUrl("abc")) }{
{"http://abc.com", true},
{"https://abc.com", true},
{"ftp://abc.com", true},
{"http://abc.com/path?query=123", true},
{"https://abc.com/path/to/resource", true},
{"ws://abc.com", true},
{"wss://abc.com", true},
{"mailto://abc.com", true},
{"file://path/to/file", true},
{"data://text/plain;base64,SGVsbG8sIFdvcmxkIQ==", true},
{"http://abc.com/path/to/resource?query=123#fragment", true},
{"abc", false},
{"http://", false},
{"http://abc", false},
{"http://abc:8080", false},
{"http://abc:99999999", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsUrl(tt.input))
}
} }
func TestIsDns(t *testing.T) { func TestIsDns(t *testing.T) {
@@ -477,12 +499,24 @@ func TestIsEmail(t *testing.T) {
func TestContainChinese(t *testing.T) { func TestContainChinese(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestContainChinese") assert := internal.NewAssert(t, "TestContainChinese")
assert.Equal(true, ContainChinese("你好")) tests := []struct {
assert.Equal(true, ContainChinese("你好hello")) input string
assert.Equal(false, ContainChinese("hello")) expected bool
}{
{"你好", true},
{"hello", false},
{"你好hello", true},
{"hello你好", true},
{"", false},
{"123", false},
{"!@#$%^&*()", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, ContainChinese(tt.input))
}
} }
func TestIsChineseMobile(t *testing.T) { func TestIsChineseMobile(t *testing.T) {
@@ -490,8 +524,20 @@ func TestIsChineseMobile(t *testing.T) {
assert := internal.NewAssert(t, "TestIsChineseMobile") assert := internal.NewAssert(t, "TestIsChineseMobile")
assert.Equal(true, IsChineseMobile("13263527980")) tests := []struct {
assert.Equal(false, IsChineseMobile("434324324")) input string
expected bool
}{
{"13263527980", true},
{"1326352798", false},
{"132635279801", false},
{"1326352798a", false},
{"1326352798@", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsChineseMobile(tt.input))
}
} }
func TestIsChinesePhone(t *testing.T) { func TestIsChinesePhone(t *testing.T) {
@@ -887,7 +933,7 @@ func TestIsUnionPay(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIsUnionPay") assert := internal.NewAssert(t, "TestIsUnionPay")
assert.Equal(true, IsUnionPay("6221263430109903")) assert.Equal(true, IsUnionPay("6228480402564890"))
assert.Equal(false, IsUnionPay("3782822463100007")) assert.Equal(false, IsUnionPay("3782822463100007"))
} }
@@ -895,8 +941,25 @@ func TestIsChinaUnionPay(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIsChinaUnionPay") assert := internal.NewAssert(t, "TestIsChinaUnionPay")
assert.Equal(true, IsChinaUnionPay("6250941006528599")) tests := []struct {
assert.Equal(false, IsChinaUnionPay("3782822463100007")) cardNumber string
expected bool
}{
{"6228480420000000000", true},
{"6214830000000000", true},
{"6230580000000000000", true},
{"6259640000000000000", true},
{"6260000000000000000", true},
{"6288888888888888", true},
// 非银联前缀
{"4123456789012345", false},
{"3528000000000000", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsChinaUnionPay(tt.cardNumber))
}
} }
func TestIsAlphaNumeric(t *testing.T) { func TestIsAlphaNumeric(t *testing.T) {
@@ -924,3 +987,72 @@ func TestIsAlphaNumeric(t *testing.T) {
assert.Equal(tt.expected, IsAlphaNumeric(tt.input)) 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))
}
}
func TestIsChineseHMPassport(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsChineseHMPassport")
tests := []struct {
input string
expected bool
}{
{"C12345678", true},
{"C00000000", true},
{"C99999999", true},
{"M12345678", true}, // M prefix
{"M00000000", true}, // M prefix
{"M99999999", true}, // M prefix
{"c12345678", false}, // lowercase c
{"m12345678", false}, // lowercase m
{"C1234567", false}, // 7 digits
{"M1234567", false}, // 7 digits with M
{"C123456789", false}, // 9 digits
{"M123456789", false}, // 9 digits with M
{"C1234567a", false}, // contains letter
{"M1234567a", false}, // contains letter with M
{"D12345678", false}, // starts with D
{"12345678", false}, // no prefix
{"CC12345678", false}, // double C
{"MM12345678", false}, // double M
{"C 12345678", false}, // contains space
{"M 12345678", false}, // contains space with M
{"C12345-678", false}, // contains dash
{"M12345-678", false}, // contains dash with M
{"", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsChineseHMPassport(tt.input))
}
}