mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-05 21:32:27 +08:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdefbde9f5 | ||
|
|
1e57a743af | ||
|
|
7d4184a365 | ||
|
|
1b73483945 | ||
|
|
622aacaf44 | ||
|
|
e78ac65605 | ||
|
|
03f0d4d905 | ||
|
|
b2ae71c983 | ||
|
|
093f4a2286 | ||
|
|
f7ada6093c | ||
|
|
47e82aad39 | ||
|
|
9e813d236b | ||
|
|
72a23d2cb9 | ||
|
|
27667f8b3a | ||
|
|
0b5dc86d70 | ||
|
|
d88bba07dd | ||
|
|
d7f3354b98 | ||
|
|
f4dee28ebb | ||
|
|
c841a5b88c | ||
|
|
333038634b | ||
|
|
ef0fed23b2 | ||
|
|
1e0ee1fac1 | ||
|
|
4947327ed6 | ||
|
|
ceb706b874 | ||
|
|
3849337919 | ||
|
|
f4427b9fbc | ||
|
|
0ef45b533b | ||
|
|
169f280c9c | ||
|
|
e74126a0bd | ||
|
|
f7be4190f1 | ||
|
|
8089b71bfd | ||
|
|
5b9543255a | ||
|
|
bf859387f4 | ||
|
|
a8a92844f3 | ||
|
|
6dfdadd34e |
97
README.md
97
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -214,6 +214,30 @@ import "github.com/duke-git/lancet/v2/concurrency"
|
||||
- **<big>Tee</big>** : split one chanel into two channels, until cancel the context.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Tee)]
|
||||
[[play](https://go.dev/play/p/3TQPKnCirrP)]
|
||||
- **<big>NewKeyedLocker</big>** : KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Do</big>** :acquires a lock for the specified key and executes the provided function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Do)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>NewRWKeyedLocker</big>** :RRWKeyedLocker is a read-write version of KeyedLocker.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewRWKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>RLock</big>** : acquires a read lock for the specified key and executes the provided function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RLock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Lock</big>** : acquires a write lock for the specified key and executes the provided function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Lock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>NewTryKeyedLocker</big>** : TryKeyedLocker is a non-blocking version of KeyedLocker.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewTryKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>TryLock</big>** : TryLock tries to acquire a lock for the specified key.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#TryLock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Unlock</big>** : Unlock releases the lock for the specified key.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Unlock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="condition"> 4. Condition package contains some functions for conditional judgment. eg. And, Or, TernaryOperator... <a href="#index">index</a> </h3>
|
||||
|
||||
@@ -331,7 +355,7 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
|
||||
[[play](https://go.dev/play/p/HwdDPFcza1O)]
|
||||
- **<big>ToBigInt</big>** : converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int.
|
||||
- **<big>ToBigInt</big>** : converts an integer of any supported type (int, int64, uint64, etc.) to \*big.Int.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToBigInt)]
|
||||
[[play](https://go.dev/play/p/X3itkCxwB_x)]
|
||||
|
||||
@@ -508,22 +532,22 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/nT1heB1KUUK)]
|
||||
- **<big>AddWeek</big>** : add or sub week to time.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddWeek)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/M9TqdMiaA2p)]
|
||||
- **<big>AddMonth</big>** : add or sub months to time.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddMonth)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/DLoiOnpLvsN)]
|
||||
- **<big>AddYear</big>** : add or sub year to the time.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddYear)]
|
||||
[[play](https://go.dev/play/p/MqW2ujnBx10)]
|
||||
- **<big>AddDaySafe</big>** : add or sub days to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddDaySafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/JTohZFpoDJ3)]
|
||||
- **<big>AddMonthSafe</big>** : add or sub months to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddMonthSafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KLw0lo6mbVW)]
|
||||
- **<big>AddYearSafe</big>** : Add or sub years to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddYearSafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KVGXWZZ54ZH)]
|
||||
- **<big>BeginOfMinute</big>** : return the date time at the begin of minute of specific date.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfMinute)]
|
||||
[[play](https://go.dev/play/p/ieOLVJ9CiFT)]
|
||||
@@ -535,7 +559,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/94m_UT6cWs9)]
|
||||
- **<big>BeginOfWeek</big>** : return the date time at the begin of week of specific date.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfWeek)]
|
||||
[[play](https://go.dev/play/p/ynjoJPz7VNV)]
|
||||
[[play](https://go.dev/play/p/DCHdcL6gnfV)]
|
||||
- **<big>BeginOfMonth</big>** : return the date time at the begin of month of specific date.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfMonth)]
|
||||
[[play](https://go.dev/play/p/bWXVFsmmzwL)]
|
||||
@@ -553,7 +577,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/eMBOvmq5Ih1)]
|
||||
- **<big>EndOfWeek</big>** : return the date time at the end of week of specific date.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfWeek)]
|
||||
[[play](https://go.dev/play/p/i08qKXD9flf)]
|
||||
[[play](https://go.dev/play/p/mGSA162YgX9)]
|
||||
- **<big>EndOfMonth</big>** : return the date time at the end of month of specific date.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfMonth)]
|
||||
[[play](https://go.dev/play/p/_GWh10B3Nqi)]
|
||||
@@ -693,7 +717,6 @@ import optional "github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
|
||||
- **<big>Optional</big>** : Optional container.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
|
||||
|
||||
|
||||
<h3 id="eventbus"> 9. EventBus is an event bus used for handling events within an application. <a href="#index">Index</a></h3>
|
||||
|
||||
@@ -705,34 +728,34 @@ import "github.com/duke-git/lancet/v2/eventbus"
|
||||
|
||||
- **<big>NewEventBus</big>** : Create an EventBus instance.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#NewEventBus)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gHbOPV_NUOJ)]
|
||||
- **<big>Subscribe</big>** : subscribes to an event with a specific event topic and listener function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#Subscribe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/EYGf_8cHei-)]
|
||||
- **<big>Unsubscribe</big>** : unsubscribes from an event with a specific event topic and listener function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#Unsubscribe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/Tmh7Ttfvprf)]
|
||||
- **<big>Publish</big>** : publishes an event with a specific event topic and data payload.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#Publish)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gHTtVexFSH9)]
|
||||
- **<big>ClearListeners</big>** : clears all the listeners.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#ClearListeners)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KBfBYlKPgqD)]
|
||||
- **<big>ClearListenersByTopic</big>** : clears all the listeners by topic.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#ClearListenersByTopic)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gvMljmJOZmU)]
|
||||
- **<big>GetListenersCount</big>** : returns the number of listeners for a specific event topic.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#GetListenersCount)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/8VPJsMQgStM)]
|
||||
- **<big>GetAllListenersCount</big>** : returns the total number of all listeners.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#GetAllListenersCount)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/PUlr0xcpEOz)]
|
||||
- **<big>GetEvents</big>** : returns all the events topics.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#GetEvents)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/etgjjcOtAjX)]
|
||||
- **<big>SetErrorHandler</big>** : sets the error handler function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/eventbus.md#SetErrorHandler)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gmB0gnFe5mc)]
|
||||
|
||||
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -778,6 +801,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>RemoveFile</big>** : remove file, param should be file path.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#RemoveFile)]
|
||||
[[play](https://go.dev/play/p/P2y0XW8a1SH)]
|
||||
- **<big>RemoveDir</big>** : delete directory.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#RemoveDir)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>ReadFileToString</big>** : return string of file content.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFileToString)]
|
||||
[[play](https://go.dev/play/p/cmfwp_5SQTp)]
|
||||
@@ -926,7 +952,6 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
|
||||
|
||||
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map. <a href="#index">index</a></h3>
|
||||
|
||||
```go
|
||||
@@ -1097,7 +1122,9 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>GetOrDefault</big>** : returns the value of the given key or a default value if the key is not present.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrDefault)]
|
||||
[[play](https://go.dev/play/p/99QjSYSBdiM)]
|
||||
|
||||
- **<big>FindValuesBy</big>** : returns a slice of values from the map that satisfy the given predicate function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#FindValuesBy)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="mathutil"> 13. Mathutil package implements some functions for math calculation. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -1282,6 +1309,12 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
- **<big>IsTelnetConnected</big>** : checks if can if can telnet the specified host or not.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#IsTelnetConnected)]
|
||||
[[play](https://go.dev/play/p/yiLCGtQv_ZG)]
|
||||
- **<big>BuildUrl</big>** : builds a URL from the given params.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#BuildUrl)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>AddQueryParams</big>** : adds query parameters to the given URL.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#AddQueryParams)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="pointer"> 15. Pointer package contains some util functions to operate go pointer. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -1407,7 +1440,6 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||
[[play](https://go.dev/play/p/xp1avQmn16X)]
|
||||
|
||||
|
||||
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -1479,7 +1511,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/b9iygtgsHI1)]
|
||||
- **<big>EqualUnordered</big>** : Checks if two slices are equal: the same length and all elements value are equal (unordered).
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#EqualUnordered)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/n8fSc2w8ZgX)]
|
||||
- **<big>Every</big>** : return true if all of the values in the slice pass the predicate function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Every)]
|
||||
[[play](https://go.dev/play/p/R8U6Sl-j8cD)]
|
||||
@@ -1581,7 +1613,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/YHvhnWGU3Ge)]
|
||||
- **<big>ShuffleCopy</big>** : return a new slice with elements shuffled.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ShuffleCopy)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/vqDa-Gs1vT0)]
|
||||
- **<big>IsAscending</big>** : Checks if a slice is ascending order.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsAscending)]
|
||||
[[play](https://go.dev/play/p/9CtsFjet4SH)]
|
||||
@@ -1654,7 +1686,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Partition</big>** : partition all slice elements with the evaluation of the given predicate functions.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Partition)]
|
||||
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
|
||||
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
|
||||
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
|
||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
|
||||
@@ -1730,7 +1762,7 @@ import "github.com/duke-git/lancet/v2/stream"
|
||||
[[play](https://go.dev/play/p/A8_zkJnLHm4)]
|
||||
- **<big>ReverseCopy</big>** : returns a new slice of element order is reversed to the given slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#ReverseCopy)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/c9arEaP7Cg-)]
|
||||
- **<big>Range</big>** : returns a stream whose elements are in the range from start(included) to end(excluded) original stream.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Range)]
|
||||
[[play](https://go.dev/play/p/indZY5V2f4j)]
|
||||
@@ -1962,7 +1994,7 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/Ay9UIk7Rum9)]
|
||||
- **<big>FindAllOccurrences</big>** : Returns the positions of all occurrences of a substring in a string.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#FindAllOccurrences)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/uvyA6azGLB1)]
|
||||
|
||||
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -2012,7 +2044,6 @@ import "github.com/duke-git/lancet/v2/system"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)]
|
||||
[[play](https://go.dev/play/p/NQDVywEYYx7)]
|
||||
|
||||
|
||||
<h3 id="tuple"> 23. Tuple package implements tuple data type and some operations on it. <a href="#index">index</a></h3>
|
||||
|
||||
```go
|
||||
@@ -2195,6 +2226,9 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsNumberStr</big>** : check if the string can convert to a number.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsNumberStr)]
|
||||
[[play](https://go.dev/play/p/LzaKocSV79u)]
|
||||
- **<big>IsAlphaNumeric</big>** : check if the string is alphanumeric.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAlphaNumeric)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>IsJSON</big>** : check if the string is valid JSON.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJSON)]
|
||||
[[play](https://go.dev/play/p/8Kip1Itjiil)]
|
||||
@@ -2218,7 +2252,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
[[play](https://go.dev/play/p/AHA0r0AzIdC)]
|
||||
- **<big>IsIpPort</big>** : check if the string is ip:port.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIpPort)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/xUmls_b9L29)]
|
||||
- **<big>IsStrongPassword</big>** : check if the string is strong password.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsStrongPassword)]
|
||||
[[play](https://go.dev/play/p/QHdVcSQ3uDg)]
|
||||
@@ -2315,7 +2349,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
- **<big>TryUnwrap</big>** : check if err is nil then it returns a valid value. If err is not nil, TryUnwrap panics with err.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#TryUnwrap)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
- **<big>TryCatch</big>** : simple simulation of Java-style try-catch.
|
||||
- **<big>TryCatch</big>** : simple simulation of Java-style try-catch.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#TryCatch)]
|
||||
[[play](https://go.dev/play/p/D5Mdb0mRj0P)]
|
||||
|
||||
@@ -2324,6 +2358,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
#### [Contribution Guide](./CONTRIBUTION.md)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thank you to all the people who contributed to lancet!
|
||||
|
||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||
|
||||
259
README_zh-CN.md
259
README_zh-CN.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -101,7 +101,6 @@ func main() {
|
||||
- [Validator](#user-content-validator)
|
||||
- [Xerror](#user-content-xerror)
|
||||
|
||||
|
||||
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -214,6 +213,30 @@ import "github.com/duke-git/lancet/v2/concurrency"
|
||||
- **<big>Tee</big>** : 将一个 channel 分成两个 channel,直到取消上下文。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Tee)]
|
||||
[[play](https://go.dev/play/p/3TQPKnCirrP)]
|
||||
- **<big>NewKeyedLocker</big>** : NewKeyedLocker 创建一个新的 KeyedLocker,并为锁的过期设置指定的 TTL。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Do</big>** :为指定的键获取锁并执行提供的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Do)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>NewRWKeyedLocker</big>** :RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewRWKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>RLock</big>** : 为指定的键获取读锁并执行提供的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#RLock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Lock</big>** : 为指定的键获取锁并执行提供的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Lock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>NewTryKeyedLocker</big>** : 创建一个 TryKeyedLocker 实例,TryKeyedLocker 是 KeyedLocker 的非阻塞版本。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewTryKeyedLocker)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>TryLock</big>** : TryLock 尝试获取指定键的锁。如果锁成功获取,则返回 true,否则返回 false。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#TryLock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>Unlock</big>** : 释放指定键的锁。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Unlock)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="condition"> 4. condition 包含一些用于条件判断的函数。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -319,19 +342,19 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
|
||||
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
||||
- **<big>ToStdBase64</big>** : 将值转换为StdBase64编码的字符串。
|
||||
- **<big>ToStdBase64</big>** : 将值转换为 StdBase64 编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)]
|
||||
[[play](https://go.dev/play/p/_fLJqJD3NMo)]
|
||||
- **<big>ToUrlBase64</big>** : 将值转换为url Base64编码的字符串。
|
||||
- **<big>ToUrlBase64</big>** : 将值转换为 url Base64 编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)]
|
||||
[[play](https://go.dev/play/p/C_d0GlvEeUR)]
|
||||
- **<big>ToRawStdBase64</big>** : 将值转换为RawStdBase64编码的字符串。
|
||||
- **<big>ToRawStdBase64</big>** : 将值转换为 RawStdBase64 编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)]
|
||||
[[play](https://go.dev/play/p/wSAr3sfkDcv)]
|
||||
- **<big>ToRawUrlBase64</big>** : 将值转换为RawUrlBase64编码的字符串。
|
||||
- **<big>ToRawUrlBase64</big>** : 将值转换为 RawUrlBase64 编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)]
|
||||
[[play](https://go.dev/play/p/HwdDPFcza1O)]
|
||||
- **<big>ToBigInt</big>** : 将整数转为*big.Int。
|
||||
- **<big>ToBigInt</big>** : 将整数转为\*big.Int。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToBigInt)]
|
||||
[[play](https://go.dev/play/p/X3itkCxwB_x)]
|
||||
|
||||
@@ -474,19 +497,19 @@ import "github.com/duke-git/lancet/v2/cryptor"
|
||||
- **<big>RsaDecrypt</big>** : 用私钥文件 rsa 解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecrypt)]
|
||||
[[play](https://go.dev/play/p/7_zo6mrx-eX)]
|
||||
- **<big>GenerateRsaKeyPair</big>** : 创建rsa公钥私钥和key。
|
||||
- **<big>GenerateRsaKeyPair</big>** : 创建 rsa 公钥私钥和 key。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKeyPair)]
|
||||
[[play](https://go.dev/play/p/sSVmkfENKMz)]
|
||||
- **<big>RsaEncryptOAEP</big>** : rsa OAEP加密。
|
||||
- **<big>RsaEncryptOAEP</big>** : rsa OAEP 加密。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncryptOAEP)]
|
||||
[[play](https://go.dev/play/p/sSVmkfENKMz)]
|
||||
- **<big>RsaDecryptOAEP</big>** : rsa OAEP解密。
|
||||
- **<big>RsaDecryptOAEP</big>** : rsa OAEP 解密。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecryptOAEP)]
|
||||
[[play](https://go.dev/play/p/sSVmkfENKMz)]
|
||||
- **<big>RsaSign</big>** : 应用RSA算法签名数据。
|
||||
- **<big>RsaSign</big>** : 应用 RSA 算法签名数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaSign)]
|
||||
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
|
||||
- **<big>RsaVerifySign</big>** : 验证数据的签名是否符合RSA算法。
|
||||
- **<big>RsaVerifySign</big>** : 验证数据的签名是否符合 RSA 算法。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)]
|
||||
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
|
||||
|
||||
@@ -509,22 +532,22 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/nT1heB1KUUK)]
|
||||
- **<big>AddWeek</big>** : 将日期加/减星期数.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddWeek)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/M9TqdMiaA2p)]
|
||||
- **<big>AddMonth</big>** : 将日期加/减月数.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMonth)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/DLoiOnpLvsN)]
|
||||
- **<big>AddYear</big>** : 将日期加/减分年数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYear)]
|
||||
[[play](https://go.dev/play/p/MqW2ujnBx10)]
|
||||
- **<big>AddDaySafe</big>** : 增加/减少指定的天数,并确保日期是有效日期。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddDaySafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/JTohZFpoDJ3)]
|
||||
- **<big>AddMonthSafe</big>** : 增加/减少指定的月份,并确保日期是有效日期。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMonthSafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KLw0lo6mbVW)]
|
||||
- **<big>AddYearSafe</big>** : 增加/减少指定的年份,并确保日期是有效日期。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYearSafe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KVGXWZZ54ZH)]
|
||||
- **<big>BeginOfMinute</big>** : 返回指定时间的分钟开始时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMinute)]
|
||||
[[play](https://go.dev/play/p/ieOLVJ9CiFT)]
|
||||
@@ -536,7 +559,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/94m_UT6cWs9)]
|
||||
- **<big>BeginOfWeek</big>** : 返回指定时间的每周开始时间,默认开始时间星期日。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfWeek)]
|
||||
[[play](https://go.dev/play/p/ynjoJPz7VNV)]
|
||||
[[play](https://go.dev/play/p/DCHdcL6gnfV)]
|
||||
- **<big>BeginOfMonth</big>** : 返回指定时间的当月开始时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMonth)]
|
||||
[[play](https://go.dev/play/p/bWXVFsmmzwL)]
|
||||
@@ -554,7 +577,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/eMBOvmq5Ih1)]
|
||||
- **<big>EndOfWeek</big>** : 返回指定时间的星期结束时间,默认结束时间星期六。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfWeek)]
|
||||
[[play](https://go.dev/play/p/i08qKXD9flf)]
|
||||
[[play](https://go.dev/play/p/mGSA162YgX9)]
|
||||
- **<big>EndOfMonth</big>** : 返回指定时间的月份结束时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfMonth)]
|
||||
[[play](https://go.dev/play/p/_GWh10B3Nqi)]
|
||||
@@ -645,7 +668,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
- **<big>DaysBetween</big>** : 返回两个日期之间的天数差。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)]
|
||||
[[play](https://go.dev/play/p/qD6qGb3TbOy)]
|
||||
- **<big>GenerateDatetimesBetween</big>** : 生成从start到end的所有日期时间的字符串列表。
|
||||
- **<big>GenerateDatetimesBetween</big>** : 生成从 start 到 end 的所有日期时间的字符串列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)]
|
||||
[[play](https://go.dev/play/p/6kHBpAxD9ZC)]
|
||||
- **<big>Min</big>** : 返回最早时间。
|
||||
@@ -658,7 +681,6 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#MaxMin)]
|
||||
[[play](https://go.dev/play/p/rbW51cDtM_2)]
|
||||
|
||||
|
||||
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如:list, linklist, stack, queue, set, tree, graph。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -702,36 +724,36 @@ import "github.com/duke-git/lancet/v2/eventbus"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>NewEventBus</big>** : 创建EventBus实例。
|
||||
- **<big>NewEventBus</big>** : 创建 EventBus 实例。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#NewEventBus)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gHbOPV_NUOJ)]
|
||||
- **<big>Subscribe</big>** : 订阅具有特定事件主题和监听函数的事件。支持异步,事件优先级,事件过滤器。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Subscribe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/EYGf_8cHei-)]
|
||||
- **<big>Unsubscribe</big>** : 取消订阅具有特定事件主题的事件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Unsubscribe)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/Tmh7Ttfvprf)]
|
||||
- **<big>Publish</big>** : 发布一个带有特定事件主题和数据负载的事件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Publish)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gHTtVexFSH9)]
|
||||
- **<big>ClearListeners</big>** : 清空所有事件监听器。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#ClearListeners)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/KBfBYlKPgqD)]
|
||||
- **<big>ClearListenersByTopic</big>** : 清空特定事件监听器。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#ClearListenersByTopic)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gvMljmJOZmU)]
|
||||
- **<big>GetListenersCount</big>** : 获取特定事件的监听器数量。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetListenersCount)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/8VPJsMQgStM)]
|
||||
- **<big>GetAllListenersCount</big>** : 获取所有事件的监听器数量。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetAllListenersCount)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>GetEvents</big>** : 获取所有事件的topic。
|
||||
[[play](https://go.dev/play/p/PUlr0xcpEOz)]
|
||||
- **<big>GetEvents</big>** : 获取所有事件的 topic。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetEvents)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/etgjjcOtAjX)]
|
||||
- **<big>SetErrorHandler</big>** : 设置事件的错误处理函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#SetErrorHandler)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
[[play](https://go.dev/play/p/gmB0gnFe5mc)]
|
||||
|
||||
<h3 id="fileutil"> 10. fileutil 包含文件基本操作。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -777,6 +799,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>RemoveFile</big>** : 删除文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveFile)]
|
||||
[[play](https://go.dev/play/p/P2y0XW8a1SH)]
|
||||
- **<big>RemoveDir</big>** : 删除目录,支持传入删除前的回调函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveDir)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>ReadFileToString</big>** : 读取文件内容并返回字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFileToString)]
|
||||
[[play](https://go.dev/play/p/cmfwp_5SQTp)]
|
||||
@@ -809,9 +834,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
|
||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||
- **<big>WriteCsvFile</big>** : 向csv文件写入切片数据。
|
||||
- **<big>WriteCsvFile</big>** : 向 csv 文件写入切片数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
|
||||
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
|
||||
- **<big>WriteMapsToCsv</big>** : 将 map 切片写入 csv 文件中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
|
||||
[[play](https://go.dev/play/p/umAIomZFV1c)]
|
||||
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
|
||||
@@ -820,7 +845,7 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>WriteStringToFile</big>** : 将字符串写入文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteStringToFile)]
|
||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||
- **<big>ReadFile</big>** : 读取文件或者URL。
|
||||
- **<big>ReadFile</big>** : 读取文件或者 URL。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
|
||||
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
|
||||
@@ -828,11 +853,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
|
||||
[[play](https://go.dev/play/p/teMXnCsdSEw)]
|
||||
- **<big>GetExeOrDllVersion</big>** : 返回exe,dll文件版本号(仅Window平台)。
|
||||
- **<big>GetExeOrDllVersion</big>** : 返回 exe,dll 文件版本号(仅 Window 平台)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#GetExeOrDllVersion)]
|
||||
[[play](https://go.dev/play/p/iLRrDBhE38E)]
|
||||
|
||||
|
||||
<h3 id="formatter"> 11. formatter 格式化器包含一些数据格式化处理方法。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -901,33 +925,31 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
- **<big>Pipeline</big>** : 从右至左执行函数列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
|
||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数,该函数的签名与apply函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
||||
- **<big>AcceptIf</big>** : AcceptIf 函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
|
||||
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
|
||||
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。
|
||||
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 and 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
|
||||
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
|
||||
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。
|
||||
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 or 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
|
||||
[[play](https://go.dev/play/p/LitCIsDFNDA)]
|
||||
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
|
||||
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
|
||||
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或nor的操作。
|
||||
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或 nor 的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
|
||||
[[play](https://go.dev/play/p/2KdCoBEOq84)]
|
||||
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与nand的操作。
|
||||
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与 nand 的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
|
||||
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
|
||||
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或xnor的操作。
|
||||
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或 xnor 的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
|
||||
[[play](https://go.dev/play/p/FJxko8SFbqc)]
|
||||
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
|
||||
|
||||
|
||||
<h3 id="maputil"> 13. maputil 包括一些操作 map 的函数。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -1005,13 +1027,13 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
|
||||
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
|
||||
- **<big>MapToStruct</big>** : 将map转成struct。
|
||||
- **<big>MapToStruct</big>** : 将 map 转成 struct。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
|
||||
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
|
||||
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。
|
||||
- **<big>ToSortedSlicesDefault</big>** : 将 map 的 key 和 value 转化成两个根据 key 的值从小到大排序的切片,value 切片中元素的位置与 key 对应。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||
[[play](https://go.dev/play/p/43gEM2po-qy)]
|
||||
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。
|
||||
- **<big>ToSortedSlicesWithComparator</big>** : 将 map 的 key 和 value 转化成两个使用比较器函数根据 key 的值自定义排序规则的切片,value 切片中元素的位置与 key 对应。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
|
||||
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
|
||||
- **<big>NewOrderedMap</big>** : 创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。
|
||||
@@ -1026,7 +1048,7 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>OrderedMap_Delete</big>** : 删除给定键的键值对。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Delete)]
|
||||
[[play](ttps://go.dev/play/p/5bIi4yaZ3K-)]
|
||||
- **<big>OrderedMap_Clear</big>** : 清空map数据。
|
||||
- **<big>OrderedMap_Clear</big>** : 清空 map 数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Clear)]
|
||||
[[play](https://go.dev/play/p/8LwoJyEfuFr)]
|
||||
- **<big>OrderedMap_Front</big>** : 返回第一个键值对。
|
||||
@@ -1050,7 +1072,7 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>OrderedMap_Len</big>** : 返回键值对的数量。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Len)]
|
||||
[[play](https://go.dev/play/p/cLe6z2VX5N-)]
|
||||
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回true。
|
||||
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Contains)]
|
||||
[[play](https://go.dev/play/p/QuwqqnzwDNX)]
|
||||
- **<big>OrderedMap_Iter</big>** : 返回按顺序产生键值对的通道。
|
||||
@@ -1059,13 +1081,13 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>OrderedMap_ReverseIter</big>** : 返回以相反顺序产生键值对的通道。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_ReverseIter)]
|
||||
[[play](https://go.dev/play/p/8Q0ssg6hZzO)]
|
||||
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序map key。
|
||||
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序 map key。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_SortByKey)]
|
||||
[[play](https://go.dev/play/p/N7hjD_alZPq)]
|
||||
- **<big>OrderedMap_MarshalJSON</big>** : 实现json.Marshaler接口。
|
||||
- **<big>OrderedMap_MarshalJSON</big>** : 实现 json.Marshaler 接口。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_MarshalJSON)]
|
||||
[[play](https://go.dev/play/p/C-wAwydIAC7)]
|
||||
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现json.Unmarshaler接口。
|
||||
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现 json.Unmarshaler 接口。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
|
||||
[[play](https://go.dev/play/p/8C3MvJ3-mut)]
|
||||
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
|
||||
@@ -1092,12 +1114,15 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>ConcurrentMap_Range</big>** : 为 map 中每个键和值顺序调用迭代器。 如果 iterator 返回 false,则停止迭代。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Range)]
|
||||
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
|
||||
- **<big>SortByKey</big>** : 对传入的map根据key进行排序。
|
||||
- **<big>SortByKey</big>** : 对传入的 map 根据 key 进行排序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#SortByKey)]
|
||||
[[play](https://go.dev/play/p/PVdmBSnm6P_W)]
|
||||
- **<big>GetOrDefault</big>** : 返回给定键的值,如果键不存在,则返回默认值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
|
||||
[[play](https://go.dev/play/p/99QjSYSBdiM)]
|
||||
- **<big>FindValuesBy</big>** : 返回一个切片,包含满足给定谓词判断函数的 map 中的值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#FindValuesBy)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="mathutil"> 14. mathutil 包实现了一些数学计算的函数。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -1143,16 +1168,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
|
||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
|
||||
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留 n 位小数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
|
||||
[[play](https://go.dev/play/p/8hOeSADZPCo)]
|
||||
- **<big>CeilToString</big>** : 向上舍入(进一法),保留n位小数,返回字符串。
|
||||
- **<big>CeilToString</big>** : 向上舍入(进一法),保留 n 位小数,返回字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
|
||||
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
|
||||
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留n位小数。
|
||||
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留 n 位小数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
|
||||
[[play](https://go.dev/play/p/vbCBrQHZEED)]
|
||||
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留n位小数,返回字符串。
|
||||
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留 n 位小数,返回字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
|
||||
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
|
||||
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
|
||||
@@ -1203,10 +1228,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>StdDev</big>** : 计算标准差。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#StdDev)]
|
||||
[[play](https://go.dev/play/p/FkNZDXvHD2l)]
|
||||
- **<big>Permutation</big>** : 计算排列数P(n, k)。
|
||||
- **<big>Permutation</big>** : 计算排列数 P(n, k)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Permutation)]
|
||||
[[play](https://go.dev/play/p/MgobwH_FOxj)]
|
||||
- **<big>Combination</big>** : 计算组合数C(n, k)。
|
||||
- **<big>Combination</big>** : 计算组合数 C(n, k)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Combination)]
|
||||
[[play](https://go.dev/play/p/ENFQRDQUFi9)]
|
||||
|
||||
@@ -1282,6 +1307,12 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
- **<big>IsTelnetConnected</big>** : 检查能否 telnet 到主机。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsTelnetConnected)]
|
||||
[[play](https://go.dev/play/p/yiLCGtQv_ZG)]
|
||||
- **<big>BuildUrl</big>** : 创建 url 字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#BuildUrl)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>AddQueryParams</big>** : 向 url 添加查询参数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#AddQueryParams)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
|
||||
<h3 id="pointer"> 16. pointer 包支持一些指针类型的操作。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -1339,28 +1370,28 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
|
||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
|
||||
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机 int 切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
|
||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSymbolChar)]
|
||||
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
|
||||
- **<big>RandFloat</big>** : 生成随机float64数字,可以指定范围和精度。
|
||||
- **<big>RandFloat</big>** : 生成随机 float64 数字,可以指定范围和精度。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloat)]
|
||||
[[play](https://go.dev/play/p/zbD_tuobJtr)]
|
||||
- **<big>RandFloats</big>** : 生成随机float64数字切片,可以指定长度,范围和精度.
|
||||
- **<big>RandFloats</big>** : 生成随机 float64 数字切片,可以指定长度,范围和精度.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
|
||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||
- **<big>RandStringSlice</big>** : 生成随机字符串slice。
|
||||
- **<big>RandStringSlice</big>** : 生成随机字符串 slice。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)]
|
||||
[[play](https://go.dev/play/p/2_-PiDv3tGn)]
|
||||
- **<big>RandBool</big>** : 生成随机bool值(true or false)。
|
||||
- **<big>RandBool</big>** : 生成随机 bool 值(true or false)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBool)]
|
||||
[[play](https://go.dev/play/p/to6BLc26wBv)]
|
||||
- **<big>RandBoolSlice</big>** : 生成特定长度的随机bool slice。
|
||||
- **<big>RandBoolSlice</big>** : 生成特定长度的随机 bool slice。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBoolSlice)]
|
||||
[[play](https://go.dev/play/p/o-VSjPjnILI)]
|
||||
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机int切片,数值范围[min, max)。
|
||||
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机 int 切片,数值范围[min, max)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandIntSlice)]
|
||||
[[play](https://go.dev/play/p/GATTQ5xTEG8)]
|
||||
- **<big>RandFromGivenSlice</big>** : 从给定切片中随机生成元素。
|
||||
@@ -1390,7 +1421,7 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
- **<big>RetryFunc</big>** : 重试执行的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryFunc)]
|
||||
[[play](https://go.dev/play/p/nk2XRmagfVF)]
|
||||
- **<big>RetryTimes</big>** : 设置重试次数,默认5。
|
||||
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
|
||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||
- **<big>BackoffStrategy</big>** : 定义计算退避间隔的方法的接口。
|
||||
@@ -1405,8 +1436,6 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||
[[play](https://go.dev/play/p/xp1avQmn16X)]
|
||||
|
||||
|
||||
|
||||
<h3 id="slice"> 19. slice 包含操作切片的方法集合。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -1477,7 +1506,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/b9iygtgsHI1)]
|
||||
- **<big>EqualUnordered</big>** : 检查两个切片是否相等,元素数量相同,值相等,不考虑元素顺序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualUnordered)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/n8fSc2w8ZgX)]
|
||||
- **<big>Every</big>** : 如果切片中的所有值都通过谓词函数,则返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Every)]
|
||||
[[play](https://go.dev/play/p/R8U6Sl-j8cD)]
|
||||
@@ -1511,7 +1540,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEach)]
|
||||
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
|
||||
- **<big>ForEachConcurrent</big>** : 对slice并发执行foreach操作。
|
||||
- **<big>ForEachConcurrent</big>** : 对 slice 并发执行 foreach 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachConcurrent)]
|
||||
[[play](https://go.dev/play/p/kT4XW7DKVoV)]
|
||||
- **<big>ForEachWithBreak</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。
|
||||
@@ -1544,7 +1573,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Map</big>** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Map)]
|
||||
[[play](https://go.dev/play/p/biaTefqPquw)]
|
||||
- **<big>MapConcurrent</big>** : 对slice并发执行map操作。
|
||||
- **<big>MapConcurrent</big>** : 对 slice 并发执行 map 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#MapConcurrent)]
|
||||
[[play](https://go.dev/play/p/H1ehfPkPen0)]
|
||||
- **<big>Merge</big>** : 合并多个切片(不会消除重复元素)。
|
||||
@@ -1553,9 +1582,9 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Reverse</big>** : 反转切片中的元素顺序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reverse)]
|
||||
[[play](https://go.dev/play/p/8uI8f1lwNrQ)]
|
||||
- **<big>ReverseCopy</big>** : 反转切片中的元素顺序, 不改变原slice。
|
||||
- **<big>ReverseCopy</big>** : 反转切片中的元素顺序, 不改变原 slice。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ReverseCopy)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/c9arEaP7Cg-)]
|
||||
- **<big>Reduce<sup>deprecated</sup></big>** : 将切片中的元素依次运行 iteratee 函数,返回运行结果。(废弃:建议使用 ReduceBy)
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reduce)]
|
||||
[[play](https://go.dev/play/p/_RfXJJWIsIm)]
|
||||
@@ -1565,7 +1594,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>ReduceRight</big>** : 类似 ReduceBy 操作,迭代切片元素顺序从右至左。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceRight)]
|
||||
[[play](https://go.dev/play/p/qT9dZC03A1K)]
|
||||
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发reduce操作。
|
||||
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发 reduce 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceConcurrent)]
|
||||
[[play](https://go.dev/play/p/Tjwe6OtaG07)]
|
||||
- **<big>Replace</big>** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。
|
||||
@@ -1582,7 +1611,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/YHvhnWGU3Ge)]
|
||||
- **<big>ShuffleCopy</big>** : 随机打乱切片中的元素顺序, 不改变原切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ShuffleCopy)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/vqDa-Gs1vT0)]
|
||||
- **<big>IsAscending</big>** : 检查切片元素是否按升序排列。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsAscending)]
|
||||
[[play](https://go.dev/play/p/9CtsFjet4SH)]
|
||||
@@ -1628,7 +1657,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
|
||||
[[play](https://go.dev/play/p/rwSacr-ZHsR)]
|
||||
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
|
||||
- **<big>UniqueByField</big>** : 根据 struct 字段对 struct 切片去重复。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
|
||||
[[play](https://go.dev/play/p/6cifcZSPIGu)]
|
||||
- **<big>UniqueByConcurrent</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
|
||||
@@ -1651,13 +1680,13 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Join</big>** : 用指定的分隔符链接切片元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Join)]
|
||||
[[play](https://go.dev/play/p/huKzqwNDD7V)]
|
||||
- **<big>Partition</big>** : 根据给定的predicate判断函数分组切片元素。
|
||||
- **<big>Partition</big>** : 根据给定的 predicate 判断函数分组切片元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Partition)]
|
||||
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
|
||||
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
|
||||
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为 0 时返回下标-1。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
|
||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
|
||||
- **<big>SetToDefaultIf</big>** : 根据给定给定的 predicate 判定函数来修改切片中的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
|
||||
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
|
||||
- **<big>Break</big>** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。
|
||||
@@ -1704,13 +1733,13 @@ import "github.com/duke-git/lancet/v2/stream"
|
||||
- **<big>Concat</big>** : 创建一个延迟连接 stream,其元素是第一个 stream 的所有元素,后跟第二个 stream 的全部元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Concat)]
|
||||
[[play](https://go.dev/play/p/HM4OlYk_OUC)]
|
||||
- **<big>Distinct</big>** : 创建并返回一个stream,用于删除重复的项。
|
||||
- **<big>Distinct</big>** : 创建并返回一个 stream,用于删除重复的项。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Distinct)]
|
||||
[[play](https://go.dev/play/p/eGkOSrm64cB)]
|
||||
- **<big>Filter</big>** : 返回一个通过判定函数的stream。
|
||||
- **<big>Filter</big>** : 返回一个通过判定函数的 stream。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Filter)]
|
||||
[[play](https://go.dev/play/p/MFlSANo-buc)]
|
||||
- **<big>FilterConcurrent</big>** : 对slice并发执行filter操作。
|
||||
- **<big>FilterConcurrent</big>** : 对 slice 并发执行 filter 操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FilterConcurrent)]
|
||||
[[play](https://go.dev/play/p/t_pkwerIRVx)]
|
||||
- **<big>Map</big>** : 返回一个 stream,该 stream 由将给定函数应用于源 stream 元素的元素组成。
|
||||
@@ -1767,10 +1796,10 @@ import "github.com/duke-git/lancet/v2/stream"
|
||||
- **<big>ToSlice</big>** : 返回 stream 中的元素切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
|
||||
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
|
||||
- **<big>IndexOf</big>** : 返回在stream中找到值的第一个匹配项的索引,如果找不到值,则返回-1。
|
||||
- **<big>IndexOf</big>** : 返回在 stream 中找到值的第一个匹配项的索引,如果找不到值,则返回-1。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#IndexOf)]
|
||||
[[play](https://go.dev/play/p/tBV5Nc-XDX2)]
|
||||
- **<big>LastIndexOf</big>** : 返回在stream中找到值的最后一个匹配项的索引,如果找不到值,则返回-1。
|
||||
- **<big>LastIndexOf</big>** : 返回在 stream 中找到值的最后一个匹配项的索引,如果找不到值,则返回-1。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#LastIndexOf)]
|
||||
[[play](https://go.dev/play/p/CjeoNw2eac_G)]
|
||||
|
||||
@@ -1933,7 +1962,7 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
|
||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
|
||||
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串 start 和终止字符串 end 直接的子字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
|
||||
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
|
||||
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
|
||||
@@ -1951,7 +1980,7 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>Rotate</big>** : 按指定的字符数旋转字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)]
|
||||
[[play](https://go.dev/play/p/Kf03iOeT5bd)]
|
||||
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为map中的相应值。
|
||||
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为 map 中的相应值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)]
|
||||
[[play](https://go.dev/play/p/cXSuFvyZqv9)]
|
||||
- **<big>RegexMatchAllGroups</big>** : 使用正则表达式匹配字符串中的所有子组并返回结果。
|
||||
@@ -1962,8 +1991,7 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/Ay9UIk7Rum9)]
|
||||
- **<big>FindAllOccurrences</big>** : 返回子字符串在字符串中所有出现的位置。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#FindAllOccurrences)]
|
||||
[[play](todo)]
|
||||
|
||||
[[play](https://go.dev/play/p/uvyA6azGLB1)]
|
||||
|
||||
<h3 id="system"> 23. system 包含 os, runtime, shell command 的相关函数。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -2009,12 +2037,10 @@ import "github.com/duke-git/lancet/v2/system"
|
||||
- **<big>KillProcess</big>** : 杀掉进程。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)]
|
||||
[[play](https://go.dev/play/p/XKmvV-ExBWa)]
|
||||
- **<big>GetProcessInfo</big>** : 根据进程id获取进程信息。
|
||||
- **<big>GetProcessInfo</big>** : 根据进程 id 获取进程信息。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)]
|
||||
[[play](https://go.dev/play/p/NQDVywEYYx7)]
|
||||
|
||||
|
||||
|
||||
<h3 id="tuple"> 24. Tuple 包实现一个元组数据类型。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -2176,7 +2202,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsCreditCard</big>** : 验证字符串是否是信用卡号码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsCreditCard)]
|
||||
[[play](https://go.dev/play/p/sNwwL6B0-v4)]
|
||||
- **<big>IsDns</big>** : 验证字符串是否是有效dns。
|
||||
- **<big>IsDns</big>** : 验证字符串是否是有效 dns。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsDns)]
|
||||
[[play](https://go.dev/play/p/jlYApVLLGTZ)]
|
||||
- **<big>IsEmail</big>** : 验证字符串是否是有效电子邮件地址。
|
||||
@@ -2197,6 +2223,9 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsNumberStr</big>** : 验证字符串是否是可以转换为数字。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsNumberStr)]
|
||||
[[play](https://go.dev/play/p/LzaKocSV79u)]
|
||||
- **<big>IsAlphaNumeric</big>** : 验证字符串是字母或数字。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAlphaNumeric)]
|
||||
[[play](https://go.dev/play/p/todo)]
|
||||
- **<big>IsJSON</big>** : 验证字符串是否是有效 json。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJSON)]
|
||||
[[play](https://go.dev/play/p/8Kip1Itjiil)]
|
||||
@@ -2212,15 +2241,15 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsIp</big>** : 验证字符串是否是 ip 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIp)]
|
||||
[[play](https://go.dev/play/p/FgcplDvmxoD)]
|
||||
- **<big>IsIpV4</big>** : 验证字符串是否是ipv4地址。
|
||||
- **<big>IsIpV4</big>** : 验证字符串是否是 ipv4 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV4)]
|
||||
[[play](https://go.dev/play/p/zBGT99EjaIu)]
|
||||
- **<big>IsIpV6</big>** : 验证字符串是否是ipv6地址。
|
||||
- **<big>IsIpV6</big>** : 验证字符串是否是 ipv6 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV6)]
|
||||
[[play](https://go.dev/play/p/AHA0r0AzIdC)]
|
||||
- **<big>IsIpPort</big>** : 检查字符串是否是ip:port格式。
|
||||
- **<big>IsIpPort</big>** : 检查字符串是否是 ip:port 格式。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpPort)]
|
||||
[[play](todo)]
|
||||
[[play](https://go.dev/play/p/xUmls_b9L29)]
|
||||
- **<big>IsStrongPassword</big>** : 验证字符串是否是强密码:(字母+数字+特殊字符)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsStrongPassword)]
|
||||
[[play](https://go.dev/play/p/QHdVcSQ3uDg)]
|
||||
@@ -2233,10 +2262,10 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsZeroValue</big>** : 判断传入的参数值是否为零值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsZeroValue)]
|
||||
[[play](https://go.dev/play/p/UMrwaDCi_t4)]
|
||||
- **<big>IsGBK</big>** : 检查数据编码是否为gbk(汉字内部代码扩展规范)。
|
||||
- **<big>IsGBK</big>** : 检查数据编码是否为 gbk(汉字内部代码扩展规范)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsGBK)]
|
||||
[[play](https://go.dev/play/p/E2nt3unlmzP)]
|
||||
- **<big>IsASCII</big>** : 验证字符串全部为ASCII字符。
|
||||
- **<big>IsASCII</big>** : 验证字符串全部为 ASCII 字符。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsASCII)]
|
||||
[[play](https://go.dev/play/p/hfQNPLX0jNa)]
|
||||
- **<big>IsPrintable</big>** : 检查字符串是否全部为可打印字符。
|
||||
@@ -2251,13 +2280,13 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsBase64URL</big>** : 检查字符串是否是有效的 base64 url。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBase64URL)]
|
||||
[[play](https://go.dev/play/p/vhl4mr8GZ6S)]
|
||||
- **<big>IsJWT</big>** : 检查字符串是否是有效的JSON Web Token (JWT)。
|
||||
- **<big>IsJWT</big>** : 检查字符串是否是有效的 JSON Web Token (JWT)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJWT)]
|
||||
[[play](https://go.dev/play/p/R6Op7heJbKI)]
|
||||
- **<big>IsVisa</big>** : 检查字符串是否是有效的visa卡号。
|
||||
- **<big>IsVisa</big>** : 检查字符串是否是有效的 visa 卡号。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsVisa)]
|
||||
[[play](https://go.dev/play/p/SdS2keOyJsl)]
|
||||
- **<big>IsMasterCard</big>** : 检查字符串是否是有效的MasterCard卡号。
|
||||
- **<big>IsMasterCard</big>** : 检查字符串是否是有效的 MasterCard 卡号。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsMasterCard)]
|
||||
[[play](https://go.dev/play/p/CwWBFRrG27b)]
|
||||
- **<big>IsAmericanExpress</big>** : 检查字符串是否是有效的 American Express 卡号。
|
||||
@@ -2293,31 +2322,31 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
- **<big>XError_Unwrap</big>** : 解构 XEerror 为 error 对象。适配 github.com/pkg/errors。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Unwrap)
|
||||
[[play](https://go.dev/play/p/VUXJ8BST4c6)]
|
||||
- **<big>XError_With</big>** : 添加与XError对象的键和值。
|
||||
- **<big>XError_With</big>** : 添加与 XError 对象的键和值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_With)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_Id</big>** : 设置XError对象的id。
|
||||
- **<big>XError_Id</big>** : 设置 XError 对象的 id。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Id)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Is</big>** : 检查目标error是否为XError,两个错误中的error.id是否匹配。
|
||||
- **<big>XError_Is</big>** : 检查目标 error 是否为 XError,两个错误中的 error.id 是否匹配。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Is)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Values</big>** : 返回由With设置的键和值的映射。将合并所有XError键和值。
|
||||
- **<big>XError_Values</big>** : 返回由 With 设置的键和值的映射。将合并所有 XError 键和值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Values)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_StackTrace</big>** : 返回与pkg/error兼容的堆栈信息。
|
||||
- **<big>XError_StackTrace</big>** : 返回与 pkg/error 兼容的堆栈信息。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_StackTrace)]
|
||||
[[play](https://go.dev/play/p/6FAvSQpa7pc)]
|
||||
- **<big>XError_Info</big>** : 返回可打印的XError对象信息。
|
||||
- **<big>XError_Info</big>** : 返回可打印的 XError 对象信息。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Info)]
|
||||
[[play](https://go.dev/play/p/1ZX0ME1F-Jb)]
|
||||
- **<big>XError_Error</big>** : 实现标准库的error接口。
|
||||
- **<big>XError_Error</big>** : 实现标准库的 error 接口。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Error)]
|
||||
[[play](https://go.dev/play/p/w4oWZts7q7f)]
|
||||
- **<big>TryUnwrap</big>** : 检查error, 如果err为nil则展开,则它返回一个有效值,如果err不是nil则Unwrap使用err发生panic。
|
||||
- **<big>TryUnwrap</big>** : 检查 error, 如果 err 为 nil 则展开,则它返回一个有效值,如果 err 不是 nil 则 Unwrap 使用 err 发生 panic。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#TryUnwrap)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
- **<big>TryCatch</big>** : 简单实现的java风格异常处理(try-catch-finally)。
|
||||
- **<big>TryCatch</big>** : 简单实现的 java 风格异常处理(try-catch-finally)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#TryCatch)]
|
||||
[[play](https://go.dev/play/p/D5Mdb0mRj0P)]
|
||||
|
||||
@@ -2327,7 +2356,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
## 贡献者
|
||||
|
||||
感谢所有为lancet贡献过代码的人!
|
||||
感谢所有为 lancet 贡献过代码的人!
|
||||
|
||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
||||
@@ -2335,4 +2364,4 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
## GitHub Stars
|
||||
|
||||
[](https://star-history.com/#duke-git/lancet&Date)
|
||||
[](https://star-history.com/#duke-git/lancet&Date)
|
||||
|
||||
@@ -3,6 +3,7 @@ package concurrency
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -199,3 +200,159 @@ func ExampleChannel_Bridge() {
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleKeyedLocker_Do() {
|
||||
locker := NewKeyedLocker[string](2 * time.Second)
|
||||
|
||||
task := func() {
|
||||
fmt.Println("Executing task...")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Task completed.")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := locker.Do(ctx, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
if err := locker.Do(ctx2, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
}
|
||||
|
||||
func ExampleRWKeyedLocker_Lock() {
|
||||
locker := NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.Lock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
|
||||
func ExampleRWKeyedLocker_RLock() {
|
||||
locker := NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.RLock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
|
||||
func ExampleTryKeyedLocker() {
|
||||
locker := NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
|
||||
func ExampleTryKeyedLocker_TryLock() {
|
||||
locker := NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
if locker.TryLock(key) {
|
||||
time.Sleep(2 * time.Second)
|
||||
locker.Unlock(key)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
locker.Unlock(key)
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
// wait for the goroutine to finish
|
||||
<-done
|
||||
|
||||
fmt.Println("Retrying...")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Lock failed
|
||||
// Retrying...
|
||||
// Lock acquired
|
||||
// Lock released
|
||||
}
|
||||
|
||||
249
concurrency/keyed_locker.go
Normal file
249
concurrency/keyed_locker.go
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright 2025 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, locker.
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.
|
||||
type KeyedLocker[K comparable] struct {
|
||||
locks sync.Map
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
type lockEntry struct {
|
||||
mu sync.Mutex
|
||||
ref int32
|
||||
timer atomic.Pointer[time.Timer]
|
||||
}
|
||||
|
||||
// NewKeyedLocker creates a new KeyedLocker with the specified TTL for lock expiration.
|
||||
// The TTL is used to automatically release locks that are no longer held.
|
||||
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] {
|
||||
return &KeyedLocker[K]{ttl: ttl}
|
||||
}
|
||||
|
||||
// Do acquires a lock for the specified key and executes the provided function.
|
||||
// It returns an error if the context is canceled before the function completes.
|
||||
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error {
|
||||
entry := l.acquire(key)
|
||||
defer l.release(key, entry, key)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
fn()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// acquire tries to acquire a lock for the specified key.
|
||||
func (l *KeyedLocker[K]) acquire(key K) *lockEntry {
|
||||
lock, _ := l.locks.LoadOrStore(key, &lockEntry{})
|
||||
entry := lock.(*lockEntry)
|
||||
|
||||
atomic.AddInt32(&entry.ref, 1)
|
||||
if t := entry.timer.Swap(nil); t != nil {
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// release releases the lock for the specified key.
|
||||
func (l *KeyedLocker[K]) release(key K, entry *lockEntry, rawKey K) {
|
||||
if atomic.AddInt32(&entry.ref, -1) == 0 {
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
|
||||
if entry.ref == 0 {
|
||||
if t := entry.timer.Swap(nil); t != nil {
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
l.locks.Delete(rawKey)
|
||||
} else {
|
||||
if entry.timer.Load() == nil {
|
||||
t := time.AfterFunc(l.ttl, func() {
|
||||
l.release(key, entry, rawKey)
|
||||
})
|
||||
entry.timer.Store(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RWKeyedLocker is a read-write version of KeyedLocker.
|
||||
type RWKeyedLocker[K comparable] struct {
|
||||
locks sync.Map
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
type rwLockEntry struct {
|
||||
mu sync.RWMutex
|
||||
ref int32
|
||||
timer atomic.Pointer[time.Timer]
|
||||
}
|
||||
|
||||
// NewRWKeyedLocker creates a new RWKeyedLocker with the specified TTL for lock expiration.
|
||||
// The TTL is used to automatically release locks that are no longer held.
|
||||
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] {
|
||||
return &RWKeyedLocker[K]{ttl: ttl}
|
||||
}
|
||||
|
||||
// RLock acquires a read lock for the specified key and executes the provided function.
|
||||
// It returns an error if the context is canceled before the function completes.
|
||||
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error {
|
||||
entry := l.acquire(key)
|
||||
defer l.release(entry, key)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
entry.mu.RLock()
|
||||
defer entry.mu.RUnlock()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
fn()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Lock acquires a write lock for the specified key and executes the provided function.
|
||||
// It returns an error if the context is canceled before the function completes.
|
||||
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error {
|
||||
entry := l.acquire(key)
|
||||
defer l.release(entry, key)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
entry.mu.Lock()
|
||||
defer entry.mu.Unlock()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
fn()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// acquire tries to acquire a read lock for the specified key.
|
||||
func (l *RWKeyedLocker[K]) acquire(key K) *rwLockEntry {
|
||||
actual, _ := l.locks.LoadOrStore(key, &rwLockEntry{})
|
||||
entry := actual.(*rwLockEntry)
|
||||
atomic.AddInt32(&entry.ref, 1)
|
||||
|
||||
if t := entry.timer.Swap(nil); t != nil {
|
||||
t.Stop()
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// release releases the lock for the specified key.
|
||||
func (l *RWKeyedLocker[K]) release(entry *rwLockEntry, rawKey K) {
|
||||
if atomic.AddInt32(&entry.ref, -1) == 0 {
|
||||
timer := time.AfterFunc(l.ttl, func() {
|
||||
if atomic.LoadInt32(&entry.ref) == 0 {
|
||||
l.locks.Delete(rawKey)
|
||||
}
|
||||
})
|
||||
entry.timer.Store(timer)
|
||||
}
|
||||
}
|
||||
|
||||
// TryKeyedLocker is a non-blocking version of KeyedLocker.
|
||||
// It allows for trying to acquire a lock without blocking if the lock is already held.
|
||||
type TryKeyedLocker[K comparable] struct {
|
||||
mu sync.Mutex
|
||||
locks map[K]*casMutex
|
||||
}
|
||||
|
||||
// NewTryKeyedLocker creates a new TryKeyedLocker.
|
||||
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] {
|
||||
return &TryKeyedLocker[K]{locks: make(map[K]*casMutex)}
|
||||
}
|
||||
|
||||
// TryLock tries to acquire a lock for the specified key.
|
||||
// It returns true if the lock was acquired, false otherwise.
|
||||
func (l *TryKeyedLocker[K]) TryLock(key K) bool {
|
||||
l.mu.Lock()
|
||||
|
||||
lock, ok := l.locks[key]
|
||||
if !ok {
|
||||
lock = &casMutex{}
|
||||
l.locks[key] = lock
|
||||
}
|
||||
l.mu.Unlock()
|
||||
|
||||
return lock.TryLock()
|
||||
}
|
||||
|
||||
// Unlock releases the lock for the specified key.
|
||||
func (l *TryKeyedLocker[K]) Unlock(key K) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
lock, ok := l.locks[key]
|
||||
if ok {
|
||||
lock.Unlock()
|
||||
if lock.lock == 0 {
|
||||
delete(l.locks, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// casMutex is a simple mutex that uses atomic operations to provide a non-blocking lock.
|
||||
type casMutex struct {
|
||||
lock int32
|
||||
}
|
||||
|
||||
// TryLock tries to acquire the lock without blocking.
|
||||
// It returns true if the lock was acquired, false otherwise.
|
||||
func (m *casMutex) TryLock() bool {
|
||||
return atomic.CompareAndSwapInt32(&m.lock, 0, 1)
|
||||
}
|
||||
|
||||
// Unlock releases the lock.
|
||||
func (m *casMutex) Unlock() {
|
||||
atomic.StoreInt32(&m.lock, 0)
|
||||
}
|
||||
230
concurrency/keyed_locker_test.go
Normal file
230
concurrency/keyed_locker_test.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestKeyedLocker_SerialExecutionSameKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestKeyedLocker_SerialExecutionSameKey")
|
||||
|
||||
locker := NewKeyedLocker[string](100 * time.Millisecond)
|
||||
|
||||
var result []int
|
||||
var mu sync.Mutex
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
err := locker.Do(context.Background(), "key1", func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
result = append(result, i)
|
||||
})
|
||||
|
||||
assert.IsNil(err)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(5, len(result))
|
||||
}
|
||||
|
||||
func TestKeyedLocker_ParallelExecutionDifferentKeys(t *testing.T) {
|
||||
locker := NewKeyedLocker[string](100 * time.Millisecond)
|
||||
|
||||
start := time.Now()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
key := "key" + strconv.Itoa(i)
|
||||
locker.Do(context.Background(), key, func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
})
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed > 100*time.Millisecond {
|
||||
t.Errorf("parallel execution took too long: %s", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyedLocker_ContextTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestKeyedLocker_ContextTimeout")
|
||||
|
||||
locker := NewKeyedLocker[string](100 * time.Millisecond)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
// Lock key before calling
|
||||
go func() {
|
||||
_ = locker.Do(context.Background(), "key-timeout", func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
|
||||
time.Sleep(1 * time.Millisecond) // ensure lock is acquired first
|
||||
|
||||
err := locker.Do(ctx, "key-timeout", func() {
|
||||
t.Error("should not execute")
|
||||
})
|
||||
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestKeyedLocker_LockReleaseAfterTTL(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
|
||||
|
||||
locker := NewKeyedLocker[string](50 * time.Millisecond)
|
||||
|
||||
err := locker.Do(context.Background(), "ttl-key", func() {})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Wait for TTL to pass
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
err = locker.Do(context.Background(), "ttl-key", func() {})
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestRWKeyedLocker_LockAndUnlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
|
||||
|
||||
locker := NewRWKeyedLocker[string](500 * time.Millisecond)
|
||||
|
||||
var locked bool
|
||||
err := locker.Lock(context.Background(), "key1", func() {
|
||||
locked = true
|
||||
})
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(true, locked)
|
||||
}
|
||||
|
||||
func TestRWKeyedLocker_RLockParallel(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
|
||||
|
||||
locker := NewRWKeyedLocker[string](1 * time.Second)
|
||||
|
||||
var mu sync.Mutex
|
||||
var count int
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := locker.RLock(context.Background(), "shared-key", func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mu.Lock()
|
||||
count++
|
||||
mu.Unlock()
|
||||
})
|
||||
|
||||
assert.IsNil(err)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(5, count)
|
||||
}
|
||||
|
||||
func TestRWKeyedLocker_LockTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRWKeyedLocker_LockTimeout")
|
||||
|
||||
locker := NewRWKeyedLocker[string](1 * time.Second)
|
||||
|
||||
start := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
locker.Lock(context.Background(), "key-timeout", func() {
|
||||
close(start)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
|
||||
<-start
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
err := locker.Lock(ctx, "key-timeout", func() {
|
||||
t.Error("should not reach here")
|
||||
})
|
||||
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestTryKeyedLocker_SimpleLockUnlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestTryKeyedLocker_SimpleLockUnlock")
|
||||
|
||||
locker := NewTryKeyedLocker[string]()
|
||||
|
||||
ok := locker.TryLock("key1")
|
||||
assert.Equal(true, ok)
|
||||
|
||||
ok = locker.TryLock("key1")
|
||||
assert.Equal(false, ok)
|
||||
|
||||
locker.Unlock("key1")
|
||||
|
||||
ok = locker.TryLock("key1")
|
||||
assert.Equal(true, ok)
|
||||
|
||||
locker.Unlock("key1")
|
||||
}
|
||||
|
||||
func TestTryKeyedLocker_ParallelTry(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestTryKeyedLocker_ParallelTry")
|
||||
|
||||
locker := NewTryKeyedLocker[string]()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
var count int
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
ok := locker.TryLock("key" + strconv.Itoa(i))
|
||||
mu.Lock()
|
||||
if ok {
|
||||
count++
|
||||
}
|
||||
mu.Unlock()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if ok {
|
||||
locker.Unlock("key" + strconv.Itoa(i))
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(5, count)
|
||||
assert.Equal(0, len(locker.locks))
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
@@ -66,34 +65,37 @@ func Md5ByteWithBase64(data []byte) string {
|
||||
|
||||
// Md5File return the md5 value of file.
|
||||
func Md5File(filename string) (string, error) {
|
||||
if fileInfo, err := os.Stat(filename); err != nil {
|
||||
return "", err
|
||||
} else if fileInfo.IsDir() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hash := md5.New()
|
||||
|
||||
chunkSize := 65536
|
||||
for buf, reader := make([]byte, chunkSize), bufio.NewReader(file); ; {
|
||||
n, err := reader.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
hash.Write(buf[:n])
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
checksum := fmt.Sprintf("%x", hash.Sum(nil))
|
||||
return checksum, nil
|
||||
hash := md5.New()
|
||||
buf := make([]byte, 65536) // 64KB
|
||||
|
||||
for {
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
if n > 0 {
|
||||
hash.Write(buf[:n])
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// HmacMd5 return the hmac hash of string use md5.
|
||||
|
||||
@@ -15,39 +15,40 @@ import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
blockSize := aes.BlockSize
|
||||
dataLen := len(data)
|
||||
padding := blockSize - (dataLen % blockSize)
|
||||
paddedLen := dataLen + padding
|
||||
|
||||
copy(plain, data)
|
||||
paddedData := make([]byte, paddedLen)
|
||||
copy(paddedData, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
for i := dataLen; i < paddedLen; i++ {
|
||||
paddedData[i] = byte(padding)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
cipher, err := aes.NewCipher(generateAesKey(key, len(key)))
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
encrypted := make([]byte, paddedLen)
|
||||
for bs := 0; bs < paddedLen; bs += blockSize {
|
||||
cipher.Encrypt(encrypted[bs:], paddedData[bs:])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
@@ -57,77 +58,107 @@ func AesEcbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
|
||||
blockSize := aes.BlockSize
|
||||
if len(encrypted)%blockSize != 0 {
|
||||
panic("aes: encrypted data length is not a multiple of block size")
|
||||
}
|
||||
|
||||
cipher, err := aes.NewCipher(generateAesKey(key, len(key)))
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
for i := 0; i < len(encrypted); i += blockSize {
|
||||
cipher.Decrypt(decrypted[i:], encrypted[i:])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
if len(decrypted) == 0 {
|
||||
return nil
|
||||
}
|
||||
padding := int(decrypted[len(decrypted)-1])
|
||||
if padding == 0 || padding > blockSize {
|
||||
panic("aes: invalid PKCS#7 padding")
|
||||
}
|
||||
for i := len(decrypted) - padding; i < len(decrypted); i++ {
|
||||
if decrypted[i] != byte(padding) {
|
||||
panic("aes: invalid PKCS#7 padding content")
|
||||
}
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
return decrypted[:len(decrypted)-padding]
|
||||
}
|
||||
|
||||
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
padding := aes.BlockSize - len(data)%aes.BlockSize
|
||||
padded := append(data, bytes.Repeat([]byte{byte(padding)}, padding)...)
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(padded))
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[aes.BlockSize:], data)
|
||||
mode.CryptBlocks(encrypted, padded)
|
||||
|
||||
return encrypted
|
||||
return append(iv, encrypted...)
|
||||
}
|
||||
|
||||
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("aes: ciphertext too short")
|
||||
}
|
||||
|
||||
if len(encrypted)%aes.BlockSize != 0 {
|
||||
panic("aes: ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
ciphertext := encrypted[aes.BlockSize:]
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(ciphertext))
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
mode.CryptBlocks(decrypted, ciphertext)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
return pkcs7UnPadding(decrypted)
|
||||
}
|
||||
|
||||
// AesCtrCrypt encrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/SpaZO0-5Nsp
|
||||
// deprecated: use AesCtrEncrypt and AesCtrDecrypt instead.
|
||||
func AesCtrCrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
@@ -141,158 +172,214 @@ func AesCtrCrypt(data, key []byte) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
|
||||
// AesCtrEncrypt encrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
// Play: todo
|
||||
func AesCtrEncrypt(data, key []byte) []byte {
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
ciphertext := make([]byte, len(data))
|
||||
stream.XORKeyStream(ciphertext, data)
|
||||
|
||||
return encrypted
|
||||
return append(iv, ciphertext...)
|
||||
}
|
||||
|
||||
// AesCtrDecrypt decrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: todo
|
||||
func AesCtrDecrypt(encrypted, key []byte) []byte {
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("aes: invalid ciphertext length")
|
||||
}
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
ciphertext := encrypted[aes.BlockSize:]
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
stream.XORKeyStream(plaintext, ciphertext)
|
||||
|
||||
return plaintext
|
||||
}
|
||||
|
||||
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic("aes: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, len(data))
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext, data)
|
||||
|
||||
return append(iv, ciphertext...)
|
||||
}
|
||||
|
||||
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
panic("aes: encrypted data too short")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
ciphertext := encrypted[aes.BlockSize:]
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(plaintext, ciphertext)
|
||||
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
|
||||
return encrypted
|
||||
return plaintext
|
||||
}
|
||||
|
||||
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
data = pkcs7Padding(data, aes.BlockSize)
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, len(data))
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
stream.XORKeyStream(ciphertext, data)
|
||||
|
||||
return encrypted
|
||||
return append(iv, ciphertext...)
|
||||
}
|
||||
|
||||
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbDecrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if len(data) < aes.BlockSize {
|
||||
panic("aes: encrypted data too short")
|
||||
}
|
||||
|
||||
iv := data[:aes.BlockSize]
|
||||
data = data[aes.BlockSize:]
|
||||
if len(data)%aes.BlockSize != 0 {
|
||||
return nil
|
||||
ciphertext := data[aes.BlockSize:]
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(plaintext, ciphertext)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
return decrypted
|
||||
return plaintext
|
||||
}
|
||||
|
||||
// AesGcmEncrypt encrypt data with key use AES GCM algorithm
|
||||
// Play: https://go.dev/play/p/rUt0-DmsPCs
|
||||
func AesGcmEncrypt(data, key []byte) []byte {
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create GCM: " + err.Error())
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
panic(err)
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
panic("aes: failed to generate nonce: " + err.Error())
|
||||
}
|
||||
|
||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||
ciphertext := gcm.Seal(nil, nonce, data, nil)
|
||||
|
||||
return ciphertext
|
||||
return append(nonce, ciphertext...)
|
||||
}
|
||||
|
||||
// AesGcmDecrypt decrypt data with key use AES GCM algorithm
|
||||
// Play: https://go.dev/play/p/rUt0-DmsPCs
|
||||
func AesGcmDecrypt(data, key []byte) []byte {
|
||||
if !isAesKeyLengthValid(len(key)) {
|
||||
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: failed to create GCM: " + err.Error())
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(data) < nonceSize {
|
||||
panic("ciphertext too short")
|
||||
panic("aes: ciphertext too short")
|
||||
}
|
||||
|
||||
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("aes: decryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
return plaintext
|
||||
@@ -302,20 +389,17 @@ func AesGcmDecrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/8qivmPeZy4P
|
||||
func DesEcbEncrypt(data, key []byte) []byte {
|
||||
length := (len(data) + des.BlockSize) / des.BlockSize
|
||||
plain := make([]byte, length*des.BlockSize)
|
||||
copy(plain, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
cipher, err := des.NewCipher(generateDesKey(key))
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
blockSize := cipher.BlockSize()
|
||||
padded := pkcs5Padding(data, blockSize)
|
||||
encrypted := make([]byte, len(padded))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
for i := 0; i < len(padded); i += blockSize {
|
||||
cipher.Encrypt(encrypted[i:], padded[i:])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
@@ -325,42 +409,50 @@ func DesEcbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/8qivmPeZy4P
|
||||
func DesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
cipher, err := des.NewCipher(generateDesKey(key))
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
blockSize := cipher.BlockSize()
|
||||
if len(encrypted)%blockSize != 0 {
|
||||
panic("des: invalid encrypted data length")
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
for i := 0; i < len(encrypted); i += blockSize {
|
||||
cipher.Decrypt(decrypted[i:], encrypted[i:])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
// Remove padding
|
||||
return pkcs5UnPadding(decrypted)
|
||||
}
|
||||
|
||||
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
blockSize := block.BlockSize()
|
||||
data = pkcs7Padding(data, blockSize)
|
||||
|
||||
encrypted := make([]byte, blockSize+len(data))
|
||||
iv := encrypted[:blockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
panic("des: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[des.BlockSize:], data)
|
||||
mode.CryptBlocks(encrypted[blockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
@@ -369,26 +461,33 @@ func DesCbcEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
blockSize := block.BlockSize()
|
||||
if len(encrypted) < blockSize || len(encrypted)%blockSize != 0 {
|
||||
panic("des: invalid encrypted data length")
|
||||
}
|
||||
|
||||
iv := encrypted[:blockSize]
|
||||
ciphertext := encrypted[blockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
return pkcs7UnPadding(ciphertext)
|
||||
}
|
||||
|
||||
// DesCtrCrypt encrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/9-T6OjKpcdw
|
||||
// deprecated: use DesCtrEncrypt and DesCtrDecrypt instead.
|
||||
func DesCtrCrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
@@ -406,25 +505,83 @@ func DesCtrCrypt(data, key []byte) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
|
||||
// DesCtrEncrypt encrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
// Play: todo
|
||||
func DesCtrEncrypt(data, key []byte) []byte {
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
iv := make([]byte, block.BlockSize())
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic("des: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
encrypted := make([]byte, len(data))
|
||||
stream.XORKeyStream(encrypted, data)
|
||||
|
||||
// 返回前缀包含 IV,便于解密
|
||||
return append(iv, encrypted...)
|
||||
}
|
||||
|
||||
// DesCtrDecrypt decrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: todo
|
||||
func DesCtrDecrypt(encrypted, key []byte) []byte {
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
blockSize := block.BlockSize()
|
||||
if len(encrypted) < blockSize {
|
||||
panic("des: ciphertext too short")
|
||||
}
|
||||
|
||||
iv := encrypted[:blockSize]
|
||||
ciphertext := encrypted[blockSize:]
|
||||
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
decrypted := make([]byte, len(ciphertext))
|
||||
stream.XORKeyStream(decrypted, ciphertext)
|
||||
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
iv := make([]byte, des.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic("des: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
copy(encrypted[:des.BlockSize], iv)
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
@@ -436,44 +593,51 @@ func DesCfbEncrypt(data, key []byte) []byte {
|
||||
// len(encrypted) should be great than 16, len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("des: encrypted data too short")
|
||||
}
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
ciphertext := encrypted[des.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
|
||||
return encrypted
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
data = pkcs7Padding(data, des.BlockSize)
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
|
||||
iv := make([]byte, des.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
panic("des: failed to generate IV: " + err.Error())
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
copy(encrypted[:des.BlockSize], iv)
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
|
||||
@@ -484,25 +648,25 @@ func DesOfbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbDecrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
if len(key) != 8 {
|
||||
panic("des: key length must be 8 bytes")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic("des: failed to create cipher: " + err.Error())
|
||||
}
|
||||
|
||||
if len(data) < des.BlockSize {
|
||||
panic("des: encrypted data too short")
|
||||
}
|
||||
|
||||
iv := data[:des.BlockSize]
|
||||
data = data[des.BlockSize:]
|
||||
if len(data)%des.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
ciphertext := data[des.BlockSize:]
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
decrypted := make([]byte, len(ciphertext))
|
||||
stream.XORKeyStream(decrypted, ciphertext)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
@@ -693,117 +857,3 @@ func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName stri
|
||||
|
||||
return rsa.VerifyPKCS1v15(publicKey, hash, hashed, signature)
|
||||
}
|
||||
|
||||
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
|
||||
pubKeyData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(pubKeyData)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM block containing the public key")
|
||||
}
|
||||
|
||||
var pubKey *rsa.PublicKey
|
||||
blockType := strings.ToUpper(block.Type)
|
||||
|
||||
if blockType == "RSA PUBLIC KEY" {
|
||||
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
pubKey, ok = key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
}
|
||||
} else if blockType == "PUBLIC KEY" {
|
||||
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
pubKey, ok = key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
|
||||
priKeyData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(priKeyData)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM block containing the private key")
|
||||
}
|
||||
|
||||
var privateKey *rsa.PrivateKey
|
||||
blockType := strings.ToUpper(block.Type)
|
||||
|
||||
// PKCS#1 format
|
||||
if blockType == "RSA PRIVATE KEY" {
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
|
||||
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
privateKey, ok = priKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// hashData returns the hash value of the data, using the specified hash function
|
||||
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
|
||||
if !hash.Available() {
|
||||
return nil, errors.New("unsupported hash algorithm")
|
||||
}
|
||||
|
||||
var hashed []byte
|
||||
|
||||
switch hash {
|
||||
case crypto.SHA224:
|
||||
h := sha256.Sum224(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA256:
|
||||
h := sha256.Sum256(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA384:
|
||||
h := sha512.Sum384(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA512:
|
||||
h := sha512.Sum512(data)
|
||||
hashed = h[:]
|
||||
default:
|
||||
return nil, errors.New("unsupported hash algorithm")
|
||||
}
|
||||
|
||||
return hashed, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
package cryptor
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func generateAesKey(key []byte, size int) []byte {
|
||||
genKey := make([]byte, size)
|
||||
@@ -35,3 +46,139 @@ func pkcs7UnPadding(src []byte) []byte {
|
||||
unPadding := int(src[length-1])
|
||||
return src[:(length - unPadding)]
|
||||
}
|
||||
|
||||
func pkcs5Padding(data []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(data)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(data, padText...)
|
||||
}
|
||||
|
||||
func pkcs5UnPadding(data []byte) []byte {
|
||||
length := len(data)
|
||||
if length == 0 {
|
||||
return nil
|
||||
}
|
||||
padLen := int(data[length-1])
|
||||
if padLen == 0 || padLen > length {
|
||||
return nil
|
||||
}
|
||||
return data[:length-padLen]
|
||||
}
|
||||
|
||||
func isAesKeyLengthValid(n int) bool {
|
||||
return n == 16 || n == 24 || n == 32
|
||||
}
|
||||
|
||||
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
|
||||
pubKeyData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(pubKeyData)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM block containing the public key")
|
||||
}
|
||||
|
||||
var pubKey *rsa.PublicKey
|
||||
blockType := strings.ToUpper(block.Type)
|
||||
|
||||
if blockType == "RSA PUBLIC KEY" {
|
||||
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
pubKey, ok = key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
}
|
||||
} else if blockType == "PUBLIC KEY" {
|
||||
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
pubKey, ok = key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
|
||||
priKeyData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(priKeyData)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM block containing the private key")
|
||||
}
|
||||
|
||||
var privateKey *rsa.PrivateKey
|
||||
blockType := strings.ToUpper(block.Type)
|
||||
|
||||
// PKCS#1 format
|
||||
if blockType == "RSA PRIVATE KEY" {
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
|
||||
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
privateKey, ok = priKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse RSA private key")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// hashData returns the hash value of the data, using the specified hash function
|
||||
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
|
||||
if !hash.Available() {
|
||||
return nil, errors.New("unsupported hash algorithm")
|
||||
}
|
||||
|
||||
var hashed []byte
|
||||
|
||||
switch hash {
|
||||
case crypto.SHA224:
|
||||
h := sha256.Sum224(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA256:
|
||||
h := sha256.Sum256(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA384:
|
||||
h := sha512.Sum384(data)
|
||||
hashed = h[:]
|
||||
case crypto.SHA512:
|
||||
h := sha512.Sum512(data)
|
||||
hashed = h[:]
|
||||
default:
|
||||
return nil, errors.New("unsupported hash algorithm")
|
||||
}
|
||||
|
||||
return hashed, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestAesEcbEncrypt(t *testing.T) {
|
||||
func TestAesEcbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -16,11 +16,11 @@ func TestAesEcbEncrypt(t *testing.T) {
|
||||
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
|
||||
assert := internal.NewAssert(t, "TestAesEcbCrypt")
|
||||
assert.Equal(data, string(aesEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesCbcEncrypt(t *testing.T) {
|
||||
func TestAesCbcCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -29,7 +29,7 @@ func TestAesCbcEncrypt(t *testing.T) {
|
||||
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
|
||||
assert := internal.NewAssert(t, "TestAesCbcCrypt")
|
||||
assert.Equal(data, string(aesCbcDecrypt))
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ func TestAesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
|
||||
aesCtrCrypt := AesCtrEncrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrDecrypt(aesCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCtrCrypt")
|
||||
assert.Equal(data, string(aesCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestAesCfbEncrypt(t *testing.T) {
|
||||
func TestAesCfbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -55,11 +55,11 @@ func TestAesCfbEncrypt(t *testing.T) {
|
||||
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
|
||||
assert := internal.NewAssert(t, "TestAesCfbCrypt")
|
||||
assert.Equal(data, string(aesCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesOfbEncrypt(t *testing.T) {
|
||||
func TestAesOfbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -72,7 +72,7 @@ func TestAesOfbEncrypt(t *testing.T) {
|
||||
assert.Equal(data, string(aesOfbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesEcbEncrypt(t *testing.T) {
|
||||
func TestDesEcbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -85,7 +85,7 @@ func TestDesEcbEncrypt(t *testing.T) {
|
||||
assert.Equal(data, string(desEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesCbcEncrypt(t *testing.T) {
|
||||
func TestDesCbcCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -104,14 +104,14 @@ func TestDesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
|
||||
desCtrCrypt := DesCtrEncrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrDecrypt(desCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCtrCrypt")
|
||||
assert.Equal(data, string(desCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestDesCfbEncrypt(t *testing.T) {
|
||||
func TestDesCfbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
@@ -124,7 +124,7 @@ func TestDesCfbEncrypt(t *testing.T) {
|
||||
assert.Equal(data, string(desCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesOfbEncrypt(t *testing.T) {
|
||||
func TestDesOfbCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
-----BEGIN rsa private key-----
|
||||
MIIJKAIBAAKCAgEA0HaqqGWnNqwRVKoJUUjYiH/dwkSK61La4RGYkvDfcuo9pDgc
|
||||
zHVhFUb/MYWeb3bAlFAGcZLYSBh6WAGxbeMjSkaLroaXnafhKZ2oXXUy8CzyYxZw
|
||||
pLCDgJLG7Pf0jer3STZW93ZT8UJixbKwbqD6b8fkpMANwCYrTlXDWBuZVaEKuQNn
|
||||
C4XufQis4fwRxRgfrZuLMvuQVtnyYmssmnp8JHovYkr87giDjEgvlrC84Lez32Zh
|
||||
EhORRu5NGEDeZ1OewbkXGyQtbcBQYNAxgnOcO44MTTPhEdz4/A0xwzEe+yIOxWIA
|
||||
WuEM2rclXpZBW/MEK41gZ3C04KcX5R0KRdj2PTTH7vJ33yRC4hKtWetwMvsUrewr
|
||||
RKVcUgaeFykDAlgIDoSWvG0nzb70AWSoNcj7krLlwEc2WLGy/kCYWppnfYKafEJX
|
||||
pgblHrDFKlWvte68yrvGdCsWRLQ2uUF0xokQIg4u2bApyWNroxEtjHwUp1VeJuZB
|
||||
Qkfg+vIW+t3moQpMtlhy4aai/oGIxI5TIB82nv8TPVczjHmmE3hwL6nZ5OfbFyWp
|
||||
RFvKLtXcaAKMi2HqOLRkQhqjjJUCO9Wyhu1BkX8RAeKLl3S7gRyhuOB5JNUGEpQ3
|
||||
89MdbUX5S12Qp1xTDS+otbC8tYS1gu0OOOWowxbuF5+Ci2SYgSHv/3zXKoMCAwEA
|
||||
AQKCAgBASCdt0BLVABBEDD7dStpClTNbwWkZEa6e8p8ayJ4OwH6LkiYHQjbSqdbt
|
||||
fWeStYrC8T5bbU22RZ4MX1FIMl9iewh9o9FC806yV4RgdVBk0WdY+MV5c1jJn/kp
|
||||
f3hw/sCMWe5NVrah0bfFgMl5A7jWGUy/JN3Yn6yA9l9LXw9UYVl+Hbd7zSvycGdn
|
||||
NCSCo2K5VRqCeSJUNdbRVH0nkZeQZAESjU8bU5LFAklybUOBBAS6YcaTHWeR+M/b
|
||||
J72tHRL6Z8nhO7Gqg0AF6o0pzd6iWrYeZF4F9R7uEl+C8jW8eQ8W/JZhb75X/1vN
|
||||
pAW5heGGUI0muJF+KOhsZR0S1slvHbCSvwaNBQi8HEbOEPa7wVzsrGLfc+re5ysF
|
||||
X2n5GL4qBwNdMHrADnMWWVa9LEUSr3XmXfpmNDctmdv1ekWpJ44G/qrjKOuimtfk
|
||||
t9haDjhN5lx3V05k18kb53tYAdi3CqrT4syiibtqZLvTRam/yoFJgSOczO5WxNNk
|
||||
xnhnEdr/FQcFQhzuQ6gU196SBi8Bqjz1t0cNozNu3wvp8Fu0G8Rr9gjYnH8m5xXz
|
||||
xb9xevR7X/swfxd+/KUqoenYdU5ZFJpDRCmUMDKWR/3Q7s4SRxMN0+c/uBSaih3J
|
||||
I9I8urHG6mFW2qWQZTvuostmb7vVHaefx0RZOhX0efJVMZPwiQKCAQEA8BUXRw9k
|
||||
Ms3tqmBMVM4sMcPgsffJxjAJUdunUgEpaBAqrDQmZ6uBjAYq7iTzLgJJuLI1o1sv
|
||||
HtEWuGLHRRJpdoEamGweJE/o28OH2MBiiL2ezfRIHsg8RRr6os6KvlKevty6m7d9
|
||||
hAvpS6XvZGK2eCSooL6zZ8XTgnDw+Hw8Q4i+dazjb5dDRRL97eHHUqzcYH5vIsAE
|
||||
EBaNXMKrhf/NyVWCJBVRHg8PZZjGXcrj8ZMx+ejDR0AL5BXUaAX3fYnQyJf6QzrD
|
||||
t5aNDoU6vPO77CLkqBavTePTToiVeTIMZP5yHZFQuPWtP/y67e0csNA6ezwpWEh4
|
||||
4MnpLWzC2YRZ/wKCAQEA3kjpB3977IHFw90Xn8TQu3/SzBMUBqdj5yo6gPBijKNf
|
||||
4mZ1shUp6vZkFOHbAmoafjqCzOO18tW4ci+UWAQgdXQWXwsQDUM+fZsr/5Z9LoIN
|
||||
TYuq98Yh/rjKABxkFdMmkLmykuAblDRwVeMf3e4x6/55hAkwvESBRdfnSjjoNmEM
|
||||
MRVW4v5meDv0W4NS1eyohWPe/IoR09dSsmMhz311b7btBxc97NNqDD4SdxmASnOZ
|
||||
qsE2QxRHRFjpTvytpZH/lTQH6IYdh9REZgX7uvmlmTQwqLy6wX5T/oEzVPRhXgh9
|
||||
rgbPPJuoZwWF+fXipJ8c+laQ/sdzDKCxEEhRZr7HfQKCAQBKflDVtLnjZbA78Fte
|
||||
6QYffubGcdtCyn7pzl0RfdjKOFH1Us0j17x/pR5G/GIUQZN8YpdwE4gAaOJC3it0
|
||||
jCz7Hz4QU2Pa4oyfPAF3yOIKCcQvpX+HRZwl2SQxxiKYwWwOTtD8JiglA8kktt0b
|
||||
6eEyUDWegu2J4oEpdT/f6jSMw+5M9xMu+eFemnD+EdNWHNrYegKj5q6cC1Nbl+++
|
||||
yUuiEA0sgwzDZeriHFBYo+6sc37LS2TkQ8QsxnU8vbU4V73XsAhwOdimq2kjO0Wp
|
||||
gXsq3vzSBw/n/CwBrzGqBFSCNc1UzVUdvuU9+H5L+wmu9z1eJaGyifIv1ZariJbB
|
||||
BWcjAoIBACWRJPEDdqot9IJ5pzh1RuGpZLLgto25VIUI+gI4ni8unVHiBxolwYPY
|
||||
SGnPEfiCfh+/O8Ps6B82R4nkyKlnaSTwjadac0gKiVEpHHKBuH5XtG/anvZpIe2u
|
||||
xVTnd3LI0Me82pVAEuklQ6cAT65uRzmfNGJAO2BWI5LuPkSpAXXPSQQymxCZ9i9z
|
||||
0oR02VcWPBTvIAyGOSUYSv2jC1/J0EMlI0IDh4+y20VeaDiAstHiX6IgLU+A6dp/
|
||||
PE8BHUfSOOO6e2us3ujJ0xV7BWRANOCDlYWu/9EbzI5Cv64n7xy5SqRSukt/8yIW
|
||||
KOJpz/gKfBdC8hZdFvCXZ9Vco4U90PECggEBAIwy5zuWqSaJ8kom7kMZj+JYfhVz
|
||||
rdozFil1CkmWcw4E+rNcq8bKVTa9Er6/3nBTXfKX+4sYzMzbFr6xjFyrPA62wjiT
|
||||
2MRdHu9iB+Rtxn83Ilnh+Aqu/8bPFQUlYFr7nefy2t1aL76diQnsBu4xHOtEMgSv
|
||||
F6d/pDFRC+PZN0B7glrz870jRaw9LNcuIBVoKN07hGBwD76EEZhCrn6eLqMI8srH
|
||||
YESDPUR/wl/PV8ZrA/hNRvjp44PYiLMYRDSQw8kpbLMMc0QgQCvhfMbYVvGteTmG
|
||||
qdCFyy2x70wSh6Rfzb7WEWl15I4yfBTddXkJX82S+MrZu/Xq4FDtng594h0=
|
||||
MIIJKQIBAAKCAgEAw6Q11wDtWHZiyQbv+XzntJbEELkPQ3oVSVCVjTx+ls5I+yR6
|
||||
oq8LxyoUE6SHcBlludkzs02B0rDkkfj6vl0iPCzr3HP1XT1WwFiQJnXKtNNNiLvv
|
||||
ewB/aynj/UIBCZZvMTYwK6nc76i4JWcajaiUI4u84Fp7gOn0IgXMhvrwtGHk7HK1
|
||||
Zjr0afUJVbodc6pMZfe0QWpnDq7zaeYdkPwz8klWAatLDZTeR5dYli2NiWOBepO1
|
||||
538ZvEK3S/QP2nWJzPJy2PbEHfDaFgr055w/wu0MzAQgkXGV8PubhYboJuQAkuXc
|
||||
63IPpmWVL/93WeCNRiL5fvNzUHsbPtT48A9NnnUn4QVuVGeXIxJ1skAFsoh7i+h+
|
||||
mK/p5rzjPtw56nE20gYXhxf7FzbHYtHn0egoBddUzlOY0yzvCgI5Yy1RdkNe/5vU
|
||||
1bpLsdU/WItjR2h6EfqLosQ4iKowPBYQXJOINbpkvIhkhCVbPpMvmhfi/lATiKdo
|
||||
XgtvuB04wth011c5W/yXA+A3Ob73Sr2+zyxF870tOp3rNTWsYLvCOjf+JdjgM3Az
|
||||
NuFxE4NdMLvXDf7CIXSb3X9rj3qM769Lz6XXbRBVbO0HxGyqLTPPmSWV61jQuUTW
|
||||
NGnxkovoYLrpgyumIjjIcl0aY1hogmQeA6ZNgJxaUw9r/p8vnThL5UXKlbcCAwEA
|
||||
AQKCAgA6auEMtyEn5gmvPxwO4iYJvW8EE04kduK+RoqxZpviCeyu2QCpbR1WNhFS
|
||||
oDE03tCKQvn2EzGtqNZHZBHzH7omP7+ZzyI32iX6Pzej1E5X2jRvnLZwgwCJDjQe
|
||||
nkPTChFfn2+YyhrHkRiTSosaO3koeARI9ZulLbaRJiwhLfML2aNSmxnhUb9DmEBW
|
||||
UcGTlQlE9UbktZHaR5E1UNoJPdWBBjbhPrWlc4s/00hHZ4i4unaiFFMfscsMdNto
|
||||
mZLWR3yDLu3uLRj4W7+zD0h7lCNR4BiPHrhe1YzRLUAOCe9uFWy6cFqcDy3vIKwA
|
||||
bgbEVpG4nJ7lMmp/YJMNGujOPbOOFAhziL4jcbQvcbXZTGr78+BIyploCuitqSP7
|
||||
lNwM6sL01lBcP3WnpWtGUdggRrIbLlam4KtD/hlXoHWb1YLvnswe9N233FNwyUep
|
||||
5W2Svy+DkVDH7BCl/DD0NoPuDyV6yiM+h8gG79o6UHthDwQZxZ7bi+wwulsNajuB
|
||||
uiUBQfUURCshgehUJ6u+a0FxxtVqomXKB8vKbd+St6sxpfPWHiimYF9BvXvPp8/F
|
||||
KheecohYFmHnB2ecX4Vu/WCUoFVZcuiCFwWabkKL7HlzBUjFuEHdlXSP5+z1CfZM
|
||||
Zov907ESptA4YOp+5uM+9UIijVv482ogmDN+VaGDyAhQc5IjgQKCAQEAzX4SEz59
|
||||
Ylsz34CiEh3y6KHhUmQVb3pXUevIOKA+hFh3n5pV/4/H8MseVpxqdNLE95hEC8eO
|
||||
znWutK1GyLJ/KGWryJ7PcFZIfouys1PRRyzSnuIYPwVflDsW7HDua86vtFQiaYM0
|
||||
y9XqjrIVmvMky5NBQIyfnH+UQmxkrCLPbPq0+wr7fy7vh3KxZBZ4Dk2/BkAgbvt3
|
||||
qGZIDJfBT1usxgoYe7bchhG8iXt3v1Cn+OoevO7A5LDLTbjMBiYu4TSNa7D/m4pg
|
||||
i68ROuTbZR+FhVnUrXmYIuodAaEG+H45V+YW+KT9+SA1RPGwEWIszug1CRLkDT7w
|
||||
EPWkjo+kD/hc9wKCAQEA87pMGSLSK0tUcPzmLtE6zjj48tNaDXjoBiYksi2oOb1T
|
||||
mB6GEJWaI+UdrdmucsqqS5lcLPizTIVyq3jt0eJV3HV3B1VpYkLKH1HzJNGlqOsm
|
||||
QY8tjJVrCKdyRjsmX6tD3EEt9YH/sgtCN4OHNDMKigbI9HKH1ds5nofsfu+tAmBf
|
||||
5xBi/ziWZ+wgzOXgkKtUmP09YQ4+mKRYCWtQsWjYn7tLH1IcJ+NLktfOjBa5Z6xP
|
||||
FIgjCWTpoH0IgVFzzoNtcp4bxZUuI954dhEXRlvNWx3BPV6OEGn3Z8b8crtlq2uB
|
||||
Df9H2EbycZLRouYEjmRIqoLxcaiv4U/JXV/+pqUdQQKCAQEAgivJcW1VaffSLVPD
|
||||
1uwn0tuw60tBFOQP6nIM3GoOEDImXPEcZw6CnnNc3YqqGSTm5t0Kxdd9DCYaLJPT
|
||||
UhFYYZO+NRD84PGXbKo74kNHP+oR5dndeBaETFb+F3sWXS6JuRbO0b3utOzmb8w5
|
||||
yXUaqua2IBi0hyN+HHGjeDN46FUMTjbPx+pIi6nI21ksZgsz5da7dPJRA1j/bRK4
|
||||
vveYw77jcYMNwNP9da3D1mpWbWSJYi9v+65OqwZUH9USP1DWAREXakVHEVUt+fu9
|
||||
SxqjryyPf/CVwhBBnaWOz9zNoHO9u3Zw995CQFFFsk1ZixwndH8F/aoP0v54ZmJx
|
||||
kDhJUwKCAQEAiDafHxEx6pZgLAF7V8EBn42BvJxYYN0ot38L1TTlJtfj50lv3cbu
|
||||
bY84Bhh5gZg4kqW0OUIJWwPd8W1VZI6dM+fGTSf0DMGNGvH+9J3iesRpDgzKBR7g
|
||||
weZrXErkiGU8ONWIrQQpdkUKjeIJZyf7hOaD2vJDbkbmLe6DQvIKfCCGmKm02jSz
|
||||
AoOaVUlINzI5xoMJkbuXSlHXDfSXFX+mSacwNeH68GP8saXAvtRYbFOFotDu2+o1
|
||||
E8etQDDYixsyyPMKTGOydMN6CWpF2sGlxH2dqQG2XgALEEnKfwiyqpobd0oryfjk
|
||||
uIq4dNUeyMHNRtaFv+Lp4P9pZ7ElrdbrwQKCAQBScEA9ds64/tE6zGM77kGHPOii
|
||||
RaCvaw9FeuC6WyoBPWtIi6clXPrbt/6vVlK2JKtpHfVMOFIQ175s4THQVQczhwgv
|
||||
U4rgkoRrdM9fmeaewMUgXJmqWJD3cQSXm5LJg5cQ8OSAm5PV8xdKTr36pAr+9RTS
|
||||
bYodXZW6BV/sV9HNnWjcx+l3QViJqRCsGFNTHF86e289V4IRIQOPFbhQW+dEsevz
|
||||
mnitogmSeVUwUJfi0A38Zcmu3GA7NhfGE0XrUs76skYY4Yx+r71rfN99VvnG/OAs
|
||||
NIpwYPJ/uZB9T1H7Cjlz0BH0P3GNaqbo96uPUhgBGy/PLmHOeDnJ2e1BMAYa
|
||||
-----END rsa private key-----
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
-----BEGIN rsa public key-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0HaqqGWnNqwRVKoJUUjY
|
||||
iH/dwkSK61La4RGYkvDfcuo9pDgczHVhFUb/MYWeb3bAlFAGcZLYSBh6WAGxbeMj
|
||||
SkaLroaXnafhKZ2oXXUy8CzyYxZwpLCDgJLG7Pf0jer3STZW93ZT8UJixbKwbqD6
|
||||
b8fkpMANwCYrTlXDWBuZVaEKuQNnC4XufQis4fwRxRgfrZuLMvuQVtnyYmssmnp8
|
||||
JHovYkr87giDjEgvlrC84Lez32ZhEhORRu5NGEDeZ1OewbkXGyQtbcBQYNAxgnOc
|
||||
O44MTTPhEdz4/A0xwzEe+yIOxWIAWuEM2rclXpZBW/MEK41gZ3C04KcX5R0KRdj2
|
||||
PTTH7vJ33yRC4hKtWetwMvsUrewrRKVcUgaeFykDAlgIDoSWvG0nzb70AWSoNcj7
|
||||
krLlwEc2WLGy/kCYWppnfYKafEJXpgblHrDFKlWvte68yrvGdCsWRLQ2uUF0xokQ
|
||||
Ig4u2bApyWNroxEtjHwUp1VeJuZBQkfg+vIW+t3moQpMtlhy4aai/oGIxI5TIB82
|
||||
nv8TPVczjHmmE3hwL6nZ5OfbFyWpRFvKLtXcaAKMi2HqOLRkQhqjjJUCO9Wyhu1B
|
||||
kX8RAeKLl3S7gRyhuOB5JNUGEpQ389MdbUX5S12Qp1xTDS+otbC8tYS1gu0OOOWo
|
||||
wxbuF5+Ci2SYgSHv/3zXKoMCAwEAAQ==
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw6Q11wDtWHZiyQbv+Xzn
|
||||
tJbEELkPQ3oVSVCVjTx+ls5I+yR6oq8LxyoUE6SHcBlludkzs02B0rDkkfj6vl0i
|
||||
PCzr3HP1XT1WwFiQJnXKtNNNiLvvewB/aynj/UIBCZZvMTYwK6nc76i4JWcajaiU
|
||||
I4u84Fp7gOn0IgXMhvrwtGHk7HK1Zjr0afUJVbodc6pMZfe0QWpnDq7zaeYdkPwz
|
||||
8klWAatLDZTeR5dYli2NiWOBepO1538ZvEK3S/QP2nWJzPJy2PbEHfDaFgr055w/
|
||||
wu0MzAQgkXGV8PubhYboJuQAkuXc63IPpmWVL/93WeCNRiL5fvNzUHsbPtT48A9N
|
||||
nnUn4QVuVGeXIxJ1skAFsoh7i+h+mK/p5rzjPtw56nE20gYXhxf7FzbHYtHn0ego
|
||||
BddUzlOY0yzvCgI5Yy1RdkNe/5vU1bpLsdU/WItjR2h6EfqLosQ4iKowPBYQXJOI
|
||||
NbpkvIhkhCVbPpMvmhfi/lATiKdoXgtvuB04wth011c5W/yXA+A3Ob73Sr2+zyxF
|
||||
870tOp3rNTWsYLvCOjf+JdjgM3AzNuFxE4NdMLvXDf7CIXSb3X9rj3qM769Lz6XX
|
||||
bRBVbO0HxGyqLTPPmSWV61jQuUTWNGnxkovoYLrpgyumIjjIcl0aY1hogmQeA6ZN
|
||||
gJxaUw9r/p8vnThL5UXKlbcCAwEAAQ==
|
||||
-----END rsa public key-----
|
||||
|
||||
@@ -15,7 +15,6 @@ func TestSinglyLink_InsertAtFirst(t *testing.T) {
|
||||
link.InsertAtHead(1)
|
||||
link.InsertAtHead(2)
|
||||
link.InsertAtHead(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{3, 2, 1}
|
||||
values := link.Values()
|
||||
@@ -32,7 +31,6 @@ func TestSinglyLink_InsertAtTail(t *testing.T) {
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
@@ -77,7 +75,6 @@ func TestSinglyLink_DeleteAtHead(t *testing.T) {
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtHead()
|
||||
link.Print()
|
||||
|
||||
expected := []int{2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
@@ -55,7 +55,7 @@ func (q *ArrayQueue[T]) IsFull() bool {
|
||||
|
||||
// Front return front value of queue
|
||||
func (q *ArrayQueue[T]) Front() T {
|
||||
return q.data[0]
|
||||
return q.data[q.head]
|
||||
}
|
||||
|
||||
// Back return back value of queue
|
||||
|
||||
@@ -83,13 +83,13 @@ func AddDay(t time.Time, days int64) time.Time {
|
||||
}
|
||||
|
||||
// AddWeek add or sub weeks to the time.
|
||||
// play: todo
|
||||
// play: https://go.dev/play/p/M9TqdMiaA2p
|
||||
func AddWeek(t time.Time, weeks int64) time.Time {
|
||||
return t.Add(7 * 24 * time.Hour * time.Duration(weeks))
|
||||
}
|
||||
|
||||
// AddMonth add or sub months to the time.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/DLoiOnpLvsN
|
||||
func AddMonth(t time.Time, months int64) time.Time {
|
||||
return t.AddDate(0, int(months), 0)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func AddYear(t time.Time, year int64) time.Time {
|
||||
}
|
||||
|
||||
// AddDaySafe add or sub days to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/JTohZFpoDJ3
|
||||
func AddDaySafe(t time.Time, days int) time.Time {
|
||||
t = t.AddDate(0, 0, days)
|
||||
year, month, day := t.Date()
|
||||
@@ -116,7 +116,7 @@ func AddDaySafe(t time.Time, days int) time.Time {
|
||||
}
|
||||
|
||||
// AddMonthSafe add or sub months to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/KLw0lo6mbVW
|
||||
func AddMonthSafe(t time.Time, months int) time.Time {
|
||||
year := t.Year()
|
||||
month := int(t.Month()) + months
|
||||
@@ -141,7 +141,7 @@ func AddMonthSafe(t time.Time, months int) time.Time {
|
||||
}
|
||||
|
||||
// AddYearSafe add or sub years to the time and ensure that the returned date does not exceed the valid date of the target year and month.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/KVGXWZZ54ZH
|
||||
func AddYearSafe(t time.Time, years int) time.Time {
|
||||
year, month, day := t.Date()
|
||||
year += years
|
||||
@@ -280,13 +280,9 @@ func EndOfDay(t time.Time) time.Time {
|
||||
}
|
||||
|
||||
// BeginOfWeek return beginning week, default week begin from Sunday.
|
||||
// Play: https://go.dev/play/p/ynjoJPz7VNV
|
||||
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
|
||||
var beginFromWeekday = time.Sunday
|
||||
if len(beginFrom) > 0 {
|
||||
beginFromWeekday = beginFrom[0]
|
||||
}
|
||||
y, m, d := t.AddDate(0, 0, int(beginFromWeekday-t.Weekday())).Date()
|
||||
// Play: https://go.dev/play/p/DCHdcL6gnfV
|
||||
func BeginOfWeek(t time.Time, beginFrom time.Weekday) time.Time {
|
||||
y, m, d := t.AddDate(0, 0, int(beginFrom-t.Weekday())).Date()
|
||||
beginOfWeek := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
|
||||
if beginOfWeek.After(t) {
|
||||
return beginOfWeek.AddDate(0, 0, -7)
|
||||
@@ -295,13 +291,9 @@ func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
|
||||
}
|
||||
|
||||
// EndOfWeek return end week time, default week end with Saturday.
|
||||
// Play: https://go.dev/play/p/i08qKXD9flf
|
||||
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time {
|
||||
var endWithWeekday = time.Saturday
|
||||
if len(endWith) > 0 {
|
||||
endWithWeekday = endWith[0]
|
||||
}
|
||||
y, m, d := t.AddDate(0, 0, int(endWithWeekday-t.Weekday())).Date()
|
||||
// Play: https://go.dev/play/p/mGSA162YgX9
|
||||
func EndOfWeek(t time.Time, endWith time.Weekday) time.Time {
|
||||
y, m, d := t.AddDate(0, 0, int(endWith-t.Weekday())).Date()
|
||||
var endWithWeek = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
if endWithWeek.Before(t) {
|
||||
endWithWeek = endWithWeek.AddDate(0, 0, 7)
|
||||
|
||||
@@ -299,23 +299,23 @@ func ExampleEndOfDay() {
|
||||
func ExampleBeginOfWeek() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfWeek(input)
|
||||
result := BeginOfWeek(input, time.Monday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 00:00:00 +0000 UTC
|
||||
// 2023-01-02 00:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfWeek() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfWeek(input)
|
||||
result := EndOfWeek(input, time.Sunday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-14 23:59:59.999999999 +0000 UTC
|
||||
// 2023-01-08 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfMonth() {
|
||||
|
||||
@@ -611,9 +611,9 @@ func TestBeginOfWeek(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local)
|
||||
expected := time.Date(2022, 2, 14, 0, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfWeek(td)
|
||||
actual := BeginOfWeek(td, time.Monday)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
@@ -623,9 +623,9 @@ func TestEndOfWeek(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local)
|
||||
expected := time.Date(2022, 2, 20, 23, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfWeek(td)
|
||||
actual := EndOfWeek(td, time.Sunday)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||
],
|
||||
},
|
||||
{ text: 'datetime', link: '/en/api/packages/datetime' },
|
||||
{ text: 'eventbus', link: '/en/api/packages/eventbus' },
|
||||
{ text: 'fileutil', link: '/en/api/packages/fileutil' },
|
||||
{ text: 'formatter', link: '/en/api/packages/formatter' },
|
||||
{ text: 'function', link: '/en/api/packages/function' },
|
||||
|
||||
@@ -127,6 +127,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||
],
|
||||
},
|
||||
{ text: '日期&时间', link: '/api/packages/datetime' },
|
||||
{ text: '事件总线', link: '/api/packages/eventbus' },
|
||||
{ text: '文件', link: '/api/packages/fileutil' },
|
||||
{ text: '格式化工具', link: '/api/packages/formatter' },
|
||||
{ text: '函数', link: '/api/packages/function' },
|
||||
|
||||
@@ -47,6 +47,7 @@ outline: deep
|
||||
<div class="package-cell">cryptor</div>
|
||||
<div class="package-cell">datastructure</div>
|
||||
<div class="package-cell">datetime</div>
|
||||
<div class="package-cell">eventbus</div>
|
||||
<div class="package-cell">fileutil</div>
|
||||
<div class="package-cell">formatter</div>
|
||||
<div class="package-cell">function</div>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -35,6 +36,17 @@ import (
|
||||
- [Take](#Take)
|
||||
- [Tee](#Tee)
|
||||
|
||||
### KeyedLocker
|
||||
|
||||
- [NewKeyedLocker](#NewKeyedLocker)
|
||||
- [KeyedLocker_Do](#Do)
|
||||
- [NewRWKeyedLocker](#NewRWKeyedLocker)
|
||||
- [RLock](#RLock)
|
||||
- [Lock](#Lock)
|
||||
- [NewTryKeyedLocker](#NewTryKeyedLocker)
|
||||
- [TryLock](#TryLock)
|
||||
- [Unlock](#Unlock)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
@@ -452,3 +464,389 @@ func main() {
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
### KeyedLocker
|
||||
|
||||
### <span id="NewKeyedLocker">NewKeyedLocker</span>
|
||||
|
||||
<p>NewKeyedLocker创建一个新的KeyedLocker,并为锁的过期设置指定的 TTL。KeyedLocker 是一个简单的键值锁实现,允许非阻塞的锁获取。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
|
||||
|
||||
task := func() {
|
||||
fmt.Println("Executing task...")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Task completed.")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := locker.Do(ctx, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
if err := locker.Do(ctx2, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Do">Do</span>
|
||||
|
||||
<p>为指定的键获取锁并执行提供的函数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
|
||||
|
||||
task := func() {
|
||||
fmt.Println("Executing task...")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Task completed.")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := locker.Do(ctx, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
if err := locker.Do(ctx2, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewRWKeyedLocker">NewRWKeyedLocker</span>
|
||||
|
||||
<p>NewRWKeyedLocker创建一个新的RWKeyedLocker,并为锁的过期设置指定的 TTL。RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.Lock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RLock">RLock</span>
|
||||
|
||||
<p>RLock为指定的键获取读锁并执行提供的函数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.RLock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Lock">Lock</span>
|
||||
|
||||
<p>Lock为指定的键获取锁并执行提供的函数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.Lock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewTryKeyedLocker">NewTryKeyedLocker</span>
|
||||
|
||||
<p>创建一个TryKeyedLocker实例,TryKeyedLocker是KeyedLocker的非阻塞版本。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TryLock">TryLock</span>
|
||||
|
||||
<p>TryLock尝试获取指定键的锁。如果锁成功获取,则返回true,否则返回false。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *TryKeyedLocker[K]) TryLock(key K) bool
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Unlock">Unlock</span>
|
||||
|
||||
<p>释放指定键的锁。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *TryKeyedLocker[K]) Unlock(key K)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1092,7 +1092,7 @@ func main() {
|
||||
|
||||
|
||||
### 4. PriorityQueue
|
||||
切片实现的额优先级队列。
|
||||
切片实现的优先级队列。
|
||||
|
||||
### <span id="NewPriorityQueue">NewPriorityQueue</span>
|
||||
<p>返回一个具有特定容量的PriorityQueue指针,参数 `comarator` 用于比较队列中T类型的值。</p>
|
||||
|
||||
@@ -153,7 +153,7 @@ func main() {
|
||||
func AddWeek(t time.Time, weeks int64) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/M9TqdMiaA2p)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -189,7 +189,7 @@ func main() {
|
||||
func AddMonth(t time.Time, months int64) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/DLoiOnpLvsN)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -333,7 +333,7 @@ func main() {
|
||||
func AddDaySafe(t time.Time, days int) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JTohZFpoDJ3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -380,7 +380,7 @@ func main() {
|
||||
func AddMonthSafe(t time.Time, months int) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KLw0lo6mbVW)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -417,7 +417,7 @@ func main() {
|
||||
func AddYearSafe(t time.Time, years int) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KVGXWZZ54ZH)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -546,7 +546,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
|
||||
func BeginOfWeek(t time.Time, beginFrom time.Weekday) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ynjoJPz7VNV)</span></b>
|
||||
@@ -562,12 +562,12 @@ import (
|
||||
|
||||
func main() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
result := datetime.BeginOfWeek(input)
|
||||
result := datetime.BeginOfWeek(input, time.Monday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 00:00:00 +0000 UTC
|
||||
// 2023-01-09 00:00:00 +0000 UTC
|
||||
}
|
||||
```
|
||||
|
||||
@@ -727,7 +727,7 @@ func main() {
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 23:59:59.999999999 +0000 UTC
|
||||
// 2023-01-02 00:00:00 +0000 UTC
|
||||
}
|
||||
```
|
||||
|
||||
@@ -738,10 +738,10 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
|
||||
func EndOfWeek(t time.Time, endWith time.Weekday) time.Time
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/i08qKXD9flf)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mGSA162YgX9)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -754,12 +754,12 @@ import (
|
||||
|
||||
func main() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
result := datetime.EndOfWeek(input)
|
||||
result := datetime.EndOfWeek(input, time.Sunday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-14 23:59:59.999999999 +0000 UTC
|
||||
// 2023-01-08 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import (
|
||||
func NewEventBus[T any]() *EventBus[T]
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gHbOPV_NUOJ)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -65,7 +65,7 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -84,7 +84,7 @@ func main() {
|
||||
func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/EYGf_8cHei-)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -106,8 +106,8 @@ func main() {
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, filter)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 2})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -126,7 +126,7 @@ func main() {
|
||||
func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T))
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Tmh7Ttfvprf)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Unsubscribe("event1", listener)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -161,10 +161,10 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (eb *EventBus[T]) Publish(event Event[T])
|
||||
func (eb *EventBus[T]) Publish(event eventbus.Event[T])
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gHTtVexFSH9)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -179,7 +179,7 @@ func main() {
|
||||
fmt.Println(eventData)
|
||||
}, false, 0, nil)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
@@ -196,7 +196,7 @@ func main() {
|
||||
func (eb *EventBus[T]) ClearListeners()
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KBfBYlKPgqD)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -213,9 +213,12 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListeners()
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -234,7 +237,7 @@ func main() {
|
||||
func (eb *EventBus[T]) ClearListenersByTopic(topic string)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gvMljmJOZmU)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -251,14 +254,17 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListenersByTopic("event1")
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 2
|
||||
}
|
||||
```
|
||||
|
||||
@@ -272,7 +278,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetListenersCount(topic string) int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/j6yaJ2xAmFz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -284,14 +290,14 @@ func main() {
|
||||
eb := eventbus.NewEventBus[int]()
|
||||
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
|
||||
|
||||
count := eb.GetListenersCount("event1")
|
||||
|
||||
fmt.Println(count)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -305,7 +311,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetAllListenersCount() int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PUlr0xcpEOz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -338,7 +344,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetEvents() []string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/etgjjcOtAjX)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -359,8 +365,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Output:
|
||||
// event1
|
||||
// event2
|
||||
// event1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -374,7 +380,7 @@ func main() {
|
||||
func (eb *EventBus[T]) SetErrorHandler(handler func(err error))
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gmB0gnFe5mc)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -393,7 +399,7 @@ func main() {
|
||||
panic("error")
|
||||
}, false, 0, nil)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
// Output:
|
||||
// error
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
- [IsDir](#IsDir)
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [RemoveDir](#RemoveDir)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
- [ReadFileByLine](#ReadFileByLine)
|
||||
- [Zip](#Zip)
|
||||
@@ -390,12 +391,12 @@ func main() {
|
||||
|
||||
### <span id="RemoveFile">RemoveFile</span>
|
||||
|
||||
<p>删除文件</p>
|
||||
<p>删除文件,支持传入删除前的回调函数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RemoveFile(path string) error
|
||||
func RemoveFile(path string, onDelete ...func(path string)) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/P2y0XW8a1SH)</span></b>
|
||||
@@ -416,6 +417,41 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveDir">RemoveDir</span>
|
||||
|
||||
<p>删除目录,支持传入删除前的回调函数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RemoveDir(path string, onDelete ...func(path string)) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var deletedPaths []string
|
||||
|
||||
err := fileutil.RemoveDir("./tempdir", func(p string) {
|
||||
deletedPaths = append(deletedPaths, p)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(deletedPaths)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileToString">ReadFileToString</span>
|
||||
|
||||
<p>读取文件内容并返回字符串</p>
|
||||
|
||||
@@ -78,7 +78,7 @@ import (
|
||||
- [GetOrSet](#GetOrSet)
|
||||
- [SortByKey](#SortByKey)
|
||||
- [GetOrDefault](#GetOrDefault)
|
||||
|
||||
- [FindValuesBy](#FindValuesBy)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -2307,4 +2307,43 @@ func main() {
|
||||
// a
|
||||
// default
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FindValuesBy">FindValuesBy</span>
|
||||
|
||||
<p>返回一个切片,包含满足给定谓词判断函数的map中的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
4: "d",
|
||||
}
|
||||
|
||||
result := maputil.FindValuesBy(m, func(k int, v string) bool {
|
||||
return k%2 == 0
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [b d]
|
||||
}
|
||||
```
|
||||
@@ -466,14 +466,14 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="T运行cRound">T运行cRound</span>
|
||||
### <span id="TruncRound">TruncRound</span>
|
||||
|
||||
<p>截短n位小数(不进行四舍五入)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func T运行cRound[T constraints.Float | constraints.Integer](x T, n int) T
|
||||
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
||||
@@ -487,9 +487,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.T运行cRound(0.124, 2)
|
||||
result2 := mathutil.T运行cRound(0.125, 2)
|
||||
result3 := mathutil.T运行cRound(0.125, 3)
|
||||
result1 := mathutil.TruncRound(0.124, 2)
|
||||
result2 := mathutil.TruncRound(0.125, 2)
|
||||
result3 := mathutil.TruncRound(0.125, 3)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
@@ -48,6 +48,8 @@ import (
|
||||
- [UploadFile](#UploadFile)
|
||||
- [IsPingConnected](#IsPingConnected)
|
||||
- [IsTelnetConnected](#IsTelnetConnected)
|
||||
- [BuildUrl](#BuildUrl)
|
||||
- [AddQueryParams](#AddQueryParams)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1031,3 +1033,83 @@ func main() {
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BuildUrl">BuildUrl</span>
|
||||
|
||||
<p>创建url字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
urlStr, err := netutil.BuildUrl(
|
||||
"https",
|
||||
"example.com",
|
||||
"query",
|
||||
map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com/query?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AddQueryParams">AddQueryParams</span>
|
||||
|
||||
<p>向url添加查询参数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func AddQueryParams(urlStr string, params map[string][]string) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
urlStr, err := netutil.BuildUrl(
|
||||
"https",
|
||||
"example.com",
|
||||
"query",
|
||||
map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com/query?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -887,7 +887,7 @@ func main() {
|
||||
func EqualUnordered[T comparable](slice1, slice2 []T) bool
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/n8fSc2w8ZgX)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -1772,7 +1772,7 @@ func main() {
|
||||
func ReverseCopy[T any](slice []T) []T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/c9arEaP7Cg-)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2065,7 +2065,7 @@ func main() {
|
||||
func ShuffleCopy[T any](slice []T) []T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vqDa-Gs1vT0)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -1772,7 +1772,7 @@ func main() {
|
||||
func FindAllOccurrences(str, substr string) []int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uvyA6azGLB1)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -824,6 +824,43 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsAlphaNumeric">IsAlphaNumeric</span>
|
||||
|
||||
<p>验证字符串是字母或数字。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsAlphaNumeric(s string) bool
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsAlphaNumeric("ABC")
|
||||
result2 := validator.IsAlphaNumeric("123")
|
||||
result3 := validator.IsAlphaNumeric("abc123")
|
||||
result4 := validator.IsAlphaNumeric("abc123@#$")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsJSON">IsJSON</span>
|
||||
|
||||
<p>验证字符串是否是有效json。</p>
|
||||
@@ -1001,7 +1038,7 @@ func main() {
|
||||
func IsIpPort(str string) bool
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
|
||||
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/xUmls_b9L29)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# Concurrency
|
||||
|
||||
Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
@@ -19,24 +22,39 @@ import (
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
### Channel
|
||||
- [NewChannel](#NewChannel)
|
||||
- [Bridge](#Bridge)
|
||||
- [FanIn](#FanIn)
|
||||
- [Generate](#Generate)
|
||||
- [Or](#Or)
|
||||
- [OrDone](#OrDone)
|
||||
- [Repeat](#Repeat)
|
||||
- [RepeatFn](#RepeatFn)
|
||||
- [Take](#Take)
|
||||
- [Tee](#Tee)
|
||||
|
||||
- [NewChannel](#NewChannel)
|
||||
- [Bridge](#Bridge)
|
||||
- [FanIn](#FanIn)
|
||||
- [Generate](#Generate)
|
||||
- [Or](#Or)
|
||||
- [OrDone](#OrDone)
|
||||
- [Repeat](#Repeat)
|
||||
- [RepeatFn](#RepeatFn)
|
||||
- [Take](#Take)
|
||||
- [Tee](#Tee)
|
||||
|
||||
### KeyedLocker
|
||||
|
||||
- [NewKeyedLocker](#NewKeyedLocker)
|
||||
- [Do](#Do)
|
||||
- [NewRWKeyedLocker](#NewRWKeyedLocker)
|
||||
- [RLock](#RLock)
|
||||
- [Lock](#Lock)
|
||||
- [NewTryKeyedLocker](#NewTryKeyedLocker)
|
||||
- [TryLock](#TryLock)
|
||||
- [Unlock](#Unlock)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
## Channel
|
||||
|
||||
### <span id="NewChannel">NewChannel</span>
|
||||
|
||||
<p>Create a Channel pointer instance.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -45,6 +63,7 @@ import (
|
||||
type Channel[T any] struct
|
||||
func NewChannel[T any]() *Channel[T]
|
||||
```
|
||||
|
||||
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
|
||||
|
||||
```go
|
||||
@@ -69,6 +88,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qmWSy1NVF-Y)</span></b>
|
||||
|
||||
```go
|
||||
@@ -121,6 +141,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2VYFMexEvTm)</span></b>
|
||||
|
||||
```go
|
||||
@@ -160,6 +181,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -199,6 +221,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
|
||||
|
||||
```go
|
||||
@@ -237,6 +260,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -279,6 +303,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -322,6 +347,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -360,6 +386,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -406,6 +433,7 @@ func main() {
|
||||
```go
|
||||
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -430,11 +458,397 @@ func main() {
|
||||
fmt.Println(v)
|
||||
fmt.Println(<-ch2)
|
||||
}
|
||||
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### KeyedLocker
|
||||
|
||||
### <span id="NewKeyedLocker">NewKeyedLocker</span>
|
||||
|
||||
<p>KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
|
||||
|
||||
task := func() {
|
||||
fmt.Println("Executing task...")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Task completed.")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := locker.Do(ctx, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
if err := locker.Do(ctx2, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Do">Do</span>
|
||||
|
||||
<p>Acquires a lock for the specified key and executes the provided function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
|
||||
|
||||
task := func() {
|
||||
fmt.Println("Executing task...")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Task completed.")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := locker.Do(ctx, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
if err := locker.Do(ctx2, "mykey", task); err != nil {
|
||||
log.Fatalf("Error executing task: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Task successfully executed.")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
// Executing task...
|
||||
// Task completed.
|
||||
// Task successfully executed.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewRWKeyedLocker">NewRWKeyedLocker</span>
|
||||
|
||||
<p>RWKeyedLocker is a read-write version of KeyedLocker.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.Lock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RLock">RLock</span>
|
||||
|
||||
<p>Acquires a read lock for the specified key and executes the provided function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.RLock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Lock">Lock</span>
|
||||
|
||||
<p>Acquires a write lock for the specified key and executes the provided function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := NewRWKeyedLocker[string](2 * time.Second)
|
||||
|
||||
// Simulate a key
|
||||
key := "resource_key"
|
||||
|
||||
fn := func() {
|
||||
fmt.Println("Starting write operation...")
|
||||
// Simulate write operation, assuming it takes 2 seconds
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Println("Write operation completed!")
|
||||
}
|
||||
|
||||
// Acquire the write lock and execute the operation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the lock operation with a 3-second timeout
|
||||
err := locker.Lock(ctx, key, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//output:
|
||||
//Starting write operation...
|
||||
//Write operation completed!
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewTryKeyedLocker">NewTryKeyedLocker</span>
|
||||
|
||||
<p>TryKeyedLocker is a non-blocking version of KeyedLocker.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K]
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TryLock">TryLock</span>
|
||||
|
||||
<p>TryLock tries to acquire a lock for the specified key.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *TryKeyedLocker[K]) TryLock(key K) bool
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Unlock">Unlock</span>
|
||||
|
||||
<p>Unlock releases the lock for the specified key.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *TryKeyedLocker[K]) Unlock(key K)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
locker := concurrency.NewTryKeyedLocker[string]()
|
||||
|
||||
key := "resource_key"
|
||||
|
||||
if locker.TryLock(key) {
|
||||
fmt.Println("Lock acquired")
|
||||
time.Sleep(1 * time.Second)
|
||||
// Unlock after work is done
|
||||
locker.Unlock(key)
|
||||
fmt.Println("Lock released")
|
||||
} else {
|
||||
fmt.Println("Lock failed")
|
||||
}
|
||||
|
||||
//output:
|
||||
//Lock acquired
|
||||
//Lock released
|
||||
}
|
||||
```
|
||||
|
||||
@@ -155,7 +155,7 @@ func main() {
|
||||
func AddWeek(t time.Time, weeks int64) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/M9TqdMiaA2p)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -191,7 +191,7 @@ func main() {
|
||||
func AddMonth(t time.Time, months int64) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/DLoiOnpLvsN)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -335,7 +335,7 @@ func main() {
|
||||
func AddDaySafe(t time.Time, days int) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JTohZFpoDJ3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -382,7 +382,7 @@ func main() {
|
||||
func AddMonthSafe(t time.Time, months int) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KLw0lo6mbVW)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -419,7 +419,7 @@ func main() {
|
||||
func AddYearSafe(t time.Time, years int) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KVGXWZZ54ZH)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -551,7 +551,7 @@ func main() {
|
||||
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ynjoJPz7VNV)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/DCHdcL6gnfV)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -564,12 +564,13 @@ import (
|
||||
|
||||
func main() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
result := datetime.BeginOfWeek(input)
|
||||
|
||||
result := datetime.BeginOfWeek(input, time.Monday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 00:00:00 +0000 UTC
|
||||
// 2023-01-02 00:00:00 +0000 UTC
|
||||
}
|
||||
```
|
||||
|
||||
@@ -743,7 +744,7 @@ func main() {
|
||||
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/i08qKXD9flf)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mGSA162YgX9)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -756,12 +757,12 @@ import (
|
||||
|
||||
func main() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
result := datetime.EndOfWeek(input)
|
||||
result := datetime.EndOfWeek(input, time.Sunday)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-14 23:59:59.999999999 +0000 UTC
|
||||
// 2023-01-08 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import (
|
||||
func NewEventBus[T any]() *EventBus[T]
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gHbOPV_NUOJ)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -65,7 +65,7 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -84,7 +84,7 @@ func main() {
|
||||
func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/EYGf_8cHei-)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -106,8 +106,8 @@ func main() {
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, filter)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 2})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -126,7 +126,7 @@ func main() {
|
||||
func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T))
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Tmh7Ttfvprf)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Unsubscribe("event1", listener)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -161,10 +161,10 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (eb *EventBus[T]) Publish(event Event[T])
|
||||
func (eb *EventBus[T]) Publish(event eventbus.Event[T])
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gHTtVexFSH9)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -179,7 +179,7 @@ func main() {
|
||||
fmt.Println(eventData)
|
||||
}, false, 0, nil)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
@@ -196,7 +196,7 @@ func main() {
|
||||
func (eb *EventBus[T]) ClearListeners()
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KBfBYlKPgqD)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -213,9 +213,12 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListeners()
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -234,7 +237,7 @@ func main() {
|
||||
func (eb *EventBus[T]) ClearListenersByTopic(topic string)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gvMljmJOZmU)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -251,14 +254,17 @@ func main() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListenersByTopic("event1")
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 2
|
||||
}
|
||||
```
|
||||
|
||||
@@ -272,7 +278,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetListenersCount(topic string) int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j6yaJ2xAmFz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -284,14 +290,14 @@ func main() {
|
||||
eb := eventbus.NewEventBus[int]()
|
||||
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
|
||||
|
||||
count := eb.GetListenersCount("event1")
|
||||
|
||||
fmt.Println(count)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -305,7 +311,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetAllListenersCount() int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/PUlr0xcpEOz)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -338,7 +344,7 @@ func main() {
|
||||
func (eb *EventBus[T]) GetEvents() []string
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/etgjjcOtAjX)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -359,8 +365,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Output:
|
||||
// event1
|
||||
// event2
|
||||
// event1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -374,7 +380,7 @@ func main() {
|
||||
func (eb *EventBus[T]) SetErrorHandler(handler func(err error))
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gmB0gnFe5mc)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -393,7 +399,7 @@ func main() {
|
||||
panic("error")
|
||||
}, false, 0, nil)
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
|
||||
|
||||
// Output:
|
||||
// error
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
- [IsDir](#IsDir)
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [RemoveDir](#RemoveDir)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
- [ReadFileByLine](#ReadFileByLine)
|
||||
- [Zip](#Zip)
|
||||
@@ -395,7 +396,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveFile(path string) error
|
||||
func RemoveFile(path string, onDelete ...func(path string)) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/P2y0XW8a1SH)</span></b>
|
||||
@@ -416,6 +417,41 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveDir">RemoveDir</span>
|
||||
|
||||
<p>Delete directory.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveDir(path string, onDelete ...func(path string)) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var deletedPaths []string
|
||||
|
||||
err := fileutil.RemoveDir("./tempdir", func(p string) {
|
||||
deletedPaths = append(deletedPaths, p)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(deletedPaths)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileToString">ReadFileToString</span>
|
||||
|
||||
<p>Return string of file content.</p>
|
||||
|
||||
@@ -79,6 +79,7 @@ import (
|
||||
- [GetOrSet](#GetOrSet)
|
||||
- [SortByKey](#SortByKey)
|
||||
- [GetOrDefault](#GetOrDefault)
|
||||
- [FindValuesBy](#FindValuesBy)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -2276,8 +2277,8 @@ func main() {
|
||||
}
|
||||
|
||||
result := maputil.SortByKey(m, func(a, b int) bool {
|
||||
return a < b
|
||||
})
|
||||
return a < b
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
@@ -2288,7 +2289,7 @@ func main() {
|
||||
|
||||
### <span id="GetOrDefault">GetOrDefault</span>
|
||||
|
||||
<p>returns the value of the given key or a default value if the key is not present.</p>
|
||||
<p>Returns the value of the given key or a default value if the key is not present.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -2296,7 +2297,7 @@ func main() {
|
||||
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/99QjSYSBdiM)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/99QjSYSBdiM)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -2324,4 +2325,43 @@ func main() {
|
||||
// a
|
||||
// default
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FindValuesBy">FindValuesBy</span>
|
||||
|
||||
<p>Returns a slice of values from the map that satisfy the given predicate function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
4: "d",
|
||||
}
|
||||
|
||||
result := maputil.FindValuesBy(m, func(k int, v string) bool {
|
||||
return k%2 == 0
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [b d]
|
||||
}
|
||||
```
|
||||
@@ -48,6 +48,9 @@ import (
|
||||
- [UploadFile](#UploadFile)
|
||||
- [IsPingConnected](#IsPingConnected)
|
||||
- [IsTelnetConnected](#IsTelnetConnected)
|
||||
- [BuildUrl](#BuildUrl)
|
||||
- [AddQueryParams](#AddQueryParams)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1031,3 +1034,83 @@ func main() {
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BuildUrl">BuildUrl</span>
|
||||
|
||||
<p>Builds a URL from the given params.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
urlStr, err := netutil.BuildUrl(
|
||||
"https",
|
||||
"example.com",
|
||||
"query",
|
||||
map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com/query?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AddQueryParams">AddQueryParams</span>
|
||||
|
||||
<p>Adds query parameters to the given URL.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func AddQueryParams(urlStr string, params map[string][]string) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
urlStr, err := netutil.BuildUrl(
|
||||
"https",
|
||||
"example.com",
|
||||
"query",
|
||||
map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com/query?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
@@ -852,7 +852,7 @@ func main() {
|
||||
func EqualUnordered[T comparable](slice1, slice2 []T) bool
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/n8fSc2w8ZgX)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -1768,7 +1768,7 @@ func main() {
|
||||
func ReverseCopy[T any](slice []T) []T
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/c9arEaP7Cg-)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -2062,7 +2062,7 @@ func main() {
|
||||
func ShuffleCopy[T any](slice []T) []T
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vqDa-Gs1vT0)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -1774,7 +1774,7 @@ func main() {
|
||||
func FindAllOccurrences(str, substr string) []int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uvyA6azGLB1)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -826,6 +826,43 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsAlphaNumeric">IsAlphaNumeric</span>
|
||||
|
||||
<p>Check if the string is alphanumeric.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsAlphaNumeric(s string) bool
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/todo)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsAlphaNumeric("ABC")
|
||||
result2 := validator.IsAlphaNumeric("123")
|
||||
result3 := validator.IsAlphaNumeric("abc123")
|
||||
result4 := validator.IsAlphaNumeric("abc123@#$")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsJSON">IsJSON</span>
|
||||
|
||||
<p>Check if the string is valid JSON.</p>
|
||||
@@ -1003,7 +1040,7 @@ func main() {
|
||||
func IsIpPort(str string) bool
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/xUmls_b9L29)</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
|
||||
@@ -33,7 +33,7 @@ type EventListener[T any] struct {
|
||||
}
|
||||
|
||||
// NewEventBus creates a new EventBus.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/gHbOPV_NUOJ
|
||||
func NewEventBus[T any]() *EventBus[T] {
|
||||
return &EventBus[T]{
|
||||
listeners: sync.Map{},
|
||||
@@ -41,7 +41,7 @@ func NewEventBus[T any]() *EventBus[T] {
|
||||
}
|
||||
|
||||
// Subscribe subscribes to an event with a specific event topic and listener function.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/EYGf_8cHei-
|
||||
func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool) {
|
||||
eb.mu.Lock()
|
||||
defer eb.mu.Unlock()
|
||||
@@ -65,7 +65,7 @@ func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async
|
||||
}
|
||||
|
||||
// Unsubscribe unsubscribes from an event with a specific event topic and listener function.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/Tmh7Ttfvprf
|
||||
func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T)) {
|
||||
eb.mu.Lock()
|
||||
defer eb.mu.Unlock()
|
||||
@@ -89,7 +89,7 @@ func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T)) {
|
||||
}
|
||||
|
||||
// Publish publishes an event with a specific event topic and data payload.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/gHTtVexFSH9
|
||||
func (eb *EventBus[T]) Publish(event Event[T]) {
|
||||
eb.mu.RLock()
|
||||
defer eb.mu.RUnlock()
|
||||
@@ -125,12 +125,13 @@ func (eb *EventBus[T]) publishToListener(listener *EventListener[T], event Event
|
||||
}
|
||||
|
||||
// SetErrorHandler sets the error handler function.
|
||||
// Play: https://go.dev/play/p/gmB0gnFe5mc
|
||||
func (eb *EventBus[T]) SetErrorHandler(handler func(err error)) {
|
||||
eb.errorHandler = handler
|
||||
}
|
||||
|
||||
// ClearListeners clears all the listeners.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/KBfBYlKPgqD
|
||||
func (eb *EventBus[T]) ClearListeners() {
|
||||
eb.mu.Lock()
|
||||
defer eb.mu.Unlock()
|
||||
@@ -139,7 +140,7 @@ func (eb *EventBus[T]) ClearListeners() {
|
||||
}
|
||||
|
||||
// ClearListenersByTopic clears all the listeners by topic.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/gvMljmJOZmU
|
||||
func (eb *EventBus[T]) ClearListenersByTopic(topic string) {
|
||||
eb.mu.Lock()
|
||||
defer eb.mu.Unlock()
|
||||
@@ -148,7 +149,7 @@ func (eb *EventBus[T]) ClearListenersByTopic(topic string) {
|
||||
}
|
||||
|
||||
// GetListenersCount returns the number of listeners for a specific event topic.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/8VPJsMQgStM
|
||||
func (eb *EventBus[T]) GetListenersCount(topic string) int {
|
||||
eb.mu.RLock()
|
||||
defer eb.mu.RUnlock()
|
||||
@@ -163,7 +164,7 @@ func (eb *EventBus[T]) GetListenersCount(topic string) int {
|
||||
}
|
||||
|
||||
// GetAllListenersCount returns the total number of listeners.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/PUlr0xcpEOz
|
||||
func (eb *EventBus[T]) GetAllListenersCount() int {
|
||||
eb.mu.RLock()
|
||||
defer eb.mu.RUnlock()
|
||||
@@ -178,7 +179,7 @@ func (eb *EventBus[T]) GetAllListenersCount() int {
|
||||
}
|
||||
|
||||
// GetEvents returns all the events topics.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/etgjjcOtAjX
|
||||
func (eb *EventBus[T]) GetEvents() []string {
|
||||
eb.mu.RLock()
|
||||
defer eb.mu.RUnlock()
|
||||
|
||||
@@ -2,6 +2,7 @@ package eventbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -136,9 +137,12 @@ func ExampleEventBus_ClearListeners() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListeners()
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
@@ -155,28 +159,31 @@ func ExampleEventBus_ClearListenersByTopic() {
|
||||
}
|
||||
|
||||
eb.Subscribe("event1", listener, false, 0, nil)
|
||||
eb.Subscribe("event2", listener, false, 0, nil)
|
||||
|
||||
eb.ClearListenersByTopic("event1")
|
||||
|
||||
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
|
||||
eb.Publish(Event[int]{Topic: "event2", Payload: 2})
|
||||
|
||||
fmt.Println(receivedData)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleEventBus_GetListenersCount() {
|
||||
eb := NewEventBus[int]()
|
||||
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
|
||||
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
|
||||
|
||||
count := eb.GetListenersCount("event1")
|
||||
|
||||
fmt.Println(count)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleEventBus_SetErrorHandler() {
|
||||
@@ -218,6 +225,7 @@ func ExampleEventBus_GetEvents() {
|
||||
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
|
||||
|
||||
events := eb.GetEvents()
|
||||
sort.Strings(events)
|
||||
|
||||
for _, event := range events {
|
||||
fmt.Println(event)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -213,7 +214,8 @@ func TestEventBus_GetEvents(t *testing.T) {
|
||||
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
|
||||
|
||||
events := eb.GetEvents()
|
||||
sort.Strings(events)
|
||||
|
||||
assert.Equal(2, len(events))
|
||||
assert.EqualValues([]string{"event1", "event2"}, events)
|
||||
assert.Equal([]string{"event1", "event2"}, events)
|
||||
}
|
||||
|
||||
@@ -172,10 +172,54 @@ func IsDir(path string) bool {
|
||||
|
||||
// RemoveFile remove the path file.
|
||||
// Play: https://go.dev/play/p/P2y0XW8a1SH
|
||||
func RemoveFile(path string) error {
|
||||
func RemoveFile(path string, onDelete ...func(path string)) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("%s is a directory", path)
|
||||
}
|
||||
|
||||
if len(onDelete) > 0 && onDelete[0] != nil {
|
||||
onDelete[0](path)
|
||||
}
|
||||
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// RemoveDir remove the path directory.
|
||||
// Play: todo
|
||||
func RemoveDir(path string, onDelete ...func(path string)) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return fmt.Errorf("%s is not a directory", path)
|
||||
}
|
||||
|
||||
var callback func(string)
|
||||
if len(onDelete) > 0 {
|
||||
callback = onDelete[0]
|
||||
}
|
||||
|
||||
err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
|
||||
if err == nil && callback != nil {
|
||||
callback(p)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// CopyFile copy src file to dest file.
|
||||
// Play: https://go.dev/play/p/Jg9AMJMLrJi
|
||||
func CopyFile(srcPath string, dstPath string) error {
|
||||
|
||||
@@ -85,14 +85,37 @@ func TestIsDir(t *testing.T) {
|
||||
|
||||
func TestRemoveFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRemoveFile")
|
||||
|
||||
f := "./text.txt"
|
||||
if !IsExist(f) {
|
||||
CreateFile(f)
|
||||
err := RemoveFile(f)
|
||||
assert.IsNil(err)
|
||||
CreateFile(f)
|
||||
err := RemoveFile(f)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestRemoveDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRemoveDir")
|
||||
|
||||
err := os.MkdirAll("./tempdir/a/b", 0755)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var deletedPaths []string
|
||||
|
||||
err = RemoveDir("./tempdir", func(p string) {
|
||||
deletedPaths = append(deletedPaths, p)
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal([]string{"./tempdir", "tempdir/a", "tempdir/a/b"}, deletedPaths)
|
||||
assert.Equal(false, IsExist("./tempdir"))
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
|
||||
@@ -666,3 +666,17 @@ func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// FindValuesBy returns a slice of values from the map that satisfy the given predicate function.
|
||||
// Play: todo
|
||||
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V {
|
||||
result := make([]V, 0)
|
||||
|
||||
for k, v := range m {
|
||||
if predicate(k, v) {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -829,3 +829,25 @@ func ExampleOrderedMap_UnmarshalJSON() {
|
||||
|
||||
// fmt.Println(om.Elements())
|
||||
}
|
||||
|
||||
func ExampleFindValuesBy() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
4: "d",
|
||||
}
|
||||
|
||||
result := FindValuesBy(m, func(k int, v string) bool {
|
||||
return k%2 == 0
|
||||
})
|
||||
|
||||
// github action will excute this test currently, so sort the result
|
||||
// to make it deterministic
|
||||
sort.Strings(result)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [b d]
|
||||
}
|
||||
|
||||
@@ -888,3 +888,41 @@ func TestGetOrDefault(t *testing.T) {
|
||||
result2 := GetOrDefault(m1, 5, "123")
|
||||
assert.Equal("123", result2)
|
||||
}
|
||||
|
||||
func TestFindValuesBy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFindValuesBy")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputMap map[string]string
|
||||
key string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Key exists",
|
||||
inputMap: map[string]string{"a": "1", "b": "2", "c": "3"},
|
||||
key: "b",
|
||||
expected: []string{"2"},
|
||||
},
|
||||
{
|
||||
name: "Key does not exist",
|
||||
inputMap: map[string]string{"a": "1", "b": "2", "c": "3"},
|
||||
key: "d",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "Empty map",
|
||||
inputMap: map[string]string{},
|
||||
key: "a",
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
result := FindValuesBy(tt.inputMap, func(key string, value string) bool {
|
||||
return key == tt.key
|
||||
})
|
||||
assert.Equal(tt.expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package netutil
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -27,8 +26,8 @@ func TestHttpGet(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestHttpPost(t *testing.T) {
|
||||
@@ -49,8 +48,8 @@ func TestHttpPost(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestHttpPostFormData(t *testing.T) {
|
||||
@@ -69,8 +68,8 @@ func TestHttpPostFormData(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestHttpPut(t *testing.T) {
|
||||
@@ -92,8 +91,8 @@ func TestHttpPut(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestHttpPatch(t *testing.T) {
|
||||
@@ -115,8 +114,8 @@ func TestHttpPatch(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestHttpDelete(t *testing.T) {
|
||||
@@ -127,8 +126,8 @@ func TestHttpDelete(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestConvertMapToQueryString(t *testing.T) {
|
||||
@@ -229,8 +228,8 @@ func TestHttpClent_Post(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
t.Log("response status:", resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestStructToUrlValues(t *testing.T) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -305,3 +307,97 @@ func IsTelnetConnected(host string, port string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// BuildUrl builds a URL from the given params.
|
||||
// Play: todo
|
||||
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error) {
|
||||
if err := validateScheme(scheme); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
if !hostRegex.MatchString(host) {
|
||||
return "", fmt.Errorf("invalid host: '%s' is not a valid host", host)
|
||||
}
|
||||
}
|
||||
|
||||
parsedUrl := &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
parsedUrl.Path = "/"
|
||||
} else if !strings.HasPrefix(path, "/") {
|
||||
parsedUrl.Path = "/" + path
|
||||
} else {
|
||||
parsedUrl.Path = path
|
||||
}
|
||||
|
||||
queryParams := parsedUrl.Query()
|
||||
|
||||
for key, values := range query {
|
||||
for _, value := range values {
|
||||
queryParams.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
parsedUrl.RawQuery = queryParams.Encode()
|
||||
|
||||
return parsedUrl.String(), nil
|
||||
}
|
||||
|
||||
var supportedSchemes = map[string]bool{
|
||||
"http": true,
|
||||
"https": true,
|
||||
"ftp": true,
|
||||
"file": true,
|
||||
"mailto": true,
|
||||
"ws": true, // WebSocket
|
||||
"wss": true, // WebSocket Secure
|
||||
"data": true, // Data URL
|
||||
}
|
||||
|
||||
func validateScheme(scheme string) error {
|
||||
if _, exists := supportedSchemes[scheme]; !exists {
|
||||
return fmt.Errorf("invalid scheme: '%s' is not supported", scheme)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])(\.[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])*$`)
|
||||
var pathRegex = regexp.MustCompile(`^\/([a-zA-Z0-9%_-]+(?:\/[a-zA-Z0-9%_-]+)*)$`)
|
||||
|
||||
var alphaNumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
|
||||
|
||||
// AddQueryParams adds query parameters to the given URL.
|
||||
// Play: todoå
|
||||
func AddQueryParams(urlStr string, params map[string][]string) (string, error) {
|
||||
parsedUrl, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
queryParams := parsedUrl.Query()
|
||||
|
||||
for k, values := range params {
|
||||
if k == "" {
|
||||
return "", errors.New("empty key is not allowed")
|
||||
}
|
||||
|
||||
if !alphaNumericRegex.MatchString(k) {
|
||||
return "", fmt.Errorf("query parameter key %s must be alphanumeric", k)
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
if !alphaNumericRegex.MatchString(v) {
|
||||
return "", fmt.Errorf("query parameter value %s must be alphanumeric", v)
|
||||
}
|
||||
queryParams.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
parsedUrl.RawQuery = queryParams.Encode()
|
||||
|
||||
return parsedUrl.String(), nil
|
||||
}
|
||||
|
||||
@@ -201,3 +201,40 @@ func ExampleIsTelnetConnected() {
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleBuildUrl() {
|
||||
urlStr, err := BuildUrl(
|
||||
"https",
|
||||
"example.com",
|
||||
"query",
|
||||
map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com/query?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
|
||||
func ExampleAddQueryParams() {
|
||||
urlStr := "https://example.com"
|
||||
|
||||
params := map[string][]string{
|
||||
"a": {"foo", "bar"},
|
||||
"b": {"baz"},
|
||||
}
|
||||
|
||||
urlStr, err := AddQueryParams(urlStr, params)
|
||||
|
||||
fmt.Println(urlStr)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// https://example.com?a=foo&a=bar&b=baz
|
||||
// <nil>
|
||||
}
|
||||
|
||||
@@ -142,3 +142,110 @@ func TestTelnetConnected(t *testing.T) {
|
||||
result2 := IsTelnetConnected("www.baidu.com", "123")
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
func TestBuildUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBuildUrl")
|
||||
|
||||
tests := []struct {
|
||||
scheme string
|
||||
host string
|
||||
path string
|
||||
query map[string][]string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
scheme: "http",
|
||||
host: "www.test.com",
|
||||
path: "/path/subpath",
|
||||
query: map[string][]string{"a": {"1"}, "b": {"2"}},
|
||||
want: "http://www.test.com/path/subpath?a=1&b=2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
scheme: "http",
|
||||
host: "www.test.com",
|
||||
path: "/simple-path",
|
||||
query: map[string][]string{"a": {"1"}, "b": {"2"}},
|
||||
want: "http://www.test.com/simple-path?a=1&b=2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
scheme: "http",
|
||||
host: "www.test.com",
|
||||
path: "",
|
||||
query: map[string][]string{"a": {"foo", "bar"}, "b": {"baz"}},
|
||||
want: "http://www.test.com/?a=foo&a=bar&b=baz",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
scheme: "https",
|
||||
host: "www.test. com",
|
||||
path: "/path",
|
||||
query: nil,
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
scheme: "https",
|
||||
host: "www.test.com",
|
||||
path: "/path with spaces",
|
||||
query: nil,
|
||||
want: "https://www.test.com/path%20with%20spaces",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got, err := BuildUrl(tt.scheme, tt.host, tt.path, tt.query)
|
||||
assert.Equal(tt.want, got)
|
||||
assert.Equal(tt.wantErr, err != nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAddQueryParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAddQueryParams")
|
||||
|
||||
tests := []struct {
|
||||
url string
|
||||
query map[string][]string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
url: "http://www.test.com",
|
||||
query: map[string][]string{"a": {"1"}, "b": {"2"}},
|
||||
want: "http://www.test.com?a=1&b=2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
url: "http://www.test.com",
|
||||
query: map[string][]string{"a": {"foo", "bar"}, "b": {"baz"}},
|
||||
want: "http://www.test.com?a=foo&a=bar&b=baz",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
url: "http://www.test.com",
|
||||
query: map[string][]string{},
|
||||
want: "http://www.test.com",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
url: "http://www.test.com",
|
||||
query: map[string][]string{"a": {"$%"}},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got, err := AddQueryParams(tt.url, tt.query)
|
||||
assert.Equal(tt.want, got)
|
||||
assert.Equal(tt.wantErr, err != nil)
|
||||
}
|
||||
}
|
||||
|
||||
225
slice/slice.go
225
slice/slice.go
@@ -59,16 +59,18 @@ func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
elementMap := make(map[T]struct{}, len(slice))
|
||||
elementCount := make(map[T]int, len(slice))
|
||||
for _, item := range slice {
|
||||
elementMap[item] = struct{}{}
|
||||
elementCount[item]++
|
||||
}
|
||||
|
||||
for _, item := range subSlice {
|
||||
if _, ok := elementMap[item]; !ok {
|
||||
if elementCount[item] == 0 {
|
||||
return false
|
||||
}
|
||||
elementCount[item]--
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -81,14 +83,18 @@ func Chunk[T any](slice []T, size int) [][]T {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, item := range slice {
|
||||
l := len(result)
|
||||
if l == 0 || len(result[l-1]) == size {
|
||||
result = append(result, []T{})
|
||||
l++
|
||||
}
|
||||
currentChunk := []T{}
|
||||
|
||||
result[l-1] = append(result[l-1], item)
|
||||
for _, item := range slice {
|
||||
if len(currentChunk) == size {
|
||||
result = append(result, currentChunk)
|
||||
currentChunk = []T{}
|
||||
}
|
||||
currentChunk = append(currentChunk, item)
|
||||
}
|
||||
|
||||
if len(currentChunk) > 0 {
|
||||
result = append(result, currentChunk)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -106,6 +112,7 @@ func Compact[T comparable](slice []T) []T {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result[:len(result):len(result)]
|
||||
}
|
||||
|
||||
@@ -133,8 +140,17 @@ func Concat[T any](slices ...[]T) []T {
|
||||
func Difference[T comparable](slice, comparedSlice []T) []T {
|
||||
result := []T{}
|
||||
|
||||
if len(slice) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
comparedMap := make(map[T]struct{}, len(comparedSlice))
|
||||
for _, v := range comparedSlice {
|
||||
comparedMap[v] = struct{}{}
|
||||
}
|
||||
|
||||
for _, v := range slice {
|
||||
if !Contain(comparedSlice, v) {
|
||||
if _, found := comparedMap[v]; !found {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
@@ -147,13 +163,17 @@ func Difference[T comparable](slice, comparedSlice []T) []T {
|
||||
// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy.
|
||||
// Play: https://go.dev/play/p/DiivgwM5OnC
|
||||
func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(index int, item T) T) []T {
|
||||
orginSliceAfterMap := Map(slice, iteratee)
|
||||
comparedSliceAfterMap := Map(comparedSlice, iteratee)
|
||||
|
||||
result := make([]T, 0)
|
||||
for i, v := range orginSliceAfterMap {
|
||||
if !Contain(comparedSliceAfterMap, v) {
|
||||
result = append(result, slice[i])
|
||||
|
||||
comparedMap := make(map[T]struct{}, len(comparedSlice))
|
||||
for _, item := range comparedSlice {
|
||||
comparedMap[iteratee(0, item)] = struct{}{}
|
||||
}
|
||||
|
||||
for i, item := range slice {
|
||||
transformedItem := iteratee(i, item)
|
||||
if _, found := comparedMap[transformedItem]; !found {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,23 +185,32 @@ func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(inde
|
||||
// The comparator is invoked with two arguments: (arrVal, othVal).
|
||||
// Play: https://go.dev/play/p/v2U2deugKuV
|
||||
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(item1, item2 T) bool) []T {
|
||||
result := make([]T, 0)
|
||||
|
||||
getIndex := func(arr []T, item T, comparison func(v1, v2 T) bool) int {
|
||||
index := -1
|
||||
for i, v := range arr {
|
||||
if comparison(item, v) {
|
||||
index = i
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
result := make([]T, 0, len(slice))
|
||||
|
||||
comparedMap := make(map[int]T, len(comparedSlice))
|
||||
for _, v := range comparedSlice {
|
||||
comparedMap[getIndex(comparedSlice, v, comparator)] = v
|
||||
}
|
||||
|
||||
for _, v := range slice {
|
||||
found := false
|
||||
for _, existing := range comparedSlice {
|
||||
if comparator(v, existing) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
for i, v := range slice {
|
||||
index := getIndex(comparedSlice, v, comparator)
|
||||
if index == -1 {
|
||||
result = append(result, slice[i])
|
||||
if !found {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +250,7 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
|
||||
}
|
||||
|
||||
// EqualUnordered checks if two slices are equal: the same length and all elements' value are equal (unordered).
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/n8fSc2w8ZgX
|
||||
func EqualUnordered[T comparable](slice1, slice2 []T) bool {
|
||||
if len(slice1) != len(slice2) {
|
||||
return false
|
||||
@@ -423,19 +452,20 @@ func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T,
|
||||
// Flatten flattens slice with one level.
|
||||
// Play: https://go.dev/play/p/hYa3cBEevtm
|
||||
func Flatten(slice any) any {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
var result reflect.Value
|
||||
if sv.Type().Elem().Kind() == reflect.Interface {
|
||||
result = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, sv.Len())
|
||||
} else if sv.Type().Elem().Kind() == reflect.Slice {
|
||||
result = reflect.MakeSlice(sv.Type().Elem(), 0, sv.Len())
|
||||
} else {
|
||||
return result
|
||||
sv := reflect.ValueOf(slice)
|
||||
if sv.Kind() != reflect.Slice {
|
||||
panic("Flatten: input must be a slice")
|
||||
}
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if elemType.Kind() == reflect.Slice {
|
||||
elemType = elemType.Elem()
|
||||
}
|
||||
|
||||
result := reflect.MakeSlice(reflect.SliceOf(elemType), 0, sv.Len())
|
||||
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
item := reflect.ValueOf(sv.Index(i).Interface())
|
||||
item := sv.Index(i)
|
||||
if item.Kind() == reflect.Slice {
|
||||
for j := 0; j < item.Len(); j++ {
|
||||
result = reflect.Append(result, item.Index(j))
|
||||
@@ -607,7 +637,7 @@ func Repeat[T any](item T, n int) []T {
|
||||
}
|
||||
|
||||
// InterfaceSlice convert param to slice of interface.
|
||||
// This function is deprecated, use generics feature of go1.18+ for replacement.
|
||||
// deprecated: use generics feature of go1.18+ for replacement.
|
||||
// Play: https://go.dev/play/p/FdQXF0Vvqs-
|
||||
func InterfaceSlice(slice any) []any {
|
||||
sv := sliceValue(slice)
|
||||
@@ -624,7 +654,7 @@ func InterfaceSlice(slice any) []any {
|
||||
}
|
||||
|
||||
// StringSlice convert param to slice of string.
|
||||
// This function is deprecated, use generics feature of go1.18+ for replacement.
|
||||
// deprecated: use generics feature of go1.18+ for replacement.
|
||||
// Play: https://go.dev/play/p/W0TZDWCPFcI
|
||||
func StringSlice(slice any) []string {
|
||||
v := sliceValue(slice)
|
||||
@@ -642,7 +672,7 @@ func StringSlice(slice any) []string {
|
||||
}
|
||||
|
||||
// IntSlice convert param to slice of int.
|
||||
// This function is deprecated, use generics feature of go1.18+ for replacement.
|
||||
// deprecated: use generics feature of go1.18+ for replacement.
|
||||
// Play: https://go.dev/play/p/UQDj-on9TGN
|
||||
func IntSlice(slice any) []int {
|
||||
sv := sliceValue(slice)
|
||||
@@ -662,15 +692,22 @@ func IntSlice(slice any) []int {
|
||||
// DeleteAt delete the element of slice at index.
|
||||
// Play: https://go.dev/play/p/800B1dPBYyd
|
||||
func DeleteAt[T any](slice []T, index int) []T {
|
||||
if index >= len(slice) {
|
||||
index = len(slice) - 1
|
||||
if index < 0 || index >= len(slice) {
|
||||
return slice[:len(slice)-1]
|
||||
}
|
||||
|
||||
result := make([]T, len(slice)-1)
|
||||
copy(result, slice[:index])
|
||||
copy(result[index:], slice[index+1:])
|
||||
result := append([]T(nil), slice...)
|
||||
copy(result[index:], result[index+1:])
|
||||
|
||||
return result
|
||||
// Set the last element to zero value, clean up the memory.
|
||||
result[len(result)-1] = zeroValue[T]()
|
||||
|
||||
return result[:len(result)-1]
|
||||
}
|
||||
|
||||
func zeroValue[T any]() T {
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
// DeleteRange delete the element of slice from start index to end index(exclude).
|
||||
@@ -766,46 +803,54 @@ func InsertAt[T any](slice []T, index int, value any) []T {
|
||||
return slice
|
||||
}
|
||||
|
||||
if v, ok := value.(T); ok {
|
||||
slice = append(slice[:index], append([]T{v}, slice[index:]...)...)
|
||||
switch v := value.(type) {
|
||||
case T:
|
||||
result := make([]T, size+1)
|
||||
copy(result, slice[:index])
|
||||
result[index] = v
|
||||
copy(result[index+1:], slice[index:])
|
||||
return result
|
||||
case []T:
|
||||
result := make([]T, size+len(v))
|
||||
copy(result, slice[:index])
|
||||
copy(result[index:], v)
|
||||
copy(result[index+len(v):], slice[index:])
|
||||
return result
|
||||
default:
|
||||
return slice
|
||||
}
|
||||
|
||||
if v, ok := value.([]T); ok {
|
||||
slice = append(slice[:index], append(v, slice[index:]...)...)
|
||||
return slice
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
// UpdateAt update the slice element at index.
|
||||
// Play: https://go.dev/play/p/f3mh2KloWVm
|
||||
func UpdateAt[T any](slice []T, index int, value T) []T {
|
||||
size := len(slice)
|
||||
|
||||
if index < 0 || index >= size {
|
||||
if index < 0 || index >= len(slice) {
|
||||
return slice
|
||||
}
|
||||
slice = append(slice[:index], append([]T{value}, slice[index+1:]...)...)
|
||||
|
||||
return slice
|
||||
result := make([]T, len(slice))
|
||||
copy(result, slice)
|
||||
|
||||
result[index] = value
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique remove duplicate elements in slice.
|
||||
// Play: https://go.dev/play/p/AXw0R3ZTE6a
|
||||
func Unique[T comparable](slice []T) []T {
|
||||
result := make([]T, 0, len(slice))
|
||||
if len(slice) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
seen := make(map[T]struct{}, len(slice))
|
||||
result := slice[:0]
|
||||
|
||||
for i := range slice {
|
||||
if _, ok := seen[slice[i]]; ok {
|
||||
continue
|
||||
for _, item := range slice {
|
||||
if _, exists := seen[item]; !exists {
|
||||
seen[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
seen[slice[i]] = struct{}{}
|
||||
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -815,18 +860,19 @@ func Unique[T comparable](slice []T) []T {
|
||||
// The function maintains the order of the elements.
|
||||
// Play: https://go.dev/play/p/GY7JE4yikrl
|
||||
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
|
||||
result := make([]T, 0, len(slice))
|
||||
if len(slice) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
seen := make(map[U]struct{}, len(slice))
|
||||
result := slice[:0]
|
||||
|
||||
for i := range slice {
|
||||
key := iteratee(slice[i])
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
for _, item := range slice {
|
||||
key := iteratee(item)
|
||||
if _, exists := seen[key]; !exists {
|
||||
seen[key] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
seen[key] = struct{}{}
|
||||
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -836,19 +882,20 @@ func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
|
||||
// The function maintains the order of the elements.
|
||||
// Play: https://go.dev/play/p/rwSacr-ZHsR
|
||||
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T {
|
||||
result := make([]T, 0, len(slice))
|
||||
seen := make([]T, 0, len(slice))
|
||||
if len(slice) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
result := make([]T, 0, len(slice))
|
||||
for _, item := range slice {
|
||||
duplicate := false
|
||||
for _, seenItem := range seen {
|
||||
if comparator(item, seenItem) {
|
||||
duplicate = true
|
||||
isDuplicate := false
|
||||
for _, existing := range result {
|
||||
if comparator(item, existing) {
|
||||
isDuplicate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !duplicate {
|
||||
seen = append(seen, item)
|
||||
if !isDuplicate {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
@@ -1003,7 +1050,7 @@ func Reverse[T any](slice []T) {
|
||||
}
|
||||
|
||||
// ReverseCopy return a new slice of element order is reversed to the given slice.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/c9arEaP7Cg-
|
||||
func ReverseCopy[T any](slice []T) []T {
|
||||
result := make([]T, len(slice))
|
||||
|
||||
@@ -1027,7 +1074,7 @@ func Shuffle[T any](slice []T) []T {
|
||||
}
|
||||
|
||||
// ShuffleCopy return a new slice with elements shuffled.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/vqDa-Gs1vT0
|
||||
func ShuffleCopy[T any](slice []T) []T {
|
||||
result := make([]T, len(slice))
|
||||
copy(result, slice)
|
||||
|
||||
@@ -420,3 +420,12 @@ func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int {
|
||||
func (s Stream[T]) ToSlice() []T {
|
||||
return s.source
|
||||
}
|
||||
|
||||
func ToMap[T any, K comparable, V any](s Stream[T], mapper func(item T) (K, V)) map[K]V {
|
||||
result := map[K]V{}
|
||||
for _, v := range s.source {
|
||||
key, value := mapper(v)
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -412,3 +412,22 @@ func ExampleStream_LastIndexOf() {
|
||||
// -1
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleToMap() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s := FromSlice([]Person{
|
||||
{Name: "Tom", Age: 10},
|
||||
{Name: "Jim", Age: 20},
|
||||
{Name: "Mike", Age: 30},
|
||||
})
|
||||
m := ToMap(s, func(p Person) (string, Person) {
|
||||
return p.Name, p
|
||||
})
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// map[Jim:{Jim 20} Mike:{Mike 30} Tom:{Tom 10}]
|
||||
}
|
||||
|
||||
@@ -400,3 +400,26 @@ func TestStream_LastIndexOf(t *testing.T) {
|
||||
assert.Equal(-1, s.LastIndexOf(0, func(a, b int) bool { return a == b }))
|
||||
assert.Equal(4, s.LastIndexOf(2, func(a, b int) bool { return a == b }))
|
||||
}
|
||||
|
||||
func TestStream_ToMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_ToMap")
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s := FromSlice([]Person{
|
||||
{Name: "Tom", Age: 10},
|
||||
{Name: "Jim", Age: 20},
|
||||
{Name: "Mike", Age: 30},
|
||||
})
|
||||
m := ToMap(s, func(p Person) (string, Person) {
|
||||
return p.Name, p
|
||||
})
|
||||
expected := map[string]Person{
|
||||
"Tom": {Name: "Tom", Age: 10},
|
||||
"Jim": {Name: "Jim", Age: 20},
|
||||
"Mike": {Name: "Mike", Age: 30},
|
||||
}
|
||||
assert.EqualValues(expected, m)
|
||||
|
||||
}
|
||||
|
||||
@@ -37,16 +37,17 @@ func CamelCase(s string) string {
|
||||
// Capitalize converts the first character of a string to upper case and the remaining to lower case.
|
||||
// Play: https://go.dev/play/p/2OAjgbmAqHZ
|
||||
func Capitalize(s string) string {
|
||||
result := make([]rune, len(s))
|
||||
for i, v := range s {
|
||||
if i == 0 {
|
||||
result[i] = unicode.ToUpper(v)
|
||||
} else {
|
||||
result[i] = unicode.ToLower(v)
|
||||
}
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return string(result)
|
||||
runes := []rune(s)
|
||||
runes[0] = unicode.ToUpper(runes[0])
|
||||
for i := 1; i < len(runes); i++ {
|
||||
runes[i] = unicode.ToLower(runes[i])
|
||||
}
|
||||
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
// UpperFirst converts the first character of string to upper case.
|
||||
@@ -127,49 +128,57 @@ func UpperSnakeCase(s string) string {
|
||||
// Before returns the substring of the source string up to the first occurrence of the specified string.
|
||||
// Play: https://go.dev/play/p/JAWTZDS4F5w
|
||||
func Before(s, char string) string {
|
||||
i := strings.Index(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
if char == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[0:i]
|
||||
if i := strings.Index(s, char); i >= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BeforeLast returns the substring of the source string up to the last occurrence of the specified string.
|
||||
// Play: https://go.dev/play/p/pJfXXAoG_Te
|
||||
func BeforeLast(s, char string) string {
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
if char == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[0:i]
|
||||
if i := strings.LastIndex(s, char); i >= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// After returns the substring after the first occurrence of a specified string in the source string.
|
||||
// Play: https://go.dev/play/p/RbCOQqCDA7m
|
||||
func After(s, char string) string {
|
||||
i := strings.Index(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
if char == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[i+len(char):]
|
||||
if i := strings.Index(s, char); i >= 0 {
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// AfterLast returns the substring after the last occurrence of a specified string in the source string.
|
||||
// Play: https://go.dev/play/p/1TegARrb8Yn
|
||||
func AfterLast(s, char string) string {
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
if char == "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return s[i+len(char):]
|
||||
if i := strings.LastIndex(s, char); i >= 0 {
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// IsString check if the value data type is string or not.
|
||||
@@ -213,20 +222,15 @@ func Wrap(str string, wrapWith string) string {
|
||||
// Unwrap a given string from anther string. will change source string.
|
||||
// Play: https://go.dev/play/p/Ec2q4BzCpG-
|
||||
func Unwrap(str string, wrapToken string) string {
|
||||
if str == "" || wrapToken == "" {
|
||||
if wrapToken == "" || !strings.HasPrefix(str, wrapToken) || !strings.HasSuffix(str, wrapToken) {
|
||||
return str
|
||||
}
|
||||
|
||||
firstIndex := strings.Index(str, wrapToken)
|
||||
lastIndex := strings.LastIndex(str, wrapToken)
|
||||
|
||||
if firstIndex == 0 && lastIndex > 0 && lastIndex <= len(str)-1 {
|
||||
if len(wrapToken) <= lastIndex {
|
||||
str = str[len(wrapToken):lastIndex]
|
||||
}
|
||||
if len(str) < 2*len(wrapToken) {
|
||||
return str
|
||||
}
|
||||
|
||||
return str
|
||||
return str[len(wrapToken) : len(str)-len(wrapToken)]
|
||||
}
|
||||
|
||||
// SplitEx split a given string which can control the result slice contains empty string or not.
|
||||
@@ -286,22 +290,21 @@ func Substring(s string, offset int, length uint) string {
|
||||
size := len(rs)
|
||||
|
||||
if offset < 0 {
|
||||
offset = size + offset
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
offset += size
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
if offset > size {
|
||||
return ""
|
||||
}
|
||||
|
||||
if length > uint(size)-uint(offset) {
|
||||
length = uint(size - offset)
|
||||
end := offset + int(length)
|
||||
if end > size {
|
||||
end = size
|
||||
}
|
||||
|
||||
str := string(rs[offset : offset+int(length)])
|
||||
|
||||
return strings.Replace(str, "\x00", "", -1)
|
||||
return strings.ReplaceAll(string(rs[offset:end]), "\x00", "")
|
||||
}
|
||||
|
||||
// SplitWords splits a string into words, word only contains alphabetic characters.
|
||||
@@ -393,7 +396,10 @@ func RemoveNonPrintable(str string) string {
|
||||
// StringToBytes converts a string to byte slice without a memory allocation.
|
||||
// Play: https://go.dev/play/p/7OyFBrf9AxA
|
||||
func StringToBytes(str string) (b []byte) {
|
||||
return *(*[]byte)(unsafe.Pointer(&str))
|
||||
return *(*[]byte)(unsafe.Pointer(&struct {
|
||||
string
|
||||
Cap int
|
||||
}{str, len(str)}))
|
||||
}
|
||||
|
||||
// BytesToString converts a byte slice to string without a memory allocation.
|
||||
@@ -758,7 +764,7 @@ func ExtractContent(str, start, end string) []string {
|
||||
}
|
||||
|
||||
// FindAllOccurrences returns the positions of all occurrences of a substring in a string.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/uvyA6azGLB1
|
||||
func FindAllOccurrences(str, substr string) []int {
|
||||
var positions []int
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -111,31 +112,27 @@ func padAtPosition(str string, length int, padStr string, position int) string {
|
||||
padStr = " "
|
||||
}
|
||||
|
||||
length = length - len(str)
|
||||
startPadLen := 0
|
||||
totalPad := length - len(str)
|
||||
startPad := 0
|
||||
|
||||
if position == 0 {
|
||||
startPadLen = length / 2
|
||||
startPad = totalPad / 2
|
||||
} else if position == 1 {
|
||||
startPadLen = length
|
||||
startPad = totalPad
|
||||
} else if position == 2 {
|
||||
startPad = 0
|
||||
}
|
||||
endPadLen := length - startPadLen
|
||||
endPad := totalPad - startPad
|
||||
|
||||
charLen := len(padStr)
|
||||
leftPad := ""
|
||||
cur := 0
|
||||
for cur < startPadLen {
|
||||
leftPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
repeatPad := func(n int) string {
|
||||
repeated := strings.Repeat(padStr, (n+len(padStr)-1)/len(padStr))
|
||||
return repeated[:n]
|
||||
}
|
||||
|
||||
cur = 0
|
||||
rightPad := ""
|
||||
for cur < endPadLen {
|
||||
rightPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
}
|
||||
left := repeatPad(startPad)
|
||||
right := repeatPad(endPad)
|
||||
|
||||
return leftPad + str + rightPad
|
||||
return left + str + right
|
||||
}
|
||||
|
||||
// isLetter checks r is a letter but not CJK character.
|
||||
|
||||
@@ -307,10 +307,21 @@ func TestBeforeLast(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeforeLast")
|
||||
|
||||
assert.Equal("lancet", BeforeLast("lancet", ""))
|
||||
assert.Equal("lancet", BeforeLast("lancet", "abcdef"))
|
||||
assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/"))
|
||||
assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test"))
|
||||
tests := []struct {
|
||||
input string
|
||||
char string
|
||||
expected string
|
||||
}{
|
||||
{"lancet", "", "lancet"},
|
||||
{"lancet", "lancet", ""},
|
||||
{"lancet", "abcdef", "lancet"},
|
||||
{"github.com/test/lancet", "/", "github.com/test"},
|
||||
{"github.com/test/test/lancet", "test", "github.com/test/"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, BeforeLast(tt.input, tt.char))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
@@ -318,11 +329,21 @@ func TestAfter(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestAfter")
|
||||
|
||||
assert.Equal("lancet", After("lancet", ""))
|
||||
assert.Equal("", After("lancet", "lancet"))
|
||||
assert.Equal("test/lancet", After("github.com/test/lancet", "/"))
|
||||
assert.Equal("/lancet", After("github.com/test/lancet", "test"))
|
||||
assert.Equal("lancet", After("lancet", "abcdef"))
|
||||
tests := []struct {
|
||||
input string
|
||||
char string
|
||||
expected string
|
||||
}{
|
||||
{"lancet", "", "lancet"},
|
||||
{"lancet", "lancet", ""},
|
||||
{"lancet", "abcdef", "lancet"},
|
||||
{"github.com/test/lancet", "/", "test/lancet"},
|
||||
{"github.com/test/lancet", "test", "/lancet"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, After(tt.input, tt.char))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterLast(t *testing.T) {
|
||||
@@ -330,11 +351,21 @@ func TestAfterLast(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestAfterLast")
|
||||
|
||||
assert.Equal("lancet", AfterLast("lancet", ""))
|
||||
assert.Equal("lancet", AfterLast("github.com/test/lancet", "/"))
|
||||
assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test"))
|
||||
assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test"))
|
||||
assert.Equal("lancet", AfterLast("lancet", "abcdef"))
|
||||
tests := []struct {
|
||||
input string
|
||||
char string
|
||||
expected string
|
||||
}{
|
||||
{"lancet", "", "lancet"},
|
||||
{"lancet", "lancet", ""},
|
||||
{"lancet", "abcdef", "lancet"},
|
||||
{"github.com/test/lancet", "/", "lancet"},
|
||||
{"github.com/test/test/lancet", "test", "/lancet"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, AfterLast(tt.input, tt.char))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsString(t *testing.T) {
|
||||
@@ -342,11 +373,20 @@ func TestIsString(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsString")
|
||||
|
||||
assert.Equal(true, IsString("lancet"))
|
||||
assert.Equal(true, IsString(""))
|
||||
assert.Equal(false, IsString(1))
|
||||
assert.Equal(false, IsString(true))
|
||||
assert.Equal(false, IsString([]string{}))
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
expected bool
|
||||
}{
|
||||
{"lancet", true},
|
||||
{"", true},
|
||||
{1, false},
|
||||
{true, false},
|
||||
{[]string{}, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsString(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
@@ -363,11 +403,21 @@ func TestWrap(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestWrap")
|
||||
|
||||
assert.Equal("ab", Wrap("ab", ""))
|
||||
assert.Equal("", Wrap("", "*"))
|
||||
assert.Equal("*ab*", Wrap("ab", "*"))
|
||||
assert.Equal("\"ab\"", Wrap("ab", "\""))
|
||||
assert.Equal("'ab'", Wrap("ab", "'"))
|
||||
tests := []struct {
|
||||
input string
|
||||
wrapper string
|
||||
expected string
|
||||
}{
|
||||
{"", "", ""},
|
||||
{"ab", "", "ab"},
|
||||
{"ab", "*", "*ab*"},
|
||||
{"ab", "\"", "\"ab\""},
|
||||
{"ab", "'", "'ab'"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, Wrap(tt.input, tt.wrapper))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
@@ -468,7 +518,8 @@ func TestStringToBytes(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStringToBytes")
|
||||
|
||||
bytes := StringToBytes("abc")
|
||||
assert.Equal(bytes, []byte{'a', 'b', 'c'})
|
||||
assert.Equal([]byte{'a', 'b', 'c'}, bytes)
|
||||
assert.Equal(3, cap(bytes))
|
||||
}
|
||||
|
||||
func TestBytesToString(t *testing.T) {
|
||||
@@ -485,10 +536,21 @@ func TestIsBlank(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsBlank")
|
||||
|
||||
assert.Equal(IsBlank(""), true)
|
||||
assert.Equal(IsBlank("\t\v\f\n"), true)
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"", true},
|
||||
{" ", true},
|
||||
{"\t\v\f\n", true},
|
||||
{"\t\v\f\nabc", false},
|
||||
{"abc", false},
|
||||
{" 中文", false},
|
||||
}
|
||||
|
||||
assert.Equal(IsBlank(" 中文"), false)
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsBlank(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNotBlank(t *testing.T) {
|
||||
@@ -496,12 +558,22 @@ func TestIsNotBlank(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsBlank")
|
||||
|
||||
assert.Equal(IsNotBlank(""), false)
|
||||
assert.Equal(IsNotBlank(" "), false)
|
||||
assert.Equal(IsNotBlank("\t\v\f\n"), false)
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"", false},
|
||||
{" ", false},
|
||||
{"\t\v\f\n", false},
|
||||
{"\t\v\f\nabc", true},
|
||||
{"abc", true},
|
||||
{" 中文", true},
|
||||
{" world ", true},
|
||||
}
|
||||
|
||||
assert.Equal(IsNotBlank(" 中文"), true)
|
||||
assert.Equal(IsNotBlank(" world "), true)
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsNotBlank(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefixAny(t *testing.T) {
|
||||
@@ -509,12 +581,19 @@ func TestHasPrefixAny(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestHasPrefixAny")
|
||||
|
||||
str := "foo bar"
|
||||
prefixes := []string{"fo", "xyz", "hello"}
|
||||
notMatches := []string{"oom", "world"}
|
||||
tests := []struct {
|
||||
str string
|
||||
prefixes []string
|
||||
expected bool
|
||||
}{
|
||||
{"foo bar", []string{"fo", "xyz", "hello"}, true},
|
||||
{"foo bar", []string{"oom", "world"}, false},
|
||||
{"foo bar", []string{}, false},
|
||||
}
|
||||
|
||||
assert.Equal(HasPrefixAny(str, prefixes), true)
|
||||
assert.Equal(HasPrefixAny(str, notMatches), false)
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, HasPrefixAny(tt.str, tt.prefixes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasSuffixAny(t *testing.T) {
|
||||
@@ -522,25 +601,44 @@ func TestHasSuffixAny(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestHasSuffixAny")
|
||||
|
||||
str := "foo bar"
|
||||
suffixes := []string{"bar", "xyz", "hello"}
|
||||
notMatches := []string{"oom", "world"}
|
||||
tests := []struct {
|
||||
str string
|
||||
suffixes []string
|
||||
expected bool
|
||||
}{
|
||||
{"foo bar", []string{"bar", "xyz", "hello"}, true},
|
||||
{"foo bar", []string{"oom", "world"}, false},
|
||||
{"foo bar", []string{}, false},
|
||||
}
|
||||
|
||||
assert.Equal(HasSuffixAny(str, suffixes), true)
|
||||
assert.Equal(HasSuffixAny(str, notMatches), false)
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, HasSuffixAny(tt.str, tt.suffixes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexOffset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOffset")
|
||||
|
||||
str := "foo bar hello world"
|
||||
|
||||
assert.Equal(IndexOffset(str, "o", 5), 12)
|
||||
assert.Equal(IndexOffset(str, "o", 0), 1)
|
||||
assert.Equal(IndexOffset(str, "d", len(str)-1), len(str)-1)
|
||||
assert.Equal(IndexOffset(str, "d", len(str)), -1)
|
||||
assert.Equal(IndexOffset(str, "f", -1), -1)
|
||||
tests := []struct {
|
||||
str string
|
||||
substr string
|
||||
offset int
|
||||
expected int
|
||||
}{
|
||||
{str, "o", 5, 12},
|
||||
{str, "o", 0, 1},
|
||||
{str, "d", len(str) - 1, len(str) - 1},
|
||||
{str, "d", len(str), -1},
|
||||
{str, "f", -1, -1},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IndexOffset(tt.str, tt.substr, tt.offset))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceWithMap(t *testing.T) {
|
||||
@@ -648,13 +746,23 @@ func TestSubInBetween(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestSubInBetween")
|
||||
|
||||
str := "abcde"
|
||||
tests := []struct {
|
||||
input string
|
||||
start string
|
||||
end string
|
||||
expected string
|
||||
}{
|
||||
{"abcde", "", "", ""},
|
||||
{"abcde", "a", "d", "bc"},
|
||||
{"abcde", "a", "e", "bcd"},
|
||||
{"abcde", "a", "f", ""},
|
||||
{"abcde", "a", "", ""},
|
||||
{"abcde", "", "e", "abcd"},
|
||||
}
|
||||
|
||||
assert.Equal("", SubInBetween(str, "", ""))
|
||||
assert.Equal("ab", SubInBetween(str, "", "c"))
|
||||
assert.Equal("bc", SubInBetween(str, "a", "d"))
|
||||
assert.Equal("", SubInBetween(str, "a", ""))
|
||||
assert.Equal("", SubInBetween(str, "a", "f"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, SubInBetween(tt.input, tt.start, tt.end))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHammingDistance(t *testing.T) {
|
||||
@@ -696,13 +804,22 @@ func TestConcat(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestConcat")
|
||||
|
||||
assert.Equal("", Concat(0))
|
||||
assert.Equal("a", Concat(1, "a"))
|
||||
assert.Equal("ab", Concat(2, "a", "b"))
|
||||
assert.Equal("abc", Concat(3, "a", "b", "c"))
|
||||
assert.Equal("abc", Concat(3, "a", "", "b", "c", ""))
|
||||
assert.Equal("你好,世界!", Concat(0, "你好", ",", "", "世界!", ""))
|
||||
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
|
||||
tests := []struct {
|
||||
args []string
|
||||
expected string
|
||||
}{
|
||||
{[]string{}, ""},
|
||||
{[]string{"a"}, "a"},
|
||||
{[]string{"a", "b"}, "ab"},
|
||||
{[]string{"a", "b", "c"}, "abc"},
|
||||
{[]string{"a", "", "b", "c", ""}, "abc"},
|
||||
{[]string{"你好", ",", "", "世界!", ""}, "你好,世界!"},
|
||||
{[]string{"Hello", " Wo", "r", "ld!", ""}, "Hello World!"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, Concat(0, tt.args...))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEllipsis(t *testing.T) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
var (
|
||||
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
|
||||
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||
alphaNumericMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
|
||||
numberRegexMatcher *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]*)?$`)
|
||||
@@ -181,6 +182,12 @@ func IsJSON(str string) bool {
|
||||
return json.Unmarshal([]byte(str), &js) == nil
|
||||
}
|
||||
|
||||
// IsAlphaNumeric check if the string is alphanumeric.
|
||||
// Play: todo
|
||||
func IsAlphaNumeric(s string) bool {
|
||||
return alphaNumericMatcher.MatchString(s)
|
||||
}
|
||||
|
||||
// IsNumberStr check if the string can convert to a number.
|
||||
// Play: https://go.dev/play/p/LzaKocSV79u
|
||||
func IsNumberStr(s string) bool {
|
||||
@@ -208,7 +215,7 @@ func IsIp(ipstr string) bool {
|
||||
}
|
||||
|
||||
// IsIpPort check if the string is ip:port.
|
||||
// Play:
|
||||
// Play: https://go.dev/play/p/xUmls_b9L29
|
||||
func IsIpPort(str string) bool {
|
||||
host, port, err := net.SplitHostPort(str)
|
||||
if err != nil {
|
||||
|
||||
@@ -665,3 +665,21 @@ func ExampleIsChinaUnionPay() {
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIsAlphaNumeric() {
|
||||
result1 := IsAlphaNumeric("ABC")
|
||||
result2 := IsAlphaNumeric("123")
|
||||
result3 := IsAlphaNumeric("abc123")
|
||||
result4 := IsAlphaNumeric("abc123@#$")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
@@ -15,16 +15,25 @@ func TestIsAllUpper(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsAllUpper")
|
||||
|
||||
assert.Equal(true, IsAllUpper("ABC"))
|
||||
assert.Equal(false, IsAllUpper(""))
|
||||
assert.Equal(false, IsAllUpper("abc"))
|
||||
assert.Equal(false, IsAllUpper("aBC"))
|
||||
assert.Equal(false, IsAllUpper("1BC"))
|
||||
assert.Equal(false, IsAllUpper("1bc"))
|
||||
assert.Equal(false, IsAllUpper("123"))
|
||||
assert.Equal(false, IsAllUpper("你好"))
|
||||
assert.Equal(false, IsAllUpper("A&"))
|
||||
assert.Equal(false, IsAllUpper("&@#$%^&*"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"", false},
|
||||
{"abc", false},
|
||||
{"aBC", false},
|
||||
{"1BC", false},
|
||||
{"1bc", false},
|
||||
{"123", false},
|
||||
{"你好", false},
|
||||
{"A&", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsAllUpper(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllLower(t *testing.T) {
|
||||
@@ -32,16 +41,25 @@ func TestIsAllLower(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsAllLower")
|
||||
|
||||
assert.Equal(true, IsAllLower("abc"))
|
||||
assert.Equal(false, IsAllLower("ABC"))
|
||||
assert.Equal(false, IsAllLower(""))
|
||||
assert.Equal(false, IsAllLower("aBC"))
|
||||
assert.Equal(false, IsAllLower("1BC"))
|
||||
assert.Equal(false, IsAllLower("1bc"))
|
||||
assert.Equal(false, IsAllLower("123"))
|
||||
assert.Equal(false, IsAllLower("你好"))
|
||||
assert.Equal(false, IsAllLower("A&"))
|
||||
assert.Equal(false, IsAllLower("&@#$%^&*"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"abc", true},
|
||||
{"", false},
|
||||
{"ABC", false},
|
||||
{"aBC", false},
|
||||
{"1BC", false},
|
||||
{"1bc", false},
|
||||
{"123", false},
|
||||
{"你好", false},
|
||||
{"A&", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsAllLower(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainLower(t *testing.T) {
|
||||
@@ -49,17 +67,25 @@ func TestContainLower(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestContainLower")
|
||||
|
||||
assert.Equal(true, ContainLower("abc"))
|
||||
assert.Equal(true, ContainLower("aBC"))
|
||||
assert.Equal(true, ContainLower("1bc"))
|
||||
assert.Equal(true, ContainLower("a&"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"abc", true},
|
||||
{"aBC", true},
|
||||
{"1bc", true},
|
||||
{"a&", true},
|
||||
{"ABC", false},
|
||||
{"", false},
|
||||
{"1BC", false},
|
||||
{"123", false},
|
||||
{"你好", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, ContainLower("ABC"))
|
||||
assert.Equal(false, ContainLower(""))
|
||||
assert.Equal(false, ContainLower("1BC"))
|
||||
assert.Equal(false, ContainLower("123"))
|
||||
assert.Equal(false, ContainLower("你好"))
|
||||
assert.Equal(false, ContainLower("&@#$%^&*"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, ContainLower(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainUpper(t *testing.T) {
|
||||
@@ -67,17 +93,25 @@ func TestContainUpper(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestContainUpper")
|
||||
|
||||
assert.Equal(true, ContainUpper("ABC"))
|
||||
assert.Equal(true, ContainUpper("aBC"))
|
||||
assert.Equal(true, ContainUpper("1BC"))
|
||||
assert.Equal(true, ContainUpper("A&"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"aBC", true},
|
||||
{"1BC", true},
|
||||
{"A&", true},
|
||||
{"abc", false},
|
||||
{"", false},
|
||||
{"1bc", false},
|
||||
{"123", false},
|
||||
{"你好", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, ContainUpper("abc"))
|
||||
assert.Equal(false, ContainUpper(""))
|
||||
assert.Equal(false, ContainUpper("1bc"))
|
||||
assert.Equal(false, ContainUpper("123"))
|
||||
assert.Equal(false, ContainUpper("你好"))
|
||||
assert.Equal(false, ContainUpper("&@#$%^&*"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, ContainUpper(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainLetter(t *testing.T) {
|
||||
@@ -85,15 +119,23 @@ func TestContainLetter(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestContainLetter")
|
||||
|
||||
assert.Equal(true, ContainLetter("ABC"))
|
||||
assert.Equal(true, ContainLetter("1Bc"))
|
||||
assert.Equal(true, ContainLetter("1ab"))
|
||||
assert.Equal(true, ContainLetter("A&"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"1Bc", true},
|
||||
{"1ab", true},
|
||||
{"A&", true},
|
||||
{"", false},
|
||||
{"123", false},
|
||||
{"你好", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, ContainLetter(""))
|
||||
assert.Equal(false, ContainLetter("123"))
|
||||
assert.Equal(false, ContainLetter("你好"))
|
||||
assert.Equal(false, ContainLetter("&@#$%^&*"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, ContainLetter(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainNumber(t *testing.T) {
|
||||
@@ -101,18 +143,26 @@ func TestContainNumber(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestContainNumber")
|
||||
|
||||
assert.Equal(true, ContainNumber("123"))
|
||||
assert.Equal(true, ContainNumber("1Bc"))
|
||||
assert.Equal(true, ContainNumber("a2c"))
|
||||
assert.Equal(true, ContainNumber("ab3"))
|
||||
assert.Equal(true, ContainNumber("a23"))
|
||||
assert.Equal(true, ContainNumber("a23c"))
|
||||
assert.Equal(true, ContainNumber("1%%%"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"123", true},
|
||||
{"1Bc", true},
|
||||
{"a2c", true},
|
||||
{"ab3", true},
|
||||
{"a23", true},
|
||||
{"a23c", true},
|
||||
{"1%%%", true},
|
||||
{"ABC", false},
|
||||
{"", false},
|
||||
{"你好", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, ContainNumber("ABC"))
|
||||
assert.Equal(false, ContainNumber(""))
|
||||
assert.Equal(false, ContainNumber("你好"))
|
||||
assert.Equal(false, ContainNumber("&@#$%^&*"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, ContainNumber(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsJSON(t *testing.T) {
|
||||
@@ -120,15 +170,23 @@ func TestIsJSON(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsJSON")
|
||||
|
||||
assert.Equal(true, IsJSON("{}"))
|
||||
assert.Equal(true, IsJSON("{\"name\": \"test\"}"))
|
||||
assert.Equal(true, IsJSON("[]"))
|
||||
assert.Equal(true, IsJSON("123"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"{}", true},
|
||||
{"{\"name\": \"test\"}", true},
|
||||
{"[]", true},
|
||||
{"123", true},
|
||||
{"", false},
|
||||
{"abc", false},
|
||||
{"你好", false},
|
||||
{"&@#$%^&*", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, IsJSON(""))
|
||||
assert.Equal(false, IsJSON("abc"))
|
||||
assert.Equal(false, IsJSON("你好"))
|
||||
assert.Equal(false, IsJSON("&@#$%^&*"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsJSON(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNumber(t *testing.T) {
|
||||
@@ -136,10 +194,19 @@ func TestIsNumber(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsNumber")
|
||||
|
||||
assert.Equal(false, IsNumber(""))
|
||||
assert.Equal(false, IsNumber("3"))
|
||||
assert.Equal(true, IsNumber(0))
|
||||
assert.Equal(true, IsNumber(0.1))
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
expected bool
|
||||
}{
|
||||
{"", false},
|
||||
{"3", false},
|
||||
{0, true},
|
||||
{0.1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsNumber(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFloat(t *testing.T) {
|
||||
@@ -147,10 +214,19 @@ func TestIsFloat(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsFloat")
|
||||
|
||||
assert.Equal(false, IsFloat(""))
|
||||
assert.Equal(false, IsFloat("3"))
|
||||
assert.Equal(false, IsFloat(0))
|
||||
assert.Equal(true, IsFloat(0.1))
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
expected bool
|
||||
}{
|
||||
{"", false},
|
||||
{"3", false},
|
||||
{0, false},
|
||||
{0.1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsFloat(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInt(t *testing.T) {
|
||||
@@ -158,11 +234,20 @@ func TestIsInt(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsInt")
|
||||
|
||||
assert.Equal(false, IsInt(""))
|
||||
assert.Equal(false, IsInt("3"))
|
||||
assert.Equal(false, IsInt(0.1))
|
||||
assert.Equal(true, IsInt(0))
|
||||
assert.Equal(true, IsInt(-1))
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
expected bool
|
||||
}{
|
||||
{"", false},
|
||||
{"3", false},
|
||||
{0.1, false},
|
||||
{0, true},
|
||||
{-1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsInt(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNumberStr(t *testing.T) {
|
||||
@@ -170,11 +255,21 @@ func TestIsNumberStr(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsNumberStr")
|
||||
|
||||
assert.Equal(true, IsNumberStr("3."))
|
||||
assert.Equal(true, IsNumberStr("+3."))
|
||||
assert.Equal(true, IsNumberStr("-3."))
|
||||
assert.Equal(true, IsNumberStr("+3e2"))
|
||||
assert.Equal(false, IsNumberStr("abc"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"3", true},
|
||||
{"3.", true},
|
||||
{"+3.", true},
|
||||
{"-3.", true},
|
||||
{"+3e2", true},
|
||||
{"abc", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsNumberStr(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFloatStr(t *testing.T) {
|
||||
@@ -182,11 +277,21 @@ func TestIsFloatStr(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsFloatStr")
|
||||
|
||||
assert.Equal(true, IsFloatStr("3."))
|
||||
assert.Equal(true, IsFloatStr("+3."))
|
||||
assert.Equal(true, IsFloatStr("-3."))
|
||||
assert.Equal(true, IsFloatStr("12"))
|
||||
assert.Equal(false, IsFloatStr("abc"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"3", true},
|
||||
{"3.", true},
|
||||
{"+3.", true},
|
||||
{"-3.", true},
|
||||
{"12", true},
|
||||
{"abc", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsFloatStr(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIntStr(t *testing.T) {
|
||||
@@ -194,10 +299,20 @@ func TestIsIntStr(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsIntStr")
|
||||
|
||||
assert.Equal(true, IsIntStr("+3"))
|
||||
assert.Equal(true, IsIntStr("-3"))
|
||||
assert.Equal(false, IsIntStr("3."))
|
||||
assert.Equal(false, IsIntStr("abc"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"3", true},
|
||||
{"3.", false},
|
||||
{"+3", true},
|
||||
{"-3", true},
|
||||
{"abc", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsIntStr(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPort(t *testing.T) {
|
||||
@@ -205,13 +320,22 @@ func TestIsPort(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsPort")
|
||||
|
||||
assert.Equal(true, IsPort("1"))
|
||||
assert.Equal(true, IsPort("65535"))
|
||||
assert.Equal(false, IsPort("abc"))
|
||||
assert.Equal(false, IsPort("123abc"))
|
||||
assert.Equal(false, IsPort(""))
|
||||
assert.Equal(false, IsPort("-1"))
|
||||
assert.Equal(false, IsPort("65536"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"1", true},
|
||||
{"65535", true},
|
||||
{"abc", false},
|
||||
{"123abc", false},
|
||||
{"", false},
|
||||
{"-1", false},
|
||||
{"65536", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsPort(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIp(t *testing.T) {
|
||||
@@ -219,10 +343,19 @@ func TestIsIp(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsIntStr")
|
||||
|
||||
assert.Equal(true, IsIp("127.0.0.1"))
|
||||
assert.Equal(true, IsIp("::0:0:0:0:0:0:1"))
|
||||
assert.Equal(false, IsIp("127.0.0"))
|
||||
assert.Equal(false, IsIp("127"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"127.0.0.1", true},
|
||||
{"::0:0:0:0:0:0:1", true},
|
||||
{"127.0.0", false},
|
||||
{"127", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsIp(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIpPort(t *testing.T) {
|
||||
@@ -230,16 +363,25 @@ func TestIsIpPort(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsIpPort")
|
||||
|
||||
assert.Equal(true, IsIpPort("[0:0:0:0:0:0:0:1]:8080")) // valid IPv6 and port
|
||||
assert.Equal(true, IsIpPort("127.0.0.1:8080")) // valid IP and port
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"[::0:0:0:0:0:0:1]:8080", true},
|
||||
{"127.0.0.1:8080", true},
|
||||
|
||||
assert.Equal(false, IsIpPort("")) // empty string
|
||||
assert.Equal(false, IsIpPort(":8080")) // only port
|
||||
assert.Equal(false, IsIpPort("127.0.0.1")) // valid IP without port
|
||||
assert.Equal(false, IsIpPort("0:0:0:0:0:0:0:1")) // valid IPv6 without port
|
||||
assert.Equal(false, IsIpPort("256.256.256.256:8080")) // invalid IP with valid port
|
||||
assert.Equal(false, IsIpPort("256.256.256.256:abc")) // invalid IP and invalid port
|
||||
assert.Equal(false, IsIpPort("127.0.0.1:70000")) // valid IP with invalid port
|
||||
{"", false},
|
||||
{":8080", false},
|
||||
{"127.0.0.1", false},
|
||||
{"0:0:0:0:0:0:0:1", false},
|
||||
{"256.256.256.256:8080", false},
|
||||
{"256.256.256.256:abc", false},
|
||||
{"127.0.0.1:70000", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsIpPort(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIpV4(t *testing.T) {
|
||||
@@ -247,13 +389,23 @@ func TestIsIpV4(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsIpV4")
|
||||
|
||||
assert.Equal(true, IsIpV4("127.0.0.1"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"127.0.0.1", true},
|
||||
{"::0:0:0:0:0:0:1", false},
|
||||
|
||||
assert.Equal(false, IsIpV4("::0:0:0:0:0:0:1"))
|
||||
assert.Equal(false, IsIpV4("127.0.0.1.1"))
|
||||
assert.Equal(false, IsIpV4("256.0.0.1"))
|
||||
assert.Equal(false, IsIpV4("127.0.0.a"))
|
||||
assert.Equal(false, IsIpV4(""))
|
||||
{"::0:0:0:0:0:0:1", false},
|
||||
{"127.0.0.1.1", false},
|
||||
{"256.0.0.1", false},
|
||||
{"127.0.0.a", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsIpV4(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIpV6(t *testing.T) {
|
||||
@@ -261,14 +413,23 @@ func TestIsIpV6(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsIpV6")
|
||||
|
||||
assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1"))
|
||||
assert.Equal(true, IsIpV6("::1"))
|
||||
assert.Equal(true, IsIpV6("::"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"::0:0:0:0:0:0:1", true},
|
||||
{"::1", true},
|
||||
{"::", true},
|
||||
{"127.0.0.1", false},
|
||||
{"2001:db8::8a2e:37023:7334", false},
|
||||
{"2001::25de::cade", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsIpV6(tt.input))
|
||||
}
|
||||
|
||||
assert.Equal(false, IsIpV6("127.0.0.1"))
|
||||
assert.Equal(false, IsIpV6("2001:db8::8a2e:37023:7334"))
|
||||
assert.Equal(false, IsIpV6("2001::25de::cade"))
|
||||
assert.Equal(false, IsIpV6(""))
|
||||
}
|
||||
|
||||
func TestIsUrl(t *testing.T) {
|
||||
@@ -287,13 +448,21 @@ func TestIsDns(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsDns")
|
||||
|
||||
assert.Equal(true, IsDns("abc.com"))
|
||||
assert.Equal(true, IsDns("123.cn"))
|
||||
assert.Equal(true, IsDns("a.b.com"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"abc.com", true},
|
||||
{"123.cn", true},
|
||||
{"a.b.com", true},
|
||||
{"a.b.c", false},
|
||||
{"a@b.com", false},
|
||||
{"http://abc.com", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, IsDns("a.b.c"))
|
||||
assert.Equal(false, IsDns("a@b.com"))
|
||||
assert.Equal(false, IsDns("http://abc.com"))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsDns(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmail(t *testing.T) {
|
||||
@@ -330,11 +499,19 @@ func TestIsChinesePhone(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsChinesePhone")
|
||||
|
||||
assert.Equal(true, IsChinesePhone("010-32116675"))
|
||||
assert.Equal(true, IsChinesePhone("0464-8756213"))
|
||||
assert.Equal(true, IsChinesePhone("0731-82251545")) // 长沙晚报电话
|
||||
assert.Equal(false, IsChinesePhone("123-87562"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"010-32116675", true},
|
||||
{"0464-8756213", true},
|
||||
{"0731-82251545", true},
|
||||
{"123-87562", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsChinesePhone(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsChineseIdNum(t *testing.T) {
|
||||
@@ -342,13 +519,23 @@ func TestIsChineseIdNum(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsChineseIdNum")
|
||||
|
||||
assert.Equal(true, IsChineseIdNum("210911192105130714"))
|
||||
assert.Equal(true, IsChineseIdNum("11010519491231002X"))
|
||||
assert.Equal(true, IsChineseIdNum("11010519491231002x"))
|
||||
assert.Equal(false, IsChineseIdNum("123456"))
|
||||
assert.Equal(false, IsChineseIdNum("990911192105130714"))
|
||||
assert.Equal(false, IsChineseIdNum("990911189905130714"))
|
||||
assert.Equal(false, IsChineseIdNum("210911222205130714"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"210911192105130714", true},
|
||||
{"11010519491231002X", true},
|
||||
{"11010519491231002x", true},
|
||||
{"123456", false},
|
||||
{"990911192105130714", false},
|
||||
{"990911189905130714", false},
|
||||
{"210911222205130714", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsChineseIdNum(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCreditCard(t *testing.T) {
|
||||
@@ -407,13 +594,23 @@ func TestIsStrongPassword(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsStrongPassword")
|
||||
|
||||
assert.Equal(false, IsStrongPassword("abc", 3))
|
||||
assert.Equal(false, IsStrongPassword("abc123", 6))
|
||||
assert.Equal(false, IsStrongPassword("abcABC", 6))
|
||||
assert.Equal(false, IsStrongPassword("abc123@#$", 9))
|
||||
assert.Equal(false, IsStrongPassword("abcABC123@#$", 16))
|
||||
assert.Equal(true, IsStrongPassword("abcABC123@#$", 12))
|
||||
assert.Equal(true, IsStrongPassword("abcABC123@#$", 10))
|
||||
tests := []struct {
|
||||
input string
|
||||
length int
|
||||
expected bool
|
||||
}{
|
||||
{"abc", 3, false},
|
||||
{"abc123", 6, false},
|
||||
{"abcABC", 6, false},
|
||||
{"abc123@#$", 9, false},
|
||||
{"abcABC123@#$", 16, false},
|
||||
{"abcABC123@#$", 12, true},
|
||||
{"abcABC123@#$", 10, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsStrongPassword(tt.input, tt.length))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsWeakPassword(t *testing.T) {
|
||||
@@ -421,11 +618,20 @@ func TestIsWeakPassword(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsWeakPassword")
|
||||
|
||||
assert.Equal(true, IsWeakPassword("abc"))
|
||||
assert.Equal(true, IsWeakPassword("123"))
|
||||
assert.Equal(true, IsWeakPassword("abc123"))
|
||||
assert.Equal(true, IsWeakPassword("abcABC123"))
|
||||
assert.Equal(false, IsWeakPassword("abc123@#$"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"abc", true},
|
||||
{"123", true},
|
||||
{"abc123", true},
|
||||
{"abcABC123", true},
|
||||
{"abc123@#$", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsWeakPassword(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsZeroValue(t *testing.T) {
|
||||
@@ -566,50 +772,83 @@ func TestIsPrintable(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsPrintable")
|
||||
|
||||
assert.Equal(true, IsPrintable("ABC"))
|
||||
assert.Equal(true, IsPrintable("{id: 123}"))
|
||||
assert.Equal(true, IsPrintable(""))
|
||||
assert.Equal(true, IsPrintable("😄"))
|
||||
assert.Equal(false, IsPrintable("\u0000"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"123", true},
|
||||
{"你好", true},
|
||||
{"", true},
|
||||
{"😄", true},
|
||||
{"\u0000", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsPrintable(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBin(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestIsBin")
|
||||
|
||||
assert.Equal(true, IsBin("0101"))
|
||||
assert.Equal(true, IsBin("0b1101"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"0101", true},
|
||||
{"0b1101", true},
|
||||
{"b1101", false},
|
||||
{"1201", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, IsBin("b1101"))
|
||||
assert.Equal(false, IsBin("1201"))
|
||||
assert.Equal(false, IsBin(""))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsBin(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsHex(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestIsHex")
|
||||
|
||||
assert.Equal(true, IsHex("ABCDE"))
|
||||
assert.Equal(true, IsHex("abcde"))
|
||||
assert.Equal(true, IsHex("0xabcde"))
|
||||
assert.Equal(true, IsHex("0Xabcde"))
|
||||
assert.Equal(true, IsHex("#abcde"))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABCDE", true},
|
||||
{"abcde", true},
|
||||
{"0xabcde", true},
|
||||
{"0Xabcde", true},
|
||||
{"#abcde", true},
|
||||
{"cdfeg", false},
|
||||
{"0xcdfeg", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, IsHex("cdfeg"))
|
||||
assert.Equal(false, IsHex("0xcdfeg"))
|
||||
assert.Equal(false, IsHex(""))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsHex(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBase64URL(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestIsBase64URL")
|
||||
|
||||
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ"))
|
||||
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ=="))
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"SAGsbG8sIHdvcmxkIQ", true},
|
||||
{"SAGsbG8sIHdvcmxkIQ==", true},
|
||||
{"SAGsbG8sIHdvcmxkIQ=", false},
|
||||
{"SAGsbG8sIHdvcmxkIQ===", false},
|
||||
}
|
||||
|
||||
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ="))
|
||||
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ==="))
|
||||
// assert.Equal(false, IsBase64URL(""))
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsBase64URL(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsJWT(t *testing.T) {
|
||||
@@ -659,3 +898,29 @@ func TestIsChinaUnionPay(t *testing.T) {
|
||||
assert.Equal(true, IsChinaUnionPay("6250941006528599"))
|
||||
assert.Equal(false, IsChinaUnionPay("3782822463100007"))
|
||||
}
|
||||
|
||||
func TestIsAlphaNumeric(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsAlphaNumeric")
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"abc", true},
|
||||
{"aBC", true},
|
||||
{"1BC", true},
|
||||
{"1bc", true},
|
||||
{"123", true},
|
||||
{"你好", false},
|
||||
{"A&", false},
|
||||
{"&@#$%^&*", false},
|
||||
{"", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(tt.expected, IsAlphaNumeric(tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user