1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-03-01 00:35:28 +08:00

Compare commits

...

39 Commits

Author SHA1 Message Date
dudaodong
e74126a0bd test: format test code 2025-03-27 11:17:07 +08:00
dudaodong
f7be4190f1 test: format test code 2025-03-27 10:49:40 +08:00
dudaodong
8089b71bfd feat: add IsAlphaNumeric 2025-03-15 14:57:21 +08:00
dudaodong
5b9543255a feat: add AddQueryParams 2025-03-15 14:52:49 +08:00
dudaodong
bf859387f4 feat: add BuildUrl 2025-03-15 14:25:03 +08:00
dudaodong
a8a92844f3 doc: update doc for website 2025-03-07 14:37:52 +08:00
dudaodong
6dfdadd34e doc: update for release v2.3.5 2025-03-07 14:19:07 +08:00
dudaodong
8120c4db78 test: fix TestEventBus 2025-03-07 10:42:22 +08:00
dudaodong
0d0f213d36 test: fix TestEventBus 2025-03-07 10:37:41 +08:00
dudaodong
41d4bbf0e3 test: fix TestEventBus_GetEvents 2025-03-07 10:34:20 +08:00
dudaodong
fed1d5220e release v2.3.4 2025-03-07 10:29:19 +08:00
dudaodong
29a8318d6e doc: update for release v2.3.5 2025-03-06 20:11:21 +08:00
dudaodong
3696213e5d doc: update for release v2.3.5 2025-03-06 20:02:33 +08:00
dudaodong
90a3b87b67 doc: add doc for eventbus package 2025-03-06 15:50:12 +08:00
dudaodong
a7b28ee864 update crypto fileg 2025-02-18 15:49:52 +08:00
dudaodong
f2823014f2 doc: add doc for AddDaySafe, AddMonthSafe, AddYearSafe 2025-02-18 15:47:43 +08:00
dudaodong
4181c42805 feat: add AddDaySafe, AddMonthSafe, AddYearSafe 2025-02-18 15:37:56 +08:00
dudaodong
a09f5623d6 feat: add AddWeek, AddMonth and update example of some add time functions 2025-02-18 14:10:04 +08:00
chentong
a9c75b081d feat(validator): add IsIpPort (#294)
* fix: json tag omitempty convert error

* feat(validator): add IsIpPort

- Add IsIpPort function to check if a string is in the format ip:port
- Update validator_test.go to include tests for the new IsIpPort function
- Add an example test for IsIpPort in validator_example_test.go
2025-02-17 10:03:15 +08:00
dudaodong
db479ef1bc fix: fix issue #292 2025-02-14 17:35:48 +08:00
dudaodong
df8121fbbd Merge branch 'rc' into v2 2025-02-14 17:13:11 +08:00
dudaodong
0e7297cb97 feat: add ShuffleCopy 2025-02-14 16:33:22 +08:00
dudaodong
2e619e48a3 feat: add ReverseCopy 2025-02-14 16:23:46 +08:00
残念
3069acba4a Merge pull request #293 from YoghurtFree/fix_slice_test
fix(slice_test) , should not assume the order of the slice
2025-02-09 23:10:28 +08:00
jialulu
fc43138a0e fix: fix slice_test.go ,We should not assume the order of the slice when using multithreads,sort them before compare 2025-02-09 22:23:35 +08:00
残念
1e56e9964c Merge pull request #291 from guanhonly/feature/compatible-with-pointer-in-ToString
compatible with pointer in convert.ToString
2025-01-30 22:26:34 +08:00
guanhongli
f861e18bc3 compatible with pointer in convert.ToString 2025-01-26 12:04:55 +08:00
Tuuuuuuuuu
e27df00fa8 make Bridge not block in the first stream that not closed (#288)
* not block in the first channel

* make Bridge not block in the first stream that not closed

* Bridge with test
2025-01-14 16:19:45 +08:00
dudaodong
23e61f1acf update pem file 2025-01-14 15:58:58 +08:00
dudaodong
cb308f628c feat: add eventbus 2025-01-14 15:57:43 +08:00
dudaodong
7653afa919 feat: add FindAllOccurrences 2025-01-10 14:03:45 +08:00
dudaodong
a0d97cf38e Merge branch 'rc' into v2 2024-12-25 16:13:04 +08:00
残念
1f6bab467c Merge pull request #282 from Yurunsoft/fix-RandFloats
fix the bug of random.RandFloats() infinite loop and the result of random.RandFloat() sometimes equals to max.
2024-12-25 15:14:17 +08:00
Yurun
2e5b9bc200 fix: fix the bug of random.RandFloats() infinite loop and the result of random.RandFloat() sometimes equals to max. 2024-12-25 14:13:11 +08:00
dudaodong
ab89f0aee1 feat: add EqualUnordered 2024-12-19 19:36:17 +08:00
tangke
1f64e02df4 fix(fileutil): unzip Chinese garbled code during decompression (#279)
解压时出现中文乱码
2024-12-19 19:06:27 +08:00
dudaodong
d4b425e39c doc: fix doc error 2024-12-09 09:52:40 +08:00
dudaodong
a1652c7523 doc: update v1 latest version 2024-12-04 19:13:23 +08:00
dudaodong
ecafed511c doc: add play ground demo for v2.3.4 2024-12-04 14:25:51 +08:00
63 changed files with 4336 additions and 649 deletions

114
README.md
View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.4-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.3.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml) [![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -39,7 +39,7 @@
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
``` ```
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.5. </b> 2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.6. </b>
```go ```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
@@ -333,7 +333,7 @@ import "github.com/duke-git/lancet/v2/convertor"
[[play](https://go.dev/play/p/HwdDPFcza1O)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToBigInt)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/X3itkCxwB_x)]
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -469,10 +469,10 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/zutRHrDqs0X)] [[play](https://go.dev/play/p/zutRHrDqs0X)]
- **<big>RsaEncrypt</big>** : encrypt data with ras algorithm. - **<big>RsaEncrypt</big>** : encrypt data with ras algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaEncrypt)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaEncrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)] [[play](https://go.dev/play/p/7_zo6mrx-eX)]
- **<big>RsaDecrypt</big>** : decrypt data with ras algorithm. - **<big>RsaDecrypt</big>** : decrypt data with ras algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaDecrypt)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaDecrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)] [[play](https://go.dev/play/p/7_zo6mrx-eX)]
- **<big>GenerateRsaKeyPair</big>** : creates rsa private and public key. - **<big>GenerateRsaKeyPair</big>** : creates rsa private and public key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateRsaKeyPair)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)] [[play](https://go.dev/play/p/sSVmkfENKMz)]
@@ -484,10 +484,10 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/sSVmkfENKMz)] [[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaSign</big>** : signs the data with RSA. - **<big>RsaSign</big>** : signs the data with RSA.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaSign)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaSign)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
- **<big>RsaVerifySign</big>** : verifies the signature of the data with RSA. - **<big>RsaVerifySign</big>** : verifies the signature of the data with RSA.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaVerifySign)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaVerifySign)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
<h3 id="datetime"> 7. Datetime package supports date and time format and compare. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="datetime"> 7. Datetime package supports date and time format and compare. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -506,9 +506,24 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>AddMinute</big>** : add or sub day to the time. - **<big>AddMinute</big>** : add or sub day to the time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddMinute)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddMinute)]
[[play](https://go.dev/play/p/nT1heB1KUUK)] [[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/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/DLoiOnpLvsN)]
- **<big>AddYear</big>** : add or sub year to the time. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddYear)]
[[play](https://go.dev/play/p/MqW2ujnBx10)] [[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/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/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/KVGXWZZ54ZH)]
- **<big>BeginOfMinute</big>** : return the date time at the begin of minute of specific date. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfMinute)]
[[play](https://go.dev/play/p/ieOLVJ9CiFT)] [[play](https://go.dev/play/p/ieOLVJ9CiFT)]
@@ -633,13 +648,13 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/6kHBpAxD9ZC)] [[play](https://go.dev/play/p/6kHBpAxD9ZC)]
- **<big>Min</big>** : returns the earliest time among the given times. - **<big>Min</big>** : returns the earliest time among the given times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#Min)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#Min)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/MCIDvHNOGGb)]
- **<big>Max</big>** : returns the latest time among the given times. - **<big>Max</big>** : returns the latest time among the given times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#Max)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#Max)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/9m6JMk1LB7-)]
- **<big>MaxMin</big>** : returns the latest and earliest time among the given times. - **<big>MaxMin</big>** : returns the latest and earliest time among the given times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#MaxMin)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#MaxMin)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/rbW51cDtM_2)]
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -680,6 +695,45 @@ import optional "github.com/duke-git/lancet/v2/datastructure/optional"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)] [[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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">Index</a></h3>
```go
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/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/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/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/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/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/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/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/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/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/gmB0gnFe5mc)]
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go ```go
@@ -777,7 +831,7 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[play](https://go.dev/play/p/teMXnCsdSEw)] [[play](https://go.dev/play/p/teMXnCsdSEw)]
- **<big>GetExeOrDllVersion</big>** : Get the version of exe or dll file on windows os. - **<big>GetExeOrDllVersion</big>** : Get the version of exe or dll file on windows os.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#GetExeOrDllVersion)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#GetExeOrDllVersion)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/iLRrDBhE38E)]
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="formatter"> 10. Formatter contains some functions for data formatting. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1145,16 +1199,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[play](https://go.dev/play/p/WLxDdGXXYat)] [[play](https://go.dev/play/p/WLxDdGXXYat)]
- **<big>Variance</big>** : returns the variance of numbers. - **<big>Variance</big>** : returns the variance of numbers.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Variance)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Variance)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/uHuV4YgXf8F)]
- **<big>StdDev</big>** : returns the standard deviation of numbers. - **<big>StdDev</big>** : returns the standard deviation of numbers.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#StdDev)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#StdDev)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/FkNZDXvHD2l)]
- **<big>Permutation</big>** : calculates P(n, k). - **<big>Permutation</big>** : calculates P(n, k).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Permutation)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Permutation)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/MgobwH_FOxj)]
- **<big>Combination</big>** : calculates C(n, k). - **<big>Combination</big>** : calculates C(n, k).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Combination)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Combination)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/ENFQRDQUFi9)]
<h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1317,7 +1371,7 @@ import "github.com/duke-git/lancet/v2/random"
[[play](https://go.dev/play/p/68UikN9d6VT)] [[play](https://go.dev/play/p/68UikN9d6VT)]
- **<big>RandNumberOfLength</big>** : generates a random int number of specified length. - **<big>RandNumberOfLength</big>** : generates a random int number of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandNumberOfLength)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandNumberOfLength)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/oyZbuV7bu7b)]
<h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1423,6 +1477,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>EqualWith</big>** : checks if two slices are equal with comparator func. - **<big>EqualWith</big>** : checks if two slices are equal with comparator func.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#EqualWith)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#EqualWith)]
[[play](https://go.dev/play/p/b9iygtgsHI1)] [[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](https://go.dev/play/p/n8fSc2w8ZgX)]
- **<big>Every</big>** : return true if all of the values in the slice pass the predicate function. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Every)]
[[play](https://go.dev/play/p/R8U6Sl-j8cD)] [[play](https://go.dev/play/p/R8U6Sl-j8cD)]
@@ -1522,6 +1579,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Shuffle</big>** : shuffle the slice. - **<big>Shuffle</big>** : shuffle the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Shuffle)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Shuffle)]
[[play](https://go.dev/play/p/YHvhnWGU3Ge)] [[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](https://go.dev/play/p/vqDa-Gs1vT0)]
- **<big>IsAscending</big>** : Checks if a slice is ascending order. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsAscending)]
[[play](https://go.dev/play/p/9CtsFjet4SH)] [[play](https://go.dev/play/p/9CtsFjet4SH)]
@@ -1613,10 +1673,10 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/CW3UVNdUZOq)] [[play](https://go.dev/play/p/CW3UVNdUZOq)]
- **<big>JoinFunc</big>** : joins the slice elements into a single string with the given separator. - **<big>JoinFunc</big>** : joins the slice elements into a single string with the given separator.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#JoinFunc)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#JoinFunc)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/55ib3SB5fM2)]
- **<big>ConcatBy</big>** : concats the elements of a slice into a single value using the provided separator and connector function. - **<big>ConcatBy</big>** : concats the elements of a slice into a single value using the provided separator and connector function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ConcatBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ConcatBy)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/6QcUpcY4UMW)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1668,6 +1728,9 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Reverse</big>** : returns a stream whose elements are reverse order of given stream. - **<big>Reverse</big>** : returns a stream whose elements are reverse order of given stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Reverse)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Reverse)]
[[play](https://go.dev/play/p/A8_zkJnLHm4)] [[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](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. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Range)]
[[play](https://go.dev/play/p/indZY5V2f4j)] [[play](https://go.dev/play/p/indZY5V2f4j)]
@@ -1709,10 +1772,10 @@ import "github.com/duke-git/lancet/v2/stream"
[[play](https://go.dev/play/p/jI6_iZZuVFE)] [[play](https://go.dev/play/p/jI6_iZZuVFE)]
- **<big>IndexOf</big>** : returns the index of the first occurrence of the specified element in this stream. - **<big>IndexOf</big>** : returns the index of the first occurrence of the specified element in this stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#IndexOf)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#IndexOf)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/tBV5Nc-XDX2)]
- **<big>LastIndexOf</big>** : returns the index of the last occurrence of the specified element in this stream. - **<big>LastIndexOf</big>** : returns the index of the last occurrence of the specified element in this stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#LastIndexOf)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#LastIndexOf)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/CjeoNw2eac_G)]
<h3 id="structs"> 20. Structs package provides several high level functions to manipulate struct, tag, and field. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="structs"> 20. Structs package provides several high level functions to manipulate struct, tag, and field. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1896,8 +1959,10 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/JZiu0RXpgN-)] [[play](https://go.dev/play/p/JZiu0RXpgN-)]
- **<big>ExtractContent</big>** : extracts the content between the start and end strings in the source string. - **<big>ExtractContent</big>** : extracts the content between the start and end strings in the source string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#ExtractContent)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#ExtractContent)]
[[play](https://go.dev/play/p/todo)] [[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](https://go.dev/play/p/uvyA6azGLB1)]
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -2151,6 +2216,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsIpV6</big>** : check if the string is ipv6. - **<big>IsIpV6</big>** : check if the string is ipv6.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIpV6)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIpV6)]
[[play](https://go.dev/play/p/AHA0r0AzIdC)] [[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](https://go.dev/play/p/xUmls_b9L29)]
- **<big>IsStrongPassword</big>** : check if the string is strong password. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsStrongPassword)]
[[play](https://go.dev/play/p/QHdVcSQ3uDg)] [[play](https://go.dev/play/p/QHdVcSQ3uDg)]
@@ -2249,7 +2317,7 @@ import "github.com/duke-git/lancet/v2/xerror"
[[play](https://go.dev/play/p/acyZVkNZEeW)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#TryCatch)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/D5Mdb0mRj0P)]
## How to Contribute ## How to Contribute

View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.4-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.3.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml) [![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -38,7 +38,7 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
``` ```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.5。</b> 2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。</b>
```go ```go
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本 go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
@@ -333,7 +333,7 @@ import "github.com/duke-git/lancet/v2/convertor"
[[play](https://go.dev/play/p/HwdDPFcza1O)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToBigInt)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/X3itkCxwB_x)]
<h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -470,10 +470,10 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/zutRHrDqs0X)] [[play](https://go.dev/play/p/zutRHrDqs0X)]
- **<big>RsaEncrypt</big>** : 用公钥文件 ras 加密数据。 - **<big>RsaEncrypt</big>** : 用公钥文件 ras 加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncrypt)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)] [[play](https://go.dev/play/p/7_zo6mrx-eX)]
- **<big>RsaDecrypt</big>** : 用私钥文件 rsa 解密数据。 - **<big>RsaDecrypt</big>** : 用私钥文件 rsa 解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecrypt)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)] [[play](https://go.dev/play/p/sSVmkfENKMz)]
@@ -485,10 +485,10 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/sSVmkfENKMz)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaSign)]
[[play](https://go.dev/play/p/todo)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -507,9 +507,24 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>AddMinute</big>** : 将日期加/减分钟数。 - **<big>AddMinute</big>** : 将日期加/减分钟数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMinute)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMinute)]
[[play](https://go.dev/play/p/nT1heB1KUUK)] [[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/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/DLoiOnpLvsN)]
- **<big>AddYear</big>** : 将日期加/减分年数。 - **<big>AddYear</big>** : 将日期加/减分年数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYear)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYear)]
[[play](https://go.dev/play/p/MqW2ujnBx10)] [[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/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/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/KVGXWZZ54ZH)]
- **<big>BeginOfMinute</big>** : 返回指定时间的分钟开始时间。 - **<big>BeginOfMinute</big>** : 返回指定时间的分钟开始时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMinute)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMinute)]
[[play](https://go.dev/play/p/ieOLVJ9CiFT)] [[play](https://go.dev/play/p/ieOLVJ9CiFT)]
@@ -635,13 +650,13 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/6kHBpAxD9ZC)] [[play](https://go.dev/play/p/6kHBpAxD9ZC)]
- **<big>Min</big>** : 返回最早时间。 - **<big>Min</big>** : 返回最早时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Min)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Min)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/MCIDvHNOGGb)]
- **<big>Max</big>** : 返回最晚时间。 - **<big>Max</big>** : 返回最晚时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Max)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Max)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/9m6JMk1LB7-)]
- **<big>MaxMin</big>** : 返回最早和最晚时间。 - **<big>MaxMin</big>** : 返回最早和最晚时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#MaxMin)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#MaxMin)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/rbW51cDtM_2)]
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -679,7 +694,46 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
- **<big>Hashmap</big>** : 哈希映射。 - **<big>Hashmap</big>** : 哈希映射。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/hashmap.md)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/hashmap.md)]
<h3 id="fileutil"> 9. fileutil 包含文件基本操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="eventbus"> 9. EventbBus是一个事件总线用于在应用程序中处理事件。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/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/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/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/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/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/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/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/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/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/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/gmB0gnFe5mc)]
<h3 id="fileutil"> 10. fileutil 包含文件基本操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/fileutil" import "github.com/duke-git/lancet/v2/fileutil"
@@ -776,10 +830,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[play](https://go.dev/play/p/teMXnCsdSEw)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#GetExeOrDllVersion)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/iLRrDBhE38E)]
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="formatter"> 11. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/formatter" import "github.com/duke-git/lancet/v2/formatter"
@@ -809,7 +863,7 @@ import "github.com/duke-git/lancet/v2/formatter"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#ParseBinaryBytes)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#ParseBinaryBytes)]
[[play](https://go.dev/play/p/69v1tTT62x8)] [[play](https://go.dev/play/p/69v1tTT62x8)]
<h3 id="function"> 11. function 函数包控制函数执行流程,包含部分函数式编程。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="function"> 12. function 函数包控制函数执行流程,包含部分函数式编程。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/function" import "github.com/duke-git/lancet/v2/function"
@@ -874,7 +928,7 @@ import "github.com/duke-git/lancet/v2/function"
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="maputil"> 13. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/maputil" import "github.com/duke-git/lancet/v2/maputil"
@@ -1045,7 +1099,7 @@ import "github.com/duke-git/lancet/v2/maputil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
[[play](https://go.dev/play/p/99QjSYSBdiM)] [[play](https://go.dev/play/p/99QjSYSBdiM)]
<h3 id="mathutil"> 13. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="mathutil"> 14. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/mathutil" import "github.com/duke-git/lancet/v2/mathutil"
@@ -1145,18 +1199,18 @@ import "github.com/duke-git/lancet/v2/mathutil"
[[play](https://go.dev/play/p/WLxDdGXXYat)] [[play](https://go.dev/play/p/WLxDdGXXYat)]
- **<big>Variance</big>** : 计算方差。 - **<big>Variance</big>** : 计算方差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Variance)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Variance)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/uHuV4YgXf8F)]
- **<big>StdDev</big>** : 计算标准差。 - **<big>StdDev</big>** : 计算标准差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#StdDev)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#StdDev)]
[[play](https://go.dev/play/p/todo)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Permutation)]
[[play](https://go.dev/play/p/todo)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Combination)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/ENFQRDQUFi9)]
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="netutil"> 15. netutil 网络包支持获取 ip 地址,发送 http 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/netutil" import "github.com/duke-git/lancet/v2/netutil"
@@ -1229,7 +1283,7 @@ import "github.com/duke-git/lancet/v2/netutil"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsTelnetConnected)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsTelnetConnected)]
[[play](https://go.dev/play/p/yiLCGtQv_ZG)] [[play](https://go.dev/play/p/yiLCGtQv_ZG)]
<h3 id="pointer"> 15. pointer 包支持一些指针类型的操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="pointer"> 16. pointer 包支持一些指针类型的操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/pointer" import "github.com/duke-git/lancet/v2/pointer"
@@ -1253,7 +1307,7 @@ import "github.com/duke-git/lancet/v2/pointer"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#UnwrapOrDefault)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#UnwrapOrDefault)]
[[play](https://go.dev/play/p/ZnGIHf8_o4E)] [[play](https://go.dev/play/p/ZnGIHf8_o4E)]
<h3 id="random"> 16. random 随机数生成器包,可以生成随机[]bytes, int, string。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="random"> 17. random 随机数生成器包,可以生成随机[]bytes, int, string。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/random" import "github.com/duke-git/lancet/v2/random"
@@ -1317,9 +1371,9 @@ import "github.com/duke-git/lancet/v2/random"
[[play](https://go.dev/play/p/68UikN9d6VT)] [[play](https://go.dev/play/p/68UikN9d6VT)]
- **<big>RandNumberOfLength</big>** : 生成指定长度的随机数。 - **<big>RandNumberOfLength</big>** : 生成指定长度的随机数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumberOfLength)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumberOfLength)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/oyZbuV7bu7b)]
<h3 id="retry"> 17. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="retry"> 18. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/retry" import "github.com/duke-git/lancet/v2/retry"
@@ -1353,7 +1407,7 @@ import "github.com/duke-git/lancet/v2/retry"
<h3 id="slice"> 18. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3> <h3 id="slice"> 19. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/slice" import "github.com/duke-git/lancet/v2/slice"
@@ -1421,6 +1475,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>EqualWith</big>** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数 comparator返回 true。 - **<big>EqualWith</big>** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数 comparator返回 true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualWith)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualWith)]
[[play](https://go.dev/play/p/b9iygtgsHI1)] [[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](https://go.dev/play/p/n8fSc2w8ZgX)]
- **<big>Every</big>** : 如果切片中的所有值都通过谓词函数,则返回 true。 - **<big>Every</big>** : 如果切片中的所有值都通过谓词函数,则返回 true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Every)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Every)]
[[play](https://go.dev/play/p/R8U6Sl-j8cD)] [[play](https://go.dev/play/p/R8U6Sl-j8cD)]
@@ -1496,6 +1553,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Reverse</big>** : 反转切片中的元素顺序。 - **<big>Reverse</big>** : 反转切片中的元素顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reverse)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reverse)]
[[play](https://go.dev/play/p/8uI8f1lwNrQ)] [[play](https://go.dev/play/p/8uI8f1lwNrQ)]
- **<big>ReverseCopy</big>** : 反转切片中的元素顺序, 不改变原slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ReverseCopy)]
[[play](https://go.dev/play/p/c9arEaP7Cg-)]
- **<big>Reduce<sup>deprecated</sup></big>** : 将切片中的元素依次运行 iteratee 函数,返回运行结果。(废弃:建议使用 ReduceBy) - **<big>Reduce<sup>deprecated</sup></big>** : 将切片中的元素依次运行 iteratee 函数,返回运行结果。(废弃:建议使用 ReduceBy)
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reduce)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reduce)]
[[play](https://go.dev/play/p/_RfXJJWIsIm)] [[play](https://go.dev/play/p/_RfXJJWIsIm)]
@@ -1520,6 +1580,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Shuffle</big>** : 随机打乱切片中的元素顺序。 - **<big>Shuffle</big>** : 随机打乱切片中的元素顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Shuffle)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Shuffle)]
[[play](https://go.dev/play/p/YHvhnWGU3Ge)] [[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](https://go.dev/play/p/vqDa-Gs1vT0)]
- **<big>IsAscending</big>** : 检查切片元素是否按升序排列。 - **<big>IsAscending</big>** : 检查切片元素是否按升序排列。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsAscending)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsAscending)]
[[play](https://go.dev/play/p/9CtsFjet4SH)] [[play](https://go.dev/play/p/9CtsFjet4SH)]
@@ -1610,12 +1673,12 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/CW3UVNdUZOq)] [[play](https://go.dev/play/p/CW3UVNdUZOq)]
- **<big>JoinFunc</big>** : 将切片元素用给定的分隔符连接成一个单一的字符串。 - **<big>JoinFunc</big>** : 将切片元素用给定的分隔符连接成一个单一的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#JoinFunc)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#JoinFunc)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/55ib3SB5fM2)]
- **<big>ConcatBy</big>** : 将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。 - **<big>ConcatBy</big>** : 将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ConcatBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ConcatBy)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/6QcUpcY4UMW)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="stream"> 20. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/stream" import "github.com/duke-git/lancet/v2/stream"
@@ -1706,12 +1769,12 @@ import "github.com/duke-git/lancet/v2/stream"
[[play](https://go.dev/play/p/jI6_iZZuVFE)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#IndexOf)]
[[play](https://go.dev/play/p/todo)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#LastIndexOf)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/CjeoNw2eac_G)]
<h3 id="structs"> 20. structs 提供操作 struct, tag, field 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="structs"> 21. structs 提供操作 struct, tag, field 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/structs" import "github.com/duke-git/lancet/v2/structs"
@@ -1748,7 +1811,7 @@ import "github.com/duke-git/lancet/v2/structs"
- **<big>IsTargetType</big>** : 判断属性是否是目标类型。 - **<big>IsTargetType</big>** : 判断属性是否是目标类型。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsTargetType)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsTargetType)]
<h3 id="strutil"> 21. strutil 包含字符串处理的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="strutil"> 22. strutil 包含字符串处理的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/strutil" import "github.com/duke-git/lancet/v2/strutil"
@@ -1896,9 +1959,13 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/JZiu0RXpgN-)] [[play](https://go.dev/play/p/JZiu0RXpgN-)]
- **<big>ExtractContent</big>** : 提取两个标记之间的内容。 - **<big>ExtractContent</big>** : 提取两个标记之间的内容。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ExtractContent)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ExtractContent)]
[[play](https://go.dev/play/p/todo)] [[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](https://go.dev/play/p/uvyA6azGLB1)]
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="system"> 23. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/system" import "github.com/duke-git/lancet/v2/system"
@@ -1948,7 +2015,7 @@ import "github.com/duke-git/lancet/v2/system"
<h3 id="tuple"> 23. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="tuple"> 24. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/tuple" import "github.com/duke-git/lancet/v2/tuple"
@@ -2065,7 +2132,7 @@ import "github.com/duke-git/lancet/v2/tuple"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip10)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip10)]
[[play](https://go.dev/play/p/-taQB6Wfre_z)] [[play](https://go.dev/play/p/-taQB6Wfre_z)]
<h3 id="validator"> 24. validator 验证器包,包含常用字符串格式验证函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="validator"> 25. validator 验证器包,包含常用字符串格式验证函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/validator" import "github.com/duke-git/lancet/v2/validator"
@@ -2151,6 +2218,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsIpV6</big>** : 验证字符串是否是ipv6地址。 - **<big>IsIpV6</big>** : 验证字符串是否是ipv6地址。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV6)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV6)]
[[play](https://go.dev/play/p/AHA0r0AzIdC)] [[play](https://go.dev/play/p/AHA0r0AzIdC)]
- **<big>IsIpPort</big>** : 检查字符串是否是ip:port格式。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpPort)]
[[play](https://go.dev/play/p/xUmls_b9L29)]
- **<big>IsStrongPassword</big>** : 验证字符串是否是强密码:(字母+数字+特殊字符)。 - **<big>IsStrongPassword</big>** : 验证字符串是否是强密码:(字母+数字+特殊字符)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsStrongPassword)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsStrongPassword)]
[[play](https://go.dev/play/p/QHdVcSQ3uDg)] [[play](https://go.dev/play/p/QHdVcSQ3uDg)]
@@ -2200,7 +2270,7 @@ import "github.com/duke-git/lancet/v2/validator"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChinaUnionPay)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChinaUnionPay)]
[[play](https://go.dev/play/p/yafpdxLiymu)] [[play](https://go.dev/play/p/yafpdxLiymu)]
<h3 id="xerror"> 25. xerror 包实现一些错误处理函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="xerror"> 26. xerror 包实现一些错误处理函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go ```go
import "github.com/duke-git/lancet/v2/xerror" import "github.com/duke-git/lancet/v2/xerror"
@@ -2249,7 +2319,7 @@ import "github.com/duke-git/lancet/v2/xerror"
[[play](https://go.dev/play/p/acyZVkNZEeW)] [[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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#TryCatch)]
[[play](https://go.dev/play/p/todo)] [[play](https://go.dev/play/p/D5Mdb0mRj0P)]
## 如何贡献代码 ## 如何贡献代码

View File

@@ -157,10 +157,10 @@ func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
// Play: https://go.dev/play/p/qmWSy1NVF-Y // Play: https://go.dev/play/p/qmWSy1NVF-Y
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T { func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T {
valStream := make(chan T) valStream := make(chan T)
go func() { go func() {
defer close(valStream) defer close(valStream)
wg := sync.WaitGroup{}
defer wg.Wait()
for { for {
var stream <-chan T var stream <-chan T
select { select {
@@ -169,19 +169,22 @@ func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-c
return return
} }
stream = maybeStream stream = maybeStream
wg.Add(1)
case <-ctx.Done(): case <-ctx.Done():
return return
} }
for val := range c.OrDone(ctx, stream) { go func() {
select { defer wg.Done()
case valStream <- val: for val := range c.OrDone(ctx, stream) {
case <-ctx.Done(): select {
case valStream <- val:
case <-ctx.Done():
}
} }
} }()
} }
}() }()
return valStream return valStream
} }

View File

@@ -168,7 +168,8 @@ func ExampleChannel_Tee() {
func ExampleChannel_Bridge() { func ExampleChannel_Bridge() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
m1 := make(map[int]int)
m2 := make(map[int]int)
c := NewChannel[int]() c := NewChannel[int]()
genVals := func() <-chan <-chan int { genVals := func() <-chan <-chan int {
out := make(chan (<-chan int)) out := make(chan (<-chan int))
@@ -177,6 +178,7 @@ func ExampleChannel_Bridge() {
for i := 1; i <= 5; i++ { for i := 1; i <= 5; i++ {
stream := make(chan int, 1) stream := make(chan int, 1)
stream <- i stream <- i
m1[i]++
close(stream) close(stream)
out <- stream out <- stream
} }
@@ -185,12 +187,15 @@ func ExampleChannel_Bridge() {
} }
for v := range c.Bridge(ctx, genVals()) { for v := range c.Bridge(ctx, genVals()) {
fmt.Println(v) m2[v]++
}
for k, v := range m1 {
fmt.Println(m2[k] == v)
} }
// Output: // Output:
// 1 // true
// 2 // true
// 3 // true
// 4 // true
// 5 // true
} }

View File

@@ -169,7 +169,8 @@ func TestTee(t *testing.T) {
func TestBridge(t *testing.T) { func TestBridge(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestBridge") assert := internal.NewAssert(t, "TestBridge")
m1 := make(map[int]int)
m2 := make(map[int]int)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -181,6 +182,7 @@ func TestBridge(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
stream := make(chan int, 1) stream := make(chan int, 1)
stream <- i stream <- i
m1[i]++
close(stream) close(stream)
chanStream <- stream chanStream <- stream
} }
@@ -188,9 +190,11 @@ func TestBridge(t *testing.T) {
return chanStream return chanStream
} }
index := 0
for val := range c.Bridge(ctx, genVals()) { for val := range c.Bridge(ctx, genVals()) {
assert.Equal(index, val) m2[val]++
index++ }
for k, v := range m1 {
assert.Equal(m2[k], v)
} }
} }

View File

@@ -108,6 +108,13 @@ func ToString(value any) string {
if value == nil { if value == nil {
return "" return ""
} }
rv := reflect.ValueOf(value)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return ""
}
return ToString(rv.Elem().Interface())
}
switch val := value.(type) { switch val := value.(type) {
case float32: case float32:
@@ -143,12 +150,8 @@ func ToString(value any) string {
if err != nil { if err != nil {
return "" return ""
} }
return string(b)
// todo: maybe we should't supprt other type conversion return string(b)
// v := reflect.ValueOf(value)
// log.Panicf("Unsupported data type: %s ", v.String())
// return ""
} }
} }
@@ -486,7 +489,7 @@ func ToRawUrlBase64(value any) string {
} }
// ToBigInt converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int // ToBigInt converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int
// Play: todo // Play: https://go.dev/play/p/X3itkCxwB_x
func ToBigInt[T any](v T) (*big.Int, error) { func ToBigInt[T any](v T) (*big.Int, error) {
result := new(big.Int) result := new(big.Int)

View File

@@ -142,13 +142,24 @@ func TestToString(t *testing.T) {
} }
aStruct := TestStruct{Name: "TestStruct"} aStruct := TestStruct{Name: "TestStruct"}
i32Val := int32(123)
i64Val := int64(123)
iZeroVal := 0
fVal := 12.3
sVal := "abc"
var iNilPointer *int
var sNilPointer *string
cases := []any{ cases := []any{
"", nil, "", nil,
int(0), int8(1), int16(-1), int32(123), int64(123), int(0), int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123), uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float64(12.3), float32(12.3), float64(12.3), float32(12.3),
true, false, true, false,
[]int{1, 2, 3}, aMap, aStruct, []byte{104, 101, 108, 108, 111}} []int{1, 2, 3}, aMap, aStruct, []byte{104, 101, 108, 108, 111},
&i32Val, &i64Val, &fVal, &sVal, &aStruct, iNilPointer, sNilPointer,
&iZeroVal,
}
expected := []string{ expected := []string{
"", "", "", "",
@@ -157,6 +168,8 @@ func TestToString(t *testing.T) {
"12.3", "12.3", "12.3", "12.3",
"true", "false", "true", "false",
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello", "[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello",
"123", "123", "12.3", "abc", "{\"Name\":\"TestStruct\"}", "", "",
"0",
} }
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {

View File

@@ -565,7 +565,7 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
} }
// RsaEncrypt encrypt data with ras algorithm. // RsaEncrypt encrypt data with ras algorithm.
// Play: https://go.dev/play/p/rDqTT01SPkZ // Play: https://go.dev/play/p/7_zo6mrx-eX
func RsaEncrypt(data []byte, pubKeyFileName string) []byte { func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
file, err := os.Open(pubKeyFileName) file, err := os.Open(pubKeyFileName)
if err != nil { if err != nil {
@@ -600,7 +600,7 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
} }
// RsaDecrypt decrypt data with ras algorithm. // RsaDecrypt decrypt data with ras algorithm.
// Play: https://go.dev/play/p/rDqTT01SPkZ // Play: https://go.dev/play/p/7_zo6mrx-eX
func RsaDecrypt(data []byte, privateKeyFileName string) []byte { func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
file, err := os.Open(privateKeyFileName) file, err := os.Open(privateKeyFileName)
if err != nil { if err != nil {
@@ -663,7 +663,7 @@ func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte
} }
// RsaSign signs the data with RSA. // RsaSign signs the data with RSA.
// Play: todo // Play: https://go.dev/play/p/qhsbf8BJ6Mf
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) { func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) {
privateKey, err := loadRasPrivateKey(privateKeyFileName) privateKey, err := loadRasPrivateKey(privateKeyFileName)
if err != nil { if err != nil {
@@ -679,7 +679,7 @@ func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte,
} }
// RsaVerifySign verifies the signature of the data with RSA. // RsaVerifySign verifies the signature of the data with RSA.
// Play: todo // Play: https://go.dev/play/p/qhsbf8BJ6Mf
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error { func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error {
publicKey, err := loadRsaPublicKey(pubKeyFileName) publicKey, err := loadRsaPublicKey(pubKeyFileName)
if err != nil { if err != nil {
@@ -712,7 +712,6 @@ func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
if blockType == "RSA PUBLIC KEY" { if blockType == "RSA PUBLIC KEY" {
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes) pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil { if err != nil {
// todo: here should be a bug, should return nil, err
key, err := x509.ParsePKIXPublicKey(block.Bytes) key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -1,51 +1,51 @@
-----BEGIN rsa private key----- -----BEGIN rsa private key-----
MIIJJwIBAAKCAgEAwGdN8KE2NTK41cVN7i6mZZS5J86gjNs0LJDHylb2GG+K4O8s MIIJKQIBAAKCAgEAyA3SmuwPpYGw/JdzJ9Iah+Z09Q27sueuT1DpqFTYKeJmYQxe
fq98EMKMJ2xGnKoVRJXucVohr5Eiuf5zgxQe9mpYDyDQ0vnpBkoNkfzJNZQvcQFa I7N8STbmC3dYctsT3Fd+bcfLF2EMscRaae1DAEjli56e41VYvVXPgTSMPWboETDg
IllUeH+eN4hSBhMegPspCr2BcmC/m/N40+PjBMDaHSzeNE2SyIuyflLC7GhQvHnk TtPssl0wZHZ23+cKhK9wBgLQ66mzFRM/ro+OLvydO8jZeaRvG5eATrUaB4ywnXp3
cQXkqJVNC1yesV7zYKDV/xYPI0NxN0kE+4eR650A77jso4gloRUek0cpU4ztpe6Z j8r42qTlR05EJWbzwPKkW61vePgOyObRwJrF47WGZG6cPCQMd5zSJPQw5o3xOEuh
z4za7AEKHmZO8pVHpQJihbNMhItbo6BcgeOFWImXyJfcbCtu1ayciaJ0Q45Z1btu n0FD/GPtM8f+0dxjXpLkcJwnXyUbuAU2WWweJHaYOA2s2anlG6V8ZTUZvbdsKi24
F2wFwQjwZGt7DAvbzYwMBBiTpGxig/8kzibm7bL1Td7huW6Knqbfh4/v+2m+2aNK byCzHbKwhI48vKvnPRnJt5wxvg1PsiAYAalWDrIhod+Atdxp4LXzqM5Nyxfp9+BR
1u0TfncFUr17wDDfeVj/xeNOSbPu2X/AppjVX0rxmvWN+AByFfAuw7/kqMK7QZlA nxJZASrzqd/z3ZpZJ8q9g9A7XPpGgfSB6H9tcjoy5gD+jVJqN26PBqsiW5RyckI3
+5nNjmOojvryGOSu1he4PUnhRyJ7jofOIrLZ9YYBXpRiY/WB8t+xWIlaoPXFmsOx TXPAXRMnr2xshZ4pze6mDPyiHHruLUrnvHnVgg62g/MB0p/bipVxd18418yZ2gRl
SgMwzQFGMlnSFy7nK9T4znE0QFknOQNbELPEB4UGLOEa5Tg6YD2ORDDSqCzMRdkE 0PtmqD7EfKwgREcA20zWeQtYkioOUclX30uG3vVSf0j2KyoAqWmES52KqUp9pwNd
1kAgoMRC9DkRXE45KEcQlhMX9Pahwx3mPS4lMWABVudiEWyhGaR279OWjezqvtoL opWvRtQGAey2b9mpdSgl69rM2RmbzFOIDrfw/1ftPpaUi2CVILviSeYtN2egkeB+
LBJ5GD9QL1XqaqCaJjp+qsHmX8re+MTA3ZJuyUtRoPXPsKCe2nZ6Ma87DlUCAwEA ghCPCcIS9VUpnNTYcW8Y+cTnfi9FwSEwn13WpjiYO/K2412uWjNVNvvW3RECAwEA
AQKCAgAWT+SJ8ygGI0ur/qV66Y4CWazfIOcdbo4uXNvOayc+zjCcxR+z0UXh6621 AQKCAgA8KMv4gpyB7OpIS1L52pjX3Vm2h5a2Si2E9JQPZtRKZCbdcFau7b6mVbgA
JKlLoa21tm1gV8NwSLRuPUPH/51Xlh2AI54T2Udco1nPhDERNY4K3M1HDnTtRF9k Ireacn8ad8UxRi+wrCKhIqb9y7emUkWVIvIs8vFonnF0LVUu8c8/vC74BYrcaGGx
sTpR2gW/j2DDDhbk3LNbsnBgohzBgFvK5lkeV6CeARVB8PcJ008JjFkhgj1yD15P FF7THitvk0JgzF//Plz4LX2tBMaBIr41yER97BDtfzW6dYOKWaiS8s0ZlLQZLBU2
4v3EM+6lVgF7A2PeAwQuFRmu0ZnqaNqi8h7/F9rFQ124VphESCOHCpRrrTn2BGjX yDZ9+OO8VjtOxm0LN61ahO72uJRh0ajEcu7QtxZ4sdQQAM7jxjOP9k54EaxcLcCb
/aVKHGWijRQ/zPsio8aMwxv3NBtSmSIw8Otu39qKjOnaTCyPaQKh9opdzPkd2ZuH Hb1nZpsLSakgpBTC6+nWANtDMNwvoupLkPE1031uPumVlc5T2uDQHkekQ3UZrS6f
Ca/LRdTHkWYTU4ZLmwYRqJTsEzbURlaTULIvsUjVAjHJd3derDeFrBOa8txYqzQi 7hYdtqQnH6YlbPcYa/BXZIdTmJSj9pYKz5Ml1of1KqB6KRxe2vUDtVkTJUwRkZC4
HSKk52vC5mhfeGYmm415SQGhsL1blxuQE6yoAClNeY+16FSjIoZOcFBA46xkc6I/ G8//taLRuaA1vC5bHaseI0uYlTPxueXSDStj7VcUS27nSRWdGxe24EVrIjsA9CNB
y3nWNcJOTcYkYOpb/R2voDabAen98sZHdcD/V86wNAt/JtwdUveeBKCX7YPtAMhu RCVDeU6W4r1eGrvgM8QFNWi1jUMr1rRI7EFHV+24hsfiSWZQwhltzWZ3mn99i9VD
3m6Me5c8b7F5dRTe0bnzdjjaReywooW9+XGRhbmPijqiPBjsHb8dfkZW9vtkyORL BmqI/JvX5d8+8/JlPOVG3HuuxNbU2tzHLhgLADfINvi3TpbdgAY8zm5NqumdwXp6
l8wtMvowTDLxJPB1qgPZDOFpsY/0whZpZi14vNnmGjaFRNrwWwqF5eWBy+eGsQpj JqQ6TXnO/G07fBuZnBP9+Qt5VBQV9rGF5HGpzznbBzSGgBPUNBYqFOyWrdp05J2X
VsvAx9PqTwkoof37h1xkdPT9Ft6T5X6gl+l83H9y9XVksenAQQKCAQEA1HRmshIk bpO23G+2RM0ufXbukF3mI45NJUWOeHOGrxWTEI60Uy0Vf/+/MQKCAQEA5YXXfpHo
RSlGrLrVqiAnndRAHV6flhKsF0+1IOzr7dL7WVfVNoQV2jPcaHGW+bZoi4hP8kIj z72IMQGwEUMw5vkyaoGd2Oi5g+Jjv1q/CKbmCw5sD+mfQTYh41mBpDDACbdSLYDu
YXo2IBF6xSiPiIksJJ2xBmFY+afu/HLtBMwwNhF3oFGunu8ab/ZEDlLg2oRQ1616 0wh9zw74HFOY9Jm7GO9LoIjZ76xv+RSBXLN8tpH1MiAQgBrepWhhlhmyTtiIDNXG
229MCRvgGSM59Q3JTZF0svSHu4xGZvIriRw2g500fTKh9+YiNl1hTeCp3A4f/MKl crj1Yl76J5hBd4dR+gYlJjB+hWsQvV83RxXIUw6eIM6Ngi7RUNuywI1a9mjSY8H+
mo4XvFgcIC9DdfMrb0ST/RtnM9vLCIXYl5ej862PcWD0y2FbvpXtD+WA05bWUsPW OET4kqDqJ2YUvE22ftEWPlNMxyfsVaJs8ihLGEhS8Sq3sPfbdGx9qs3nABjvX0Ab
PBseF6PUKC1IgVMo7oCBAkmnth5gkK1+a6cdZ5j+LC+q5rmK+cDa5IIJzCbXapuA MvDdMx7HeLfD2Grn75mClNji0yUdtKNJGvFx33CTdqtOKvELZf9RJmqtBgrfbqQ6
f6NrRSd18203EwKCAQEA59bPN2eNf90W7pi2+bmGETtgz/wl4DBmVH/PsOCuMlGr qGhoJ23SYD3R6wKCAQEA3yG4vmGB1/bBIV8FwmmH9mKGqd1nWBq4BJAv4dSrkEK1
MnnufkBD9ig7ZAK1iZhZw3tgDrlh7rBGHdFPtwLad9tH5MhwK39WSfqUwLt6CRjx xDsuc7ssmivqdBCAnhMx7oBObC2j/uaqeHAR/L2Ik9VdUzVB/50aFuedgN9FEQ/a
3s6U61riGQzVLKb8iWlxek1IE7s0y/3m4YH17wdLjCOTEe1Jyi1dXcu3+oHGqFy+ N3OiQmcYFz5u5On80aSFD4SeuvdDSegFAgSaMYpSKEFziDbESvYmQGe5nWsrxTG7
HJyBesyroaHswcwV1tUh3QgzuT6McJEolEVdu6XPvXnerYd+LNgYiz8gsrViU8E0 MgLRQym5OJjXX+Am7LAgyOgXD7dOCduWdevfUHHXcMg4KPUju5jCd2EuQYX/CYk2
WKrLnvdRMnn3ySJH7wkLBdeFi8N0glrEcF/Kcbyh6vTKuQkPzIC7szIy+Yvkinva PkMFime07GL3TQiE2MvhzWsV7qVBqe5T1eBFu2sbpYVkYVmmckrSaKLC8gHDEk1s
6fSBoeL72i2najXWFhZsXJbpHxMmw95ZeC7SH4/J9wKCAQAaK7CO1O9E2b3L/0Pc 7WX0K49sxAAJauktikK4viOVvYkmDnaROSQ6TKgR8wKCAQEAtMo2MeZwgaIDMsbn
rhNTPNcdBw/vg6NRR89PHABADpJJwikQixrKA0NuVje70P112rfGZuFG27AZKS4P jtFkJatIgQhT81VtO2sJsuKh8wizzyWvDOGypcUj8FbCfThBK+YQfvM+K1BW+NAL
ZVyw+/zFEevBlnJIZqhozptl0OVLc8FhrU4uY9PE4PgnL4xlPpFa0BLnPwGFybpE sxROOO0WqM9kvLDPKbCkCoiSVRi0NYLGppMHLED9+0A7DeZzwxNgJuwEgr0Z3EB/
PnOgPS+D75wJg1fJAZGWktRMEn6gndfeaENNbzrdqYkX98nUwqSsFSojLMe9urjU vsx5VNNsSQqqHGi6YBEzgFcNzZpkzreG6sSIRnDmQ1Cqfm1ZpxpuDlHyExSQz76c
Oh48RFUgYrk8H4kJ+VQ8W4h/u/1FQib+V2wwNXEAvCU0pRfGeLkz/s3AH7MIRHUY cihAUQrNoxrC8Jjgs0bJK7LjKeN/M0NUwc4qHuU9IXmLiwzg1fzOIDL4ualsMyYd
8eMRkzXik0/RAVO4emt4xvZgunhDz7PXq5OI0mhNNbWBGoesb0hv6HHexzmqjh7Y bQMi6sdapio+50vnbvWfbnfnz6c7UQr1vNW2nrB7j/4KR75jR40s7ZPC+IA0+knR
eqajAoIBAFiIpJsw1U1l3bMB6KYW3gbImSDz1nb1pK5SHLscIgmfPHRLMfNOkWV4 GinXvQKCAQAfsM5oW7jxT16GutQ6lHp3B+9QWN3AqarRGxlx3Z6wUok2YaFrVn4z
Wa3IhxDjeCv5emZFDwv6jtwmKX3m/gzVVXAdxxAlUYtwwMuVDHZa60q9swrpqvwL N2GJMn7R9K/2y8pXmvPb1C8KBADKALFRLdCfbMT+5PU7o5G/J/bLQZEgpw+4lqPZ
9YBWyIulE6uzxXmbfP8Fl9y4J3W/YG9Eyo4HAq3NgyElgb2NP5Ldz8/XSG7fqA9S SUi+wmzh6ZfsaE2TCGNKL+XmL3km6BpbFVCvW/ftHaC5LQIz1XDWBQYtSDEodNiV
abpcOF7RB1yEHFR6eWEnXcq5bqERIfLmjk3QNzPi1gSe99qm/8SiPF474wRyx7Qx rT+JonBk1W8SmcQMu2AeDk4EWYfSCRksL4OqbYaxsjSMeAT10W4zkIVfum0jBy+v
9Zj+mV/EIUx60EneOyjohqmvOv0SHvc9wgjFWB4tbwBwhBzd+kmUILZFJBfxOWJJ Ey9EhZ04nVxorHjk+BunYu4NRlkhEUlP7O4g1XbKbM4NhnBhptZp1w1Nell/hlNQ
GuypYHcQ2xLEooO2aZBU4e/OWXmqDGMCggEAU+t1zgjUM2MTUMYDQry49j0SEQmc UyEDSjLpnChY+PzfeocpLrXZg8ez83r5AoIBAQCrufc7Cu36/WoSKoMwd5n9kOQ9
nGAxhpUxwWHH9LDJeMiD/Tb5DMlqSUwpl5CMCWyvieoG+dyydIT9T3NRa9j+8ga3 W4kzu7ZCv/0PxXj8o3nyNkHD/uZHtGGPowhzt8cZR8JKfmDqrCM28WWyGk79x9bN
MSVrpDtM8O6m1t/8TdbWHFH/En48KNAIQFP5DOLydF0zIfNzLlhlDn03HoIKf5XD s/IVyTc5CHe0/zPJooTgpFA8etMhdc5JIYorSZaAgLiOqzDyGuNi9oyfotbLqiFZ
mcoKiuqr9ycnh1Yp6ns9EJLRMBR2w5yJXE0eAMfj2De+GQFUzSRfHkCFSs5kK+Wp 4uiagMt+hdTP0yEwE1JvzQAwdJvOfMxhiNka6szKb+0QGwBLwCB71OAMXeIfzmBK
JGzruiS0pX24KrTV4boOfhc9yNOJ+p/1t/lbBdp0ruEeATzQO2XaUvyY2iyctllp i0PLomu7tlogS5cePt45lp3nAowreSru/DnfZobVlVFLV9rV+xFuLmxC4zB86cto
fOQtpLwQSFnxDn/hkd9R/fQThQzcXinqCAv8db1hYUR4sVTmPH9lYjW2Og== wfZvKgSI/hP6rg0RkcQbBad0ZsVm52NtCWYTB+bISke8wDN552wZVC6lyPat
-----END rsa private key----- -----END rsa private key-----

View File

@@ -1,14 +1,14 @@
-----BEGIN rsa public key----- -----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwGdN8KE2NTK41cVN7i6m MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyA3SmuwPpYGw/JdzJ9Ia
ZZS5J86gjNs0LJDHylb2GG+K4O8sfq98EMKMJ2xGnKoVRJXucVohr5Eiuf5zgxQe h+Z09Q27sueuT1DpqFTYKeJmYQxeI7N8STbmC3dYctsT3Fd+bcfLF2EMscRaae1D
9mpYDyDQ0vnpBkoNkfzJNZQvcQFaIllUeH+eN4hSBhMegPspCr2BcmC/m/N40+Pj AEjli56e41VYvVXPgTSMPWboETDgTtPssl0wZHZ23+cKhK9wBgLQ66mzFRM/ro+O
BMDaHSzeNE2SyIuyflLC7GhQvHnkcQXkqJVNC1yesV7zYKDV/xYPI0NxN0kE+4eR LvydO8jZeaRvG5eATrUaB4ywnXp3j8r42qTlR05EJWbzwPKkW61vePgOyObRwJrF
650A77jso4gloRUek0cpU4ztpe6Zz4za7AEKHmZO8pVHpQJihbNMhItbo6BcgeOF 47WGZG6cPCQMd5zSJPQw5o3xOEuhn0FD/GPtM8f+0dxjXpLkcJwnXyUbuAU2WWwe
WImXyJfcbCtu1ayciaJ0Q45Z1btuF2wFwQjwZGt7DAvbzYwMBBiTpGxig/8kzibm JHaYOA2s2anlG6V8ZTUZvbdsKi24byCzHbKwhI48vKvnPRnJt5wxvg1PsiAYAalW
7bL1Td7huW6Knqbfh4/v+2m+2aNK1u0TfncFUr17wDDfeVj/xeNOSbPu2X/AppjV DrIhod+Atdxp4LXzqM5Nyxfp9+BRnxJZASrzqd/z3ZpZJ8q9g9A7XPpGgfSB6H9t
X0rxmvWN+AByFfAuw7/kqMK7QZlA+5nNjmOojvryGOSu1he4PUnhRyJ7jofOIrLZ cjoy5gD+jVJqN26PBqsiW5RyckI3TXPAXRMnr2xshZ4pze6mDPyiHHruLUrnvHnV
9YYBXpRiY/WB8t+xWIlaoPXFmsOxSgMwzQFGMlnSFy7nK9T4znE0QFknOQNbELPE gg62g/MB0p/bipVxd18418yZ2gRl0PtmqD7EfKwgREcA20zWeQtYkioOUclX30uG
B4UGLOEa5Tg6YD2ORDDSqCzMRdkE1kAgoMRC9DkRXE45KEcQlhMX9Pahwx3mPS4l 3vVSf0j2KyoAqWmES52KqUp9pwNdopWvRtQGAey2b9mpdSgl69rM2RmbzFOIDrfw
MWABVudiEWyhGaR279OWjezqvtoLLBJ5GD9QL1XqaqCaJjp+qsHmX8re+MTA3ZJu /1ftPpaUi2CVILviSeYtN2egkeB+ghCPCcIS9VUpnNTYcW8Y+cTnfi9FwSEwn13W
yUtRoPXPsKCe2nZ6Ma87DlUCAwEAAQ== pjiYO/K2412uWjNVNvvW3RECAwEAAQ==
-----END rsa public key----- -----END rsa public key-----

View File

@@ -64,28 +64,95 @@ func init() {
} }
} }
// AddMinute add or sub minute to the time. // AddMinute add or sub minutes to the time.
// Play: https://go.dev/play/p/nT1heB1KUUK // Play: https://go.dev/play/p/nT1heB1KUUK
func AddMinute(t time.Time, minute int64) time.Time { func AddMinute(t time.Time, minutes int64) time.Time {
return t.Add(time.Minute * time.Duration(minute)) return t.Add(time.Minute * time.Duration(minutes))
} }
// AddHour add or sub hour to the time. // AddHour add or sub hours to the time.
// Play: https://go.dev/play/p/rcMjd7OCsi5 // Play: https://go.dev/play/p/rcMjd7OCsi5
func AddHour(t time.Time, hour int64) time.Time { func AddHour(t time.Time, hours int64) time.Time {
return t.Add(time.Hour * time.Duration(hour)) return t.Add(time.Hour * time.Duration(hours))
} }
// AddDay add or sub day to the time. // AddDay add or sub days to the time.
// Play: https://go.dev/play/p/dIGbs_uTdFa // Play: https://go.dev/play/p/dIGbs_uTdFa
func AddDay(t time.Time, day int64) time.Time { func AddDay(t time.Time, days int64) time.Time {
return t.Add(24 * time.Hour * time.Duration(day)) return t.Add(24 * time.Hour * time.Duration(days))
}
// AddWeek add or sub weeks to the time.
// 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: https://go.dev/play/p/DLoiOnpLvsN
func AddMonth(t time.Time, months int64) time.Time {
return t.AddDate(0, int(months), 0)
} }
// AddYear add or sub year to the time. // AddYear add or sub year to the time.
// Play: https://go.dev/play/p/MqW2ujnBx10 // Play: https://go.dev/play/p/MqW2ujnBx10
func AddYear(t time.Time, year int64) time.Time { func AddYear(t time.Time, year int64) time.Time {
return t.Add(365 * 24 * time.Hour * time.Duration(year)) return t.AddDate(int(year), 0, 0)
}
// 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: 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()
lastDayOfMonth := time.Date(year, month+1, 0, 0, 0, 0, 0, t.Location()).Day()
if day > lastDayOfMonth {
t = time.Date(year, month, lastDayOfMonth, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
}
return t
}
// 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: https://go.dev/play/p/KLw0lo6mbVW
func AddMonthSafe(t time.Time, months int) time.Time {
year := t.Year()
month := int(t.Month()) + months
for month > 12 {
month -= 12
year++
}
for month < 1 {
month += 12
year--
}
daysInMonth := time.Date(year, time.Month(month+1), 0, 0, 0, 0, 0, time.UTC).Day()
day := t.Day()
if day > daysInMonth {
day = daysInMonth
}
return time.Date(year, time.Month(month), day, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
}
// 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: https://go.dev/play/p/KVGXWZZ54ZH
func AddYearSafe(t time.Time, years int) time.Time {
year, month, day := t.Date()
year += years
if month == time.February && day == 29 {
if !IsLeapYear(year) {
day = 28
}
}
return time.Date(year, month, day, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
} }
// GetNowDate return format yyyy-mm-dd of current date. // GetNowDate return format yyyy-mm-dd of current date.
@@ -445,7 +512,7 @@ func GenerateDatetimesBetween(start, end time.Time, layout string, interval stri
} }
// Min returns the earliest time among the given times. // Min returns the earliest time among the given times.
// Play: todo // Play: https://go.dev/play/p/MCIDvHNOGGb
func Min(t1 time.Time, times ...time.Time) time.Time { func Min(t1 time.Time, times ...time.Time) time.Time {
minTime := t1 minTime := t1
@@ -459,7 +526,7 @@ func Min(t1 time.Time, times ...time.Time) time.Time {
} }
// Max returns the latest time among the given times. // Max returns the latest time among the given times.
// Play: todo // Play: https://go.dev/play/p/9m6JMk1LB7-
func Max(t1 time.Time, times ...time.Time) time.Time { func Max(t1 time.Time, times ...time.Time) time.Time {
maxTime := t1 maxTime := t1
@@ -473,7 +540,7 @@ func Max(t1 time.Time, times ...time.Time) time.Time {
} }
// MaxMin returns the latest and earliest time among the given times. // MaxMin returns the latest and earliest time among the given times.
// Play: todo // Play: https://go.dev/play/p/rbW51cDtM_2
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) { func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) {
maxTime = t1 maxTime = t1
minTime = t1 minTime = t1

View File

@@ -7,71 +7,141 @@ import (
) )
func ExampleAddDay() { func ExampleAddDay() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
tomorrow := AddDay(now, 1) after1Day := AddDay(date, 1)
diff1 := tomorrow.Sub(now) before1Day := AddDay(date, -1)
yesterday := AddDay(now, -1) fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
diff2 := yesterday.Sub(now) fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 24h0m0s // 2021-01-02 00:00:00
// -24h0m0s // 2020-12-31 00:00:00
}
func ExampleAddWeek() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Weeks := AddWeek(date, 2)
before2Weeks := AddWeek(date, -2)
fmt.Println(after2Weeks.Format("2006-01-02"))
fmt.Println(before2Weeks.Format("2006-01-02"))
// Output:
// 2021-01-15
// 2020-12-18
}
func ExampleAddMonth() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Months := AddMonth(date, 2)
before2Months := AddMonth(date, -2)
fmt.Println(after2Months.Format("2006-01-02"))
fmt.Println(before2Months.Format("2006-01-02"))
// Output:
// 2021-03-01
// 2020-11-01
} }
func ExampleAddHour() { func ExampleAddHour() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Hours := AddHour(now, 2) after2Hours := AddHour(date, 2)
diff1 := after2Hours.Sub(now) before2Hours := AddHour(date, -2)
before2Hours := AddHour(now, -2) fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
diff2 := before2Hours.Sub(now) fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2h0m0s // 2021-01-01 02:00:00
// -2h0m0s // 2020-12-31 22:00:00
} }
func ExampleAddMinute() { func ExampleAddMinute() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Minutes := AddMinute(now, 2) after2Minutes := AddMinute(date, 2)
diff1 := after2Minutes.Sub(now) before2Minutes := AddMinute(date, -2)
before2Minutes := AddMinute(now, -2) fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
diff2 := before2Minutes.Sub(now) fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2m0s // 2021-01-01 00:02:00
// -2m0s // 2020-12-31 23:58:00
} }
func ExampleAddYear() { func ExampleAddYear() {
now := time.Now() date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := AddYear(now, 1) after2Years := AddYear(date, 2)
diff1 := after1Year.Sub(now) before2Years := AddYear(date, -2)
before1Year := AddYear(now, -1) fmt.Println(after2Years.Format("2006-01-02"))
diff2 := before1Year.Sub(now) fmt.Println(before2Years.Format("2006-01-02"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 8760h0m0s // 2023-01-01
// -8760h0m0s // 2019-01-01
}
func ExampleAddDaySafe() {
leapYearDate1, _ := time.Parse("2006-01-02", "2024-02-29")
result1 := AddDaySafe(leapYearDate1, 1)
leapYearDate2, _ := time.Parse("2006-01-02", "2024-03-01")
result2 := AddDaySafe(leapYearDate2, -1)
nonLeapYearDate1, _ := time.Parse("2006-01-02", "2025-02-28")
result3 := AddDaySafe(nonLeapYearDate1, 1)
nonLeaYearDate2, _ := time.Parse("2006-01-02", "2025-03-01")
result4 := AddDaySafe(nonLeaYearDate2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
fmt.Println(result3.Format("2006-01-02"))
fmt.Println(result4.Format("2006-01-02"))
// Output:
// 2024-03-01
// 2024-02-29
// 2025-03-01
// 2025-02-28
}
func ExampleAddMonthSafe() {
date1, _ := time.Parse("2006-01-02", "2025-01-31")
result1 := AddMonthSafe(date1, 1)
date2, _ := time.Parse("2006-01-02", "2024-02-29")
result2 := AddMonthSafe(date2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2025-02-28
// 2024-01-29
}
func ExampleAddYearSafe() {
date, _ := time.Parse("2006-01-02", "2020-02-29")
result1 := AddYearSafe(date, 1)
result2 := AddYearSafe(date, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2021-02-28
// 2019-02-28
} }
func ExampleGetNowDate() { func ExampleGetNowDate() {

View File

@@ -9,78 +9,427 @@ import (
func TestAddYear(t *testing.T) { func TestAddYear(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestAddDay") assert := internal.NewAssert(t, "TestAddDay")
now := time.Now() tests := []struct {
after2Years := AddYear(now, 1) inputDate string
diff1 := after2Years.Sub(now) years int
assert.Equal(float64(8760), diff1.Hours()) expected string
}{
{
inputDate: "2021-01-01 00:00:00",
years: 1,
expected: "2022-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
years: -1,
expected: "2020-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
years: 0,
expected: "2021-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
years: 2,
expected: "2023-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
years: 3,
expected: "2024-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
years: 4,
expected: "2025-01-01 00:00:00",
},
}
before2Years := AddYear(now, -1) for _, tt := range tests {
diff2 := before2Years.Sub(now) date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
assert.Equal(float64(-8760), diff2.Hours()) result := AddYear(date, int64(tt.years))
} assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
func TestBetweenSeconds(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBetweenSeconds")
today := time.Now()
tomorrow := AddDay(today, 1)
yesterday := AddDay(today, -1)
result1 := BetweenSeconds(today, tomorrow)
result2 := BetweenSeconds(today, yesterday)
assert.Equal(int64(86400), result1)
assert.Equal(int64(-86400), result2)
} }
func TestAddDay(t *testing.T) { func TestAddDay(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestAddDay") assert := internal.NewAssert(t, "TestAddDay")
now := time.Now() tests := []struct {
after2Days := AddDay(now, 2) inputDate string
diff1 := after2Days.Sub(now) days int
assert.Equal(float64(48), diff1.Hours()) expected string
}{
{
inputDate: "2021-01-01 00:00:00",
days: 1,
expected: "2021-01-02 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
days: -1,
expected: "2020-12-31 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
days: 0,
expected: "2021-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
days: 2,
expected: "2021-01-03 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
days: 3,
expected: "2021-01-04 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
days: 4,
expected: "2021-01-05 00:00:00",
},
}
before2Days := AddDay(now, -2) for _, tt := range tests {
diff2 := before2Days.Sub(now) date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
assert.Equal(float64(-48), diff2.Hours()) result := AddDay(date, int64(tt.days))
assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
} }
func TestAddHour(t *testing.T) { func TestAddHour(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestAddHour") assert := internal.NewAssert(t, "TestAddHour")
now := time.Now() tests := []struct {
after2Hours := AddHour(now, 2) inputDate string
diff1 := after2Hours.Sub(now) hours int
assert.Equal(float64(2), diff1.Hours()) expected string
}{
{
inputDate: "2021-01-01 00:00:00",
hours: 1,
expected: "2021-01-01 01:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: -1,
expected: "2020-12-31 23:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: 0,
expected: "2021-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: 24,
expected: "2021-01-02 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: 25,
expected: "2021-01-02 01:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: 48,
expected: "2021-01-03 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
hours: 49,
expected: "2021-01-03 01:00:00",
},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
result := AddHour(date, int64(tt.hours))
assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
before2Hours := AddHour(now, -2)
diff2 := before2Hours.Sub(now)
assert.Equal(float64(-2), diff2.Hours())
} }
func TestAddMinute(t *testing.T) { func TestAddMinute(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestAddMinute") assert := internal.NewAssert(t, "TestAddMinute")
now := time.Now() tests := []struct {
after2Minutes := AddMinute(now, 2) inputDate string
diff1 := after2Minutes.Sub(now) minutes int
assert.Equal(float64(2), diff1.Minutes()) expected string
}{
{
inputDate: "2021-01-01 00:00:00",
minutes: 1,
expected: "2021-01-01 00:01:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: -1,
expected: "2020-12-31 23:59:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: 0,
expected: "2021-01-01 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: 60,
expected: "2021-01-01 01:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: 61,
expected: "2021-01-01 01:01:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: 1440,
expected: "2021-01-02 00:00:00",
},
{
inputDate: "2021-01-01 00:00:00",
minutes: 1441,
expected: "2021-01-02 00:01:00",
},
}
before2Minutes := AddMinute(now, -2) for _, tt := range tests {
diff2 := before2Minutes.Sub(now) date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
assert.Equal(float64(-2), diff2.Minutes()) result := AddMinute(date, int64(tt.minutes))
assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
}
func TestAddWeek(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddWeek")
tests := []struct {
inputDate string
weeks int
expected string
}{
{
inputDate: "2021-01-01",
weeks: 1,
expected: "2021-01-08",
},
{
inputDate: "2021-01-01",
weeks: -1,
expected: "2020-12-25",
},
{
inputDate: "2021-01-01",
weeks: 0,
expected: "2021-01-01",
},
{
inputDate: "2021-01-01",
weeks: 52,
expected: "2021-12-31",
},
{
inputDate: "2021-01-01",
weeks: 53,
expected: "2022-01-07",
},
{
inputDate: "2021-01-01",
weeks: 104,
expected: "2022-12-30",
},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02", tt.inputDate)
result := AddWeek(date, int64(tt.weeks))
assert.Equal(tt.expected, result.Format("2006-01-02"))
}
}
func TestAddMonth(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddMonth")
tests := []struct {
inputDate string
months int
expected string
}{
{
inputDate: "2021-01-01",
months: 1,
expected: "2021-02-01",
},
{
inputDate: "2021-01-01",
months: -1,
expected: "2020-12-01",
},
{
inputDate: "2021-01-01",
months: 0,
expected: "2021-01-01",
},
{
inputDate: "2021-01-01",
months: 12,
expected: "2022-01-01",
},
{
inputDate: "2021-01-01",
months: 13,
expected: "2022-02-01",
},
{
inputDate: "2021-01-01",
months: 24,
expected: "2023-01-01",
},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02", tt.inputDate)
result := AddMonth(date, int64(tt.months))
assert.Equal(tt.expected, result.Format("2006-01-02"))
}
}
func TestAddDaySafe(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddDaySafe")
tests := []struct {
inputDate string
days int
expected string
}{
{"2025-01-31", 10, "2025-02-10"},
{"2025-01-01", 30, "2025-01-31"},
{"2025-01-31", 1, "2025-02-01"},
{"2025-02-28", 1, "2025-03-01"},
{"2024-02-29", 1, "2024-03-01"},
{"2024-02-29", 365, "2025-02-28"},
{"2025-01-31", -10, "2025-01-21"},
{"2025-01-01", -30, "2024-12-02"},
{"2025-02-01", -1, "2025-01-31"},
{"2025-03-01", -1, "2025-02-28"},
{"2024-03-01", -1, "2024-02-29"},
{"2025-01-31", -31, "2024-12-31"},
{"2025-12-31", 1, "2026-01-01"},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02", tt.inputDate)
result := AddDaySafe(date, tt.days)
assert.Equal(tt.expected, result.Format("2006-01-02"))
}
}
func TestAddMonthSafe(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddMonthSafe")
tests := []struct {
inputDate string
months int
expected string
}{
{
inputDate: "2025-01-31",
months: 1,
expected: "2025-02-28",
},
{
inputDate: "2025-01-31",
months: -1,
expected: "2024-12-31",
},
{
inputDate: "2025-12-31",
months: 1,
expected: "2026-01-31",
},
{
inputDate: "2025-01-31",
months: -1,
expected: "2024-12-31",
},
{
inputDate: "2024-02-29",
months: 1,
expected: "2024-03-29",
},
{
inputDate: "2024-02-29",
months: -1,
expected: "2024-01-29",
},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02", tt.inputDate)
result := AddMonthSafe(date, tt.months)
assert.Equal(tt.expected, result.Format("2006-01-02"))
}
}
func TestAddYearSafe(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddYearSafe")
tests := []struct {
inputDate string
years int
expected string
}{
{
inputDate: "2020-02-29",
years: 1,
expected: "2021-02-28",
},
{
inputDate: "2020-02-29",
years: 2,
expected: "2022-02-28",
},
{
inputDate: "2020-02-29",
years: -1,
expected: "2019-02-28",
},
{
inputDate: "2020-02-29",
years: -2,
expected: "2018-02-28",
},
}
for _, tt := range tests {
date, _ := time.Parse("2006-01-02", tt.inputDate)
result := AddYearSafe(date, tt.years)
assert.Equal(tt.expected, result.Format("2006-01-02"))
}
} }
func TestGetNowDate(t *testing.T) { func TestGetNowDate(t *testing.T) {
@@ -578,3 +927,19 @@ func TestMaxMin(t *testing.T) {
assert.Equal(oneMinuteAgo, min) assert.Equal(oneMinuteAgo, min)
assert.Equal(oneMinuteAfter, max) assert.Equal(oneMinuteAfter, max)
} }
func TestBetweenSeconds(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBetweenSeconds")
today := time.Now()
tomorrow := AddDay(today, 1)
yesterday := AddDay(today, -1)
result1 := BetweenSeconds(today, tomorrow)
result2 := BetweenSeconds(today, yesterday)
assert.Equal(int64(86400), result1)
assert.Equal(int64(-86400), result2)
}

View File

@@ -114,6 +114,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
], ],
}, },
{ text: 'datetime', link: '/en/api/packages/datetime' }, { text: 'datetime', link: '/en/api/packages/datetime' },
{ text: 'eventbus', link: '/en/api/packages/eventbus' },
{ text: 'fileutil', link: '/en/api/packages/fileutil' }, { text: 'fileutil', link: '/en/api/packages/fileutil' },
{ text: 'formatter', link: '/en/api/packages/formatter' }, { text: 'formatter', link: '/en/api/packages/formatter' },
{ text: 'function', link: '/en/api/packages/function' }, { text: 'function', link: '/en/api/packages/function' },

View File

@@ -127,6 +127,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
], ],
}, },
{ text: '日期&时间', link: '/api/packages/datetime' }, { text: '日期&时间', link: '/api/packages/datetime' },
{ text: '事件总线', link: '/api/packages/eventbus' },
{ text: '文件', link: '/api/packages/fileutil' }, { text: '文件', link: '/api/packages/fileutil' },
{ text: '格式化工具', link: '/api/packages/formatter' }, { text: '格式化工具', link: '/api/packages/formatter' },
{ text: '函数', link: '/api/packages/function' }, { text: '函数', link: '/api/packages/function' },

View File

@@ -47,6 +47,7 @@ outline: deep
<div class="package-cell">cryptor</div> <div class="package-cell">cryptor</div>
<div class="package-cell">datastructure</div> <div class="package-cell">datastructure</div>
<div class="package-cell">datetime</div> <div class="package-cell">datetime</div>
<div class="package-cell">eventbus</div>
<div class="package-cell">fileutil</div> <div class="package-cell">fileutil</div>
<div class="package-cell">formatter</div> <div class="package-cell">formatter</div>
<div class="package-cell">function</div> <div class="package-cell">function</div>

View File

@@ -373,7 +373,7 @@ import (
func main() { func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3} aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result, err := ToJson(aMap) result, err := convertor.ToJson(aMap)
if err != nil { if err != nil {
fmt.Printf("%v", err) fmt.Printf("%v", err)
@@ -1155,7 +1155,7 @@ func main() {
<p>将整数值转换为bigInt。</p> <p>将整数值转换为bigInt。</p>
<b>函数签名:<span style="float:right;display:inline-block;">[运行](todo)</span></b> <b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/X3itkCxwB_x)</span></b>
```go ```go
func ToBigInt[T any](v T) (*big.Int, error) func ToBigInt[T any](v T) (*big.Int, error)

View File

@@ -1436,7 +1436,7 @@ func main() {
func RsaEncrypt(data []byte, pubKeyFileName string) []byte func RsaEncrypt(data []byte, pubKeyFileName string) []byte
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uef0q1fz53I)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7_zo6mrx-eX)</span></b>
```go ```go
package main package main
@@ -1473,7 +1473,7 @@ func main() {
func RsaDecrypt(data []byte, privateKeyFileName string) []byte func RsaDecrypt(data []byte, privateKeyFileName string) []byte
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uef0q1fz53I)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7_zo6mrx-eX)</span></b>
```go ```go
package main package main
@@ -1621,7 +1621,7 @@ func main() {
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, 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/qhsbf8BJ6Mf)</span></b>
```go ```go
package main package main
@@ -1638,12 +1638,12 @@ func main() {
privateKey := "./rsa_private.pem" privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem" publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey) signature, err := cryptor.RsaSign(hash, data, privateKey)
if err != nil { if err != nil {
return return
} }
err = RsaVerifySign(hash, data, signature, publicKey) err = cryptor.RsaVerifySign(hash, data, signature, publicKey)
if err != nil { if err != nil {
return return
} }
@@ -1660,7 +1660,7 @@ func main() {
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) 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/qhsbf8BJ6Mf)</span></b>
```go ```go
package main package main
@@ -1677,12 +1677,12 @@ func main() {
privateKey := "./rsa_private.pem" privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem" publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey) signature, err := cryptor.RsaSign(hash, data, privateKey)
if err != nil { if err != nil {
return return
} }
err = RsaVerifySign(hash, data, signature, publicKey) err = cryptor.RsaVerifySign(hash, data, signature, publicKey)
if err != nil { if err != nil {
return return
} }

View File

@@ -23,9 +23,14 @@ import (
## 目录 ## 目录
- [AddDay](#AddDay) - [AddDay](#AddDay)
- [AddWeek](#AddWeek)
- [AddMonth](#AddMonth)
- [AddHour](#AddHour) - [AddHour](#AddHour)
- [AddMinute](#AddMinute) - [AddMinute](#AddMinute)
- [AddYear](#AddYear) - [AddYear](#AddYear)
- [AddDaySafe](#AddDaySafe)
- [AddMonthSafe](#AddMonthSafe)
- [AddYearSafe](#AddYearSafe)
- [BeginOfMinute](#BeginOfMinute) - [BeginOfMinute](#BeginOfMinute)
- [BeginOfHour](#BeginOfHour) - [BeginOfHour](#BeginOfHour)
- [BeginOfDay](#BeginOfDay) - [BeginOfDay](#BeginOfDay)
@@ -109,7 +114,7 @@ import (
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func AddDay(t time.Time, day int64) time.Time func AddDay(t time.Time, days int64) time.Time
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dIGbs_uTdFa)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dIGbs_uTdFa)</span></b>
@@ -124,20 +129,89 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
tomorrow := datetime.AddDay(now, 1) after1Day := datetime.AddDay(date, 1)
diff1 := tomorrow.Sub(now) before1Day := datetime.AddDay(date, -1)
yesterday := datetime.AddDay(now, -1) fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
diff2 := yesterday.Sub(now) fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 24h0m0s // 2021-01-02 00:00:00
// -24h0m0s // 2020-12-31 00:00:00
}
```
### <span id="AddWeek">AddWeek</span>
<p>将日期加/减星期数。</p>
<b>函数签名:</b>
```go
func AddWeek(t time.Time, weeks int64) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/M9TqdMiaA2p)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Weeks := datetime.AddWeek(date, 2)
before2Weeks := datetime.AddWeek(date, -2)
fmt.Println(after2Weeks.Format("2006-01-02"))
fmt.Println(before2Weeks.Format("2006-01-02"))
// Output:
// 2021-01-15
// 2020-12-18
}
```
### <span id="AddMonth">AddMonth</span>
<p>将日期加/减月数。</p>
<b>函数签名:</b>
```go
func AddMonth(t time.Time, months int64) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/DLoiOnpLvsN)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Months := datetime.AddMonth(date, 2)
before2Months := datetime.AddMonth(date, -2)
fmt.Println(after2Months.Format("2006-01-02"))
fmt.Println(before2Months.Format("2006-01-02"))
// Output:
// 2021-03-01
// 2020-11-01
} }
``` ```
@@ -148,7 +222,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func AddHour(t time.Time, hour int64) time.Time func AddHour(t time.Time, hours int64) time.Time
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rcMjd7OCsi5)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rcMjd7OCsi5)</span></b>
@@ -163,20 +237,17 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Hours := datetime.AddHour(now, 2) after2Hours := datetime.AddHour(date, 2)
diff1 := after2Hours.Sub(now) before2Hours := datetime.AddHour(date, -2)
before2Hours := datetime.AddHour(now, -2) fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
diff2 := before2Hours.Sub(now) fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2h0m0s // 2021-01-01 02:00:00
// -2h0m0s // 2020-12-31 22:00:00
} }
``` ```
@@ -187,7 +258,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func AddMinute(t time.Time, minute int64) time.Time func AddMinute(t time.Time, minutes int64) time.Time
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nT1heB1KUUK)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nT1heB1KUUK)</span></b>
@@ -202,20 +273,17 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Minutes := datetime.AddMinute(now, 2) after2Minutes := datetime.AddMinute(date, 2)
diff1 := after2Minutes.Sub(now) before2Minutes := datetime.AddMinute(date, -2)
before2Minutes := datetime.AddMinute(now, -2) fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
diff2 := before2Minutes.Sub(now) fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2m0s // 2021-01-01 00:02:00
// -2m0s // 2020-12-31 23:58:00
} }
``` ```
@@ -226,7 +294,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func AddYear(t time.Time, year int64) time.Time func AddYear(t time.Time, years int64) time.Time
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/MqW2ujnBx10)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/MqW2ujnBx10)</span></b>
@@ -241,20 +309,137 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := datetime.AddYear(now, 1) after2Years := AddYear(date, 2)
diff1 := after1Year.Sub(now) before2Years := AddYear(date, -2)
before1Year := datetime.AddYear(now, -1) fmt.Println(after2Years.Format("2006-01-02"))
diff2 := before1Year.Sub(now) fmt.Println(before2Years.Format("2006-01-02"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 8760h0m0s // 2023-01-01
// -8760h0m0s // 2019-01-01
}
```
### <span id="AddDaySafe">AddDaySafe</span>
<p>增加/减少指定的天数,并确保日期是有效日期。</p>
<b>函数签名:</b>
```go
func AddDaySafe(t time.Time, days int) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JTohZFpoDJ3)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
leapYearDate1, _ := time.Parse("2006-01-02", "2024-02-29")
result1 := datetime.AddDaySafe(leapYearDate1, 1)
leapYearDate2, _ := time.Parse("2006-01-02", "2024-03-01")
result2 := datetime.AddDaySafe(leapYearDate2, -1)
nonLeapYearDate1, _ := time.Parse("2006-01-02", "2025-02-28")
result3 := datetime.AddDaySafe(nonLeapYearDate1, 1)
nonLeaYearDate2, _ := time.Parse("2006-01-02", "2025-03-01")
result4 := datetime.AddDaySafe(nonLeaYearDate2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
fmt.Println(result3.Format("2006-01-02"))
fmt.Println(result4.Format("2006-01-02"))
// Output:
// 2024-03-01
// 2024-02-29
// 2025-03-01
// 2025-02-28
}
```
### <span id="AddMonthSafe">AddMonthSafe</span>
<p>增加/减少指定的月份,并确保日期是有效日期。</p>
<b>函数签名:</b>
```go
func AddMonthSafe(t time.Time, months int) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KLw0lo6mbVW)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date1, _ := time.Parse("2006-01-02", "2025-01-31")
result1 := datetime.AddMonthSafe(date1, 1)
date2, _ := time.Parse("2006-01-02", "2024-02-29")
result2 := datetime.AddMonthSafe(date2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2025-02-28
// 2024-01-29
}
```
### <span id="AddYearSafe">AddYearSafe</span>
<p>增加/减少指定的年份,并确保日期是有效日期。</p>
<b>函数签名:</b>
```go
func AddYearSafe(t time.Time, years int) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KVGXWZZ54ZH)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2020-02-29")
result1 := datetime.AddYearSafe(date, 1)
result2 := datetime.AddYearSafe(date, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2021-02-28
// 2019-02-28
} }
``` ```
@@ -1585,7 +1770,7 @@ func main() {
func Min(t1 time.Time, times ...time.Time) time.Time func Min(t1 time.Time, times ...time.Time) 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/MCIDvHNOGGb)</span></b>
```go ```go
package main package main
@@ -1615,7 +1800,7 @@ func main() {
func Max(t1 time.Time, times ...time.Time) time.Time func Max(t1 time.Time, times ...time.Time) 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/9m6JMk1LB7-)</span></b>
```go ```go
package main package main
@@ -1645,7 +1830,7 @@ func main() {
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime 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/rbW51cDtM_2)</span></b>
```go ```go
package main package main

View File

@@ -0,0 +1,407 @@
# EventBus
EventbBus是一个事件总线用于在应用程序中处理事件。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go](https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/eventbus"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [NewEventBus](#NewEventBus)
- [Subscribe](#Subscribe)
- [Unsubscribe](#Unsubscribe)
- [Publish](#Publish)
- [ClearListeners](#ClearListeners)
- [ClearListenersByTopic](#ClearListenersByTopic)
- [GetListenersCount](#GetListenersCount)
- [GetAllListenersCount](#GetAllListenersCount)
- [GetEvents](#GetEvents)
- [SetErrorHandler](#SetErrorHandler)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="NewEventBus">NewEventBus</span>
<p>创建EventBus实例。</p>
<b>函数签名:</b>
```go
func NewEventBus[T any]() *EventBus[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gHbOPV_NUOJ)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 1
}
```
### <span id="Subscribe">Subscribe</span>
<p>订阅具有特定事件主题和监听函数的事件。支持异步,事件优先级,事件过滤器。</p>
<b>函数签名:</b>
```go
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;">[运行](https://go.dev/play/p/EYGf_8cHei-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
filter := func(eventData int) bool {
return eventData == 1
}
eb.Subscribe("event1", listener, false, 0, filter)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 2})
fmt.Println(receivedData)
// Output:
// 1
}
```
### <span id="Unsubscribe">Unsubscribe</span>
<p>取消订阅具有特定事件主题的事件。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T))
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Tmh7Ttfvprf)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Unsubscribe("event1", listener)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 0
}
```
### <span id="Publish">Publish</span>
<p>发布一个带有特定事件主题和数据负载的事件。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) Publish(event eventbus.Event[T])
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gHTtVexFSH9)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {
fmt.Println(eventData)
}, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
// Output:
// 1
}
```
### <span id="ClearListeners">ClearListeners</span>
<p>清空所有事件监听器。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) ClearListeners()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KBfBYlKPgqD)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Subscribe("event2", listener, false, 0, nil)
eb.ClearListeners()
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
fmt.Println(receivedData)
// Output:
// 0
}
```
### <span id="ClearListenersByTopic">ClearListenersByTopic</span>
<p>清空特定事件监听器。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) ClearListenersByTopic(topic string)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gvMljmJOZmU)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Subscribe("event2", listener, false, 0, nil)
eb.ClearListenersByTopic("event1")
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
fmt.Println(receivedData)
// Output:
// 2
}
```
### <span id="GetListenersCount">GetListenersCount</span>
<p>获取特定事件的监听器数量。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) GetListenersCount(topic string) int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/j6yaJ2xAmFz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
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:
// 1
}
```
### <span id="GetAllListenersCount">GetAllListenersCount</span>
<p>获取所有事件的监听器数量。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) GetAllListenersCount() int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PUlr0xcpEOz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
count := eb.GetAllListenersCount()
fmt.Println(count)
// Output:
// 2
}
```
### <span id="GetEvents">GetEvents</span>
<p>获取所有事件的topic。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) GetEvents() []string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/etgjjcOtAjX)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
events := eb.GetEvents()
for _, event := range events {
fmt.Println(event)
}
// Output:
// event2
// event1
}
```
### <span id="SetErrorHandler">SetErrorHandler</span>
<p>设置事件的错误处理函数。</p>
<b>函数签名:</b>
```go
func (eb *EventBus[T]) SetErrorHandler(handler func(err error))
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gmB0gnFe5mc)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.SetErrorHandler(func(err error) {
fmt.Println(err)
})
eb.Subscribe("event1", func(eventData int) {
panic("error")
}, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
// Output:
// error
}
```

View File

@@ -1088,7 +1088,7 @@ func main() {
func GetExeOrDllVersion(filePath string) (string, error) func GetExeOrDllVersion(filePath string) (string, 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/iLRrDBhE38E)</span></b>
```go ```go
package main package main

View File

@@ -1178,7 +1178,7 @@ func main() {
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 func Variance[T constraints.Float | constraints.Integer](numbers []T) float64
``` ```
<b>示例:<span style="float:right;display:inline-block;">[示例](todo)</span></b> <b>示例:<span style="float:right;display:inline-block;">[示例](https://go.dev/play/p/uHuV4YgXf8F)</span></b>
```go ```go
package main package main
@@ -1211,7 +1211,7 @@ func main() {
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/FkNZDXvHD2l)</span></b>
```go ```go
package main package main
@@ -1222,8 +1222,8 @@ import (
) )
func main() { func main() {
result1 := mathutil.T运行cRound(mathutil.StdDev([]int{1, 2, 3, 4, 5}), 2) result1 := mathutil.TruncRound(mathutil.StdDev([]int{1, 2, 3, 4, 5}), 2)
result2 := mathutil.T运行cRound(mathutil.StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2) result2 := mathutil.TruncRound(mathutil.StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2)
fmt.Println(result1) fmt.Println(result1)
fmt.Println(result2) fmt.Println(result2)
@@ -1244,7 +1244,7 @@ func main() {
func Permutation(n, k uint) uint func Permutation(n, k uint) uint
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/MgobwH_FOxj)</span></b>
```go ```go
package main package main
@@ -1277,7 +1277,7 @@ func main() {
func Combination(n, k uint) uint func Combination(n, k uint) uint
``` ```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ENFQRDQUFi9)</span></b>
```go ```go
package main package main

View File

@@ -534,7 +534,7 @@ func main() {
func RandNumberOfLength(len int) int func RandNumberOfLength(len int) 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/oyZbuV7bu7b)</span></b>
```go ```go
package main package main

View File

@@ -44,6 +44,7 @@ import (
- [Every](#Every) - [Every](#Every)
- [Equal](#Equal) - [Equal](#Equal)
- [EqualWith](#EqualWith) - [EqualWith](#EqualWith)
- [EqualUnordered](#EqualUnordered)
- [Filter](#Filter) - [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent) - [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find) - [Find<sup>deprecated</sup>](#Find)
@@ -69,6 +70,7 @@ import (
- [FlatMap](#FlatMap) - [FlatMap](#FlatMap)
- [Merge](#Merge) - [Merge](#Merge)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [ReverseCopy](#ReverseCopy)
- [Reduce<sup>deprecated</sup>](#Reduce) - [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent) - [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy) - [ReduceBy](#ReduceBy)
@@ -77,6 +79,7 @@ import (
- [ReplaceAll](#ReplaceAll) - [ReplaceAll](#ReplaceAll)
- [Repeat](#Repeat) - [Repeat](#Repeat)
- [Shuffle](#Shuffle) - [Shuffle](#Shuffle)
- [ShuffleCopy](#ShuffleCopy)
- [IsAscending](#IsAscending) - [IsAscending](#IsAscending)
- [IsDescending](#IsDescending) - [IsDescending](#IsDescending)
- [IsSorted](#IsSorted) - [IsSorted](#IsSorted)
@@ -874,6 +877,37 @@ func main() {
} }
``` ```
### <span id="EqualUnordered">EqualUnordered</span>
<p>检查两个切片是否相等,元素数量相同,值相等,不考虑元素顺序。</p>
<b>函数签名:</b>
```go
func EqualUnordered[T comparable](slice1, slice2 []T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/n8fSc2w8ZgX)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result1 := slice.EqualUnordered([]int{1, 2, 3}, []int{3, 2, 1})
result2 := slice.EqualUnordered([]int{1, 2, 3}, []int{4, 5, 6})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="Filter">Filter</span> ### <span id="Filter">Filter</span>
<p>返回切片中通过predicate函数真值测试的所有元素</p> <p>返回切片中通过predicate函数真值测试的所有元素</p>
@@ -1728,6 +1762,38 @@ func main() {
} }
``` ```
### <span id="ReverseCopy">ReverseCopy</span>
<p>反转切片中的元素顺序, 不改变原slice。</p>
<b>函数签名:</b>
```go
func ReverseCopy[T any](slice []T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/c9arEaP7Cg-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "c", "d"}
reversedStrs := slice.ReverseCopy(strs)
fmt.Println(reversedStrs)
fmt.Println(strs)
// Output:
// [d c b a]
// [a b c d]
}
```
### <span id="Reduce">Reduce</span> ### <span id="Reduce">Reduce</span>
<p>将切片中的元素依次运行iteratee函数返回运行结果。</p> <p>将切片中的元素依次运行iteratee函数返回运行结果。</p>
@@ -1962,7 +2028,7 @@ func main() {
### <span id="Shuffle">Shuffle</span> ### <span id="Shuffle">Shuffle</span>
<p>随机打乱切片中的元素顺序</p> <p>随机打乱切片中的元素顺序</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -1989,6 +2055,37 @@ func main() {
} }
``` ```
### <span id="ShuffleCopy">ShuffleCopy</span>
<p>随机打乱切片中的元素顺序, 不改变原切片。</p>
<b>函数签名:</b>
```go
func ShuffleCopy[T any](slice []T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vqDa-Gs1vT0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
result := slice.ShuffleCopy(nums)
fmt.Println(result)
fmt.Println(nums)
// Output:
// [3 1 5 4 2] (random order)
// [1 2 3 4 5]
}
```
### <span id="IsAscending">IsAscending</span> ### <span id="IsAscending">IsAscending</span>
<p>检查切片元素是否按升序排列。</p> <p>检查切片元素是否按升序排列。</p>
@@ -2999,7 +3096,7 @@ func main() {
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string func JoinFunc[T any](slice []T, sep string, transform func(T) T) 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/55ib3SB5fM2)</span></b>
```go ```go
import ( import (
@@ -3029,7 +3126,7 @@ func main() {
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T func ConcatBy[T any](slice []T, sep T, connector func(T, T) 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/6QcUpcY4UMW)</span></b>
```go ```go
import ( import (

View File

@@ -951,7 +951,7 @@ func main() {
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) 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/tBV5Nc-XDX2)</span></b>
```go ```go
import ( import (
@@ -984,7 +984,7 @@ func main() {
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) 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/CjeoNw2eac_G)</span></b>
```go ```go
import ( import (

View File

@@ -69,6 +69,7 @@ import (
- [TemplateReplace](#TemplateReplace) - [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups) - [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#ExtractContent) - [ExtractContent](#ExtractContent)
- [FindAllOccurrences](#FindAllOccurrences)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1708,7 +1709,7 @@ func main() {
func RegexMatchAllGroups(pattern, str string) [][]string func RegexMatchAllGroups(pattern, str string) [][]string
``` ```
<b>示例:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JZiu0RXpgN-)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JZiu0RXpgN-)</span></b>
```go ```go
import ( import (
@@ -1741,7 +1742,7 @@ func main() {
func ExtractContent(s, start, end string) []string func ExtractContent(s, start, end string) []string
``` ```
<b>示例:<span style="float:right;display:inline-block;">[Run](todo)</span></b> <b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Ay9UIk7Rum9)</span></b>
```go ```go
import ( import (
@@ -1759,4 +1760,32 @@ func main() {
// Output: // Output:
// [content1 content2 content1] // [content1 content2 content1]
} }
```
### <span id="FindAllOccurrences">FindAllOccurrences</span>
<p>返回子字符串在字符串中所有出现的位置。</p>
<b>函数签名:</b>
```go
func FindAllOccurrences(str, substr string) []int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uvyA6azGLB1)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.FindAllOccurrences("ababab", "ab")
fmt.Println(result)
// Output:
// [0 2 4]
}
``` ```

View File

@@ -49,6 +49,7 @@ import (
- [IsIp](#IsIp) - [IsIp](#IsIp)
- [IsIpV4](#IsIpV4) - [IsIpV4](#IsIpV4)
- [IsIpV6](#IsIpV6) - [IsIpV6](#IsIpV6)
- [IsIpPort](#IsIpPort)
- [IsStrongPassword](#IsStrongPassword) - [IsStrongPassword](#IsStrongPassword)
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
@@ -990,6 +991,43 @@ func main() {
} }
``` ```
### <span id="IsIpPort">IsIpPort</span>
<p>检查字符串是否是ip:port格式。</p>
<b>函数签名:</b>
```go
func IsIpPort(str string) bool
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/xUmls_b9L29)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsIpPort("127.0.0.1:8080")
result2 := validator.IsIpPort("[0:0:0:0:0:0:0:1]:8080")
result3 := validator.IsIpPort(":8080")
result4 := validator.IsIpPort("::0:0:0:0:")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsStrongPassword">IsStrongPassword</span> ### <span id="IsStrongPassword">IsStrongPassword</span>
<p>验证字符串是否是强密码:(alpha(lower+upper) + number + special chars(!@#$%^&*()?&gt&lt))。</p> <p>验证字符串是否是强密码:(alpha(lower+upper) + number + special chars(!@#$%^&*()?&gt&lt))。</p>

View File

@@ -509,7 +509,7 @@ func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
func (tc *TryCatch) Do() func (tc *TryCatch) Do()
``` ```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b> <b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/D5Mdb0mRj0P)</span></b>
```go ```go
package main package main
@@ -529,8 +529,6 @@ func main() {
return errors.New("error in try block") return errors.New("error in try block")
}).Catch(func(ctx context.Context, err error) { }).Catch(func(ctx context.Context, err error) {
calledCatch = true calledCatch = true
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
// fmt.Println(err.Error())
}).Finally(func(ctx context.Context) { }).Finally(func(ctx context.Context) {
calledFinally = true calledFinally = true
}).Do() }).Do()

View File

@@ -373,7 +373,7 @@ import (
func main() { func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3} aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result, err := ToJson(aMap) result, err := convertor.ToJson(aMap)
if err != nil { if err != nil {
fmt.Printf("%v", err) fmt.Printf("%v", err)
@@ -1123,7 +1123,7 @@ func main() {
<p>Converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int</p> <p>Converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int</p>
<b>Signature:<span style="float:right;display:inline-block;">[Run](todo)</span></b> <b>Signature:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/X3itkCxwB_x)</span></b>
```go ```go
func ToBigInt[T any](v T) (*big.Int, error) func ToBigInt[T any](v T) (*big.Int, error)

View File

@@ -1435,7 +1435,7 @@ func main() {
func RsaEncrypt(data []byte, pubKeyFileName string) []byte func RsaEncrypt(data []byte, pubKeyFileName string) []byte
``` ```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uef0q1fz53I)</span></b> <b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7_zo6mrx-eX)</span></b>
```go ```go
package main package main
@@ -1472,7 +1472,7 @@ func main() {
func RsaDecrypt(data []byte, privateKeyFileName string) []byte func RsaDecrypt(data []byte, privateKeyFileName string) []byte
``` ```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uef0q1fz53I)</span></b> <b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7_zo6mrx-eX)</span></b>
```go ```go
package main package main
@@ -1620,7 +1620,7 @@ func main() {
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, 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/qhsbf8BJ6Mf)</span></b>
```go ```go
package main package main
@@ -1659,7 +1659,7 @@ func main() {
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) 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/qhsbf8BJ6Mf)</span></b>
```go ```go
package main package main

View File

@@ -24,9 +24,14 @@ import (
## Index ## Index
- [AddDay](#AddDay) - [AddDay](#AddDay)
- [AddWeek](#AddWeek)
- [AddMonth](#AddMonth)
- [AddHour](#AddHour) - [AddHour](#AddHour)
- [AddMinute](#AddMinute) - [AddMinute](#AddMinute)
- [AddYear](#AddYear) - [AddYear](#AddYear)
- [AddDaySafe](#AddDaySafe)
- [AddMonthSafe](#AddMonthSafe)
- [AddYearSafe](#AddYearSafe)
- [BeginOfMinute](#BeginOfMinute) - [BeginOfMinute](#BeginOfMinute)
- [BeginOfHour](#BeginOfHour) - [BeginOfHour](#BeginOfHour)
- [BeginOfDay](#BeginOfDay) - [BeginOfDay](#BeginOfDay)
@@ -111,7 +116,7 @@ import (
<b>Signature:</b> <b>Signature:</b>
```go ```go
func AddDay(t time.Time, day int64) time.Time func AddDay(t time.Time, days int64) time.Time
``` ```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dIGbs_uTdFa)</span></b> <b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dIGbs_uTdFa)</span></b>
@@ -126,20 +131,89 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
tomorrow := datetime.AddDay(now, 1) after1Day := datetime.AddDay(date, 1)
diff1 := tomorrow.Sub(now) before1Day := datetime.AddDay(date, -1)
yesterday := datetime.AddDay(now, -1) fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
diff2 := yesterday.Sub(now) fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 24h0m0s // 2021-01-02 00:00:00
// -24h0m0s // 2020-12-31 00:00:00
}
```
### <span id="AddWeek">AddWeek</span>
<p>Add or sub weeks to time.</p>
<b>Signature:</b>
```go
func AddWeek(t time.Time, weeks int64) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/M9TqdMiaA2p)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Weeks := datetime.AddWeek(date, 2)
before2Weeks := datetime.AddWeek(date, -2)
fmt.Println(after2Weeks.Format("2006-01-02"))
fmt.Println(before2Weeks.Format("2006-01-02"))
// Output:
// 2021-01-15
// 2020-12-18
}
```
### <span id="AddMonth">AddMonth</span>
<p>Add or sub months to time.</p>
<b>Signature:</b>
```go
func AddMonth(t time.Time, months int64) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/DLoiOnpLvsN)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2021-01-01")
after2Months := datetime.AddMonth(date, 2)
before2Months := datetime.AddMonth(date, -2)
fmt.Println(after2Months.Format("2006-01-02"))
fmt.Println(before2Months.Format("2006-01-02"))
// Output:
// 2021-03-01
// 2020-11-01
} }
``` ```
@@ -165,20 +239,17 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Hours := datetime.AddHour(now, 2) after2Hours := datetime.AddHour(date, 2)
diff1 := after2Hours.Sub(now) before2Hours := datetime.AddHour(date, -2)
before2Hours := datetime.AddHour(now, -2) fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
diff2 := before2Hours.Sub(now) fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2h0m0s // 2021-01-01 02:00:00
// -2h0m0s // 2020-12-31 22:00:00
} }
``` ```
@@ -204,20 +275,17 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Minutes := datetime.AddMinute(now, 2) after2Minutes := datetime.AddMinute(date, 2)
diff1 := after2Minutes.Sub(now) before2Minutes := datetime.AddMinute(date, -2)
before2Minutes := datetime.AddMinute(now, -2) fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
diff2 := before2Minutes.Sub(now) fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 2m0s // 2021-01-01 00:02:00
// -2m0s // 2020-12-31 23:58:00
} }
``` ```
@@ -243,20 +311,137 @@ import (
) )
func main() { func main() {
now := time.Now() date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := datetime.AddYear(now, 1) after2Years := AddYear(date, 2)
diff1 := after1Year.Sub(now) before2Years := AddYear(date, -2)
before1Year := datetime.AddYear(now, -1) fmt.Println(after2Years.Format("2006-01-02"))
diff2 := before1Year.Sub(now) fmt.Println(before2Years.Format("2006-01-02"))
fmt.Println(diff1)
fmt.Println(diff2)
// Output: // Output:
// 8760h0m0s // 2023-01-01
// -8760h0m0s // 2019-01-01
}
```
### <span id="AddDaySafe">AddDaySafe</span>
<p>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.</p>
<b>Signature:</b>
```go
func AddDaySafe(t time.Time, days int) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JTohZFpoDJ3)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
leapYearDate1, _ := time.Parse("2006-01-02", "2024-02-29")
result1 := datetime.AddDaySafe(leapYearDate1, 1)
leapYearDate2, _ := time.Parse("2006-01-02", "2024-03-01")
result2 := datetime.AddDaySafe(leapYearDate2, -1)
nonLeapYearDate1, _ := time.Parse("2006-01-02", "2025-02-28")
result3 := datetime.AddDaySafe(nonLeapYearDate1, 1)
nonLeaYearDate2, _ := time.Parse("2006-01-02", "2025-03-01")
result4 := datetime.AddDaySafe(nonLeaYearDate2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
fmt.Println(result3.Format("2006-01-02"))
fmt.Println(result4.Format("2006-01-02"))
// Output:
// 2024-03-01
// 2024-02-29
// 2025-03-01
// 2025-02-28
}
```
### <span id="AddMonthSafe">AddMonthSafe</span>
<p>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.</p>
<b>Signature:</b>
```go
func AddMonthSafe(t time.Time, months int) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KLw0lo6mbVW)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date1, _ := time.Parse("2006-01-02", "2025-01-31")
result1 := datetime.AddMonthSafe(date1, 1)
date2, _ := time.Parse("2006-01-02", "2024-02-29")
result2 := datetime.AddMonthSafe(date2, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2025-02-28
// 2024-01-29
}
```
### <span id="AddYearSafe">AddYearSafe</span>
<p>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.</p>
<b>Signature:</b>
```go
func AddYearSafe(t time.Time, years int) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KVGXWZZ54ZH)</span></b>
```go
package main
import (
"fmt"
"time"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
date, _ := time.Parse("2006-01-02", "2020-02-29")
result1 := datetime.AddYearSafe(date, 1)
result2 := datetime.AddYearSafe(date, -1)
fmt.Println(result1.Format("2006-01-02"))
fmt.Println(result2.Format("2006-01-02"))
// Output:
// 2021-02-28
// 2019-02-28
} }
``` ```
@@ -1342,7 +1527,7 @@ import (
func main() { func main() {
result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss") result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss")
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST") result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
fmt.Println(result1) fmt.Println(result1)
fmt.Println(result2) fmt.Println(result2)
@@ -1586,7 +1771,7 @@ func main() {
func Min(t1 time.Time, times ...time.Time) time.Time func Min(t1 time.Time, times ...time.Time) 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/MCIDvHNOGGb)</span></b>
```go ```go
package main package main
@@ -1616,7 +1801,7 @@ func main() {
func Max(t1 time.Time, times ...time.Time) time.Time func Max(t1 time.Time, times ...time.Time) 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/9m6JMk1LB7-)</span></b>
```go ```go
package main package main
@@ -1646,7 +1831,7 @@ func main() {
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime 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/rbW51cDtM_2)</span></b>
```go ```go
package main package main

View File

@@ -0,0 +1,407 @@
# EventBus
EventBus is an event bus used for handling events within an application.
<div STYLE="page-break-after: always;"></div>
## Source:
- [https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go](https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go)
<div STYLE="page-break-after: always;"></div>
## Usage:
```go
import (
"github.com/duke-git/lancet/v2/eventbus"
)
```
<div STYLE="page-break-after: always;"></div>
## Index
- [NewEventBus](#NewEventBus)
- [Subscribe](#Subscribe)
- [Unsubscribe](#Unsubscribe)
- [Publish](#Publish)
- [ClearListeners](#ClearListeners)
- [ClearListenersByTopic](#ClearListenersByTopic)
- [GetListenersCount](#GetListenersCount)
- [GetAllListenersCount](#GetAllListenersCount)
- [GetEvents](#GetEvents)
- [SetErrorHandler](#SetErrorHandler)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="NewEventBus">NewEventBus</span>
<p>Create an EventBus instance.</p>
<b>Signature:</b>
```go
func NewEventBus[T any]() *EventBus[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gHbOPV_NUOJ)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 1
}
```
### <span id="Subscribe">Subscribe</span>
<p>Subscribes to an event with a specific event topic and listener function.</p>
<b>Signature:</b>
```go
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](https://go.dev/play/p/EYGf_8cHei-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
filter := func(eventData int) bool {
return eventData == 1
}
eb.Subscribe("event1", listener, false, 0, filter)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 2})
fmt.Println(receivedData)
// Output:
// 1
}
```
### <span id="Unsubscribe">Unsubscribe</span>
<p>Unsubscribes from an event with a specific event topic and listener function.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T))
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Tmh7Ttfvprf)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Unsubscribe("event1", listener)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 0
}
```
### <span id="Publish">Publish</span>
<p>Publishes an event with a specific event topic and data payload.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) Publish(event eventbus.Event[T])
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gHTtVexFSH9)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {
fmt.Println(eventData)
}, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
// Output:
// 1
}
```
### <span id="ClearListeners">ClearListeners</span>
<p>Clears all the listeners.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) ClearListeners()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KBfBYlKPgqD)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Subscribe("event2", listener, false, 0, nil)
eb.ClearListeners()
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
fmt.Println(receivedData)
// Output:
// 0
}
```
### <span id="ClearListenersByTopic">ClearListenersByTopic</span>
<p>Clears all the listeners by topic.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) ClearListenersByTopic(topic string)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gvMljmJOZmU)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Subscribe("event2", listener, false, 0, nil)
eb.ClearListenersByTopic("event1")
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
eb.Publish(eventbus.Event[int]{Topic: "event2", Payload: 2})
fmt.Println(receivedData)
// Output:
// 2
}
```
### <span id="GetListenersCount">GetListenersCount</span>
<p>Returns the number of listeners for a specific event topic.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) GetListenersCount(topic string) int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j6yaJ2xAmFz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
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:
// 1
}
```
### <span id="GetAllListenersCount">GetAllListenersCount</span>
<p>Returns the total number of all listeners.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) GetAllListenersCount() int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/PUlr0xcpEOz)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
count := eb.GetAllListenersCount()
fmt.Println(count)
// Output:
// 2
}
```
### <span id="GetEvents">GetEvents</span>
<p>Returns all the events topics.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) GetEvents() []string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/etgjjcOtAjX)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
events := eb.GetEvents()
for _, event := range events {
fmt.Println(event)
}
// Output:
// event2
// event1
}
```
### <span id="SetErrorHandler">SetErrorHandler</span>
<p>Sets the error handler function.</p>
<b>Signature:</b>
```go
func (eb *EventBus[T]) SetErrorHandler(handler func(err error))
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gmB0gnFe5mc)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/eventbus"
)
func main() {
eb := eventbus.NewEventBus[int]()
eb.SetErrorHandler(func(err error) {
fmt.Println(err)
})
eb.Subscribe("event1", func(eventData int) {
panic("error")
}, false, 0, nil)
eb.Publish(eventbus.Event[int]{Topic: "event1", Payload: 1})
// Output:
// error
}
```

View File

@@ -1087,7 +1087,7 @@ func main() {
func GetExeOrDllVersion(filePath string) (string, error) func GetExeOrDllVersion(filePath string) (string, 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/iLRrDBhE38E)</span></b>
```go ```go
package main package main

View File

@@ -1177,7 +1177,7 @@ func main() {
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 func Variance[T constraints.Float | constraints.Integer](numbers []T) float64
``` ```
<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/uHuV4YgXf8F)</span></b>
```go ```go
package main package main
@@ -1210,7 +1210,7 @@ func main() {
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64
``` ```
<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/FkNZDXvHD2l)</span></b>
```go ```go
package main package main
@@ -1243,7 +1243,7 @@ func main() {
func Permutation(n, k uint) uint func Permutation(n, k uint) uint
``` ```
<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/MgobwH_FOxj)</span></b>
```go ```go
package main package main
@@ -1276,7 +1276,7 @@ func main() {
func Combination(n, k uint) uint func Combination(n, k uint) uint
``` ```
<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/ENFQRDQUFi9)</span></b>
```go ```go
package main package main

View File

@@ -536,7 +536,7 @@ func main() {
func RandNumberOfLength(len int) int func RandNumberOfLength(len int) int
``` ```
<b>Signature:<span style="float:right;display:inline-block;">[Run](todo)</span></b> <b>Signature:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/oyZbuV7bu7b)</span></b>
```go ```go
package main package main

View File

@@ -43,6 +43,7 @@ import (
- [DropRightWhile](#DropRightWhile) - [DropRightWhile](#DropRightWhile)
- [Equal](#Equal) - [Equal](#Equal)
- [EqualWith](#EqualWith) - [EqualWith](#EqualWith)
- [EqualUnordered](#EqualUnordered)
- [Every](#Every) - [Every](#Every)
- [Filter](#Filter) - [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent) - [FilterConcurrent](#FilterConcurrent)
@@ -69,6 +70,7 @@ import (
- [FlatMap](#FlatMap) - [FlatMap](#FlatMap)
- [Merge](#Merge) - [Merge](#Merge)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [ReverseCopy](#ReverseCopy)
- [Reduce<sup>deprecated</sup>](#Reduce) - [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent) - [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy) - [ReduceBy](#ReduceBy)
@@ -77,6 +79,7 @@ import (
- [ReplaceAll](#ReplaceAll) - [ReplaceAll](#ReplaceAll)
- [Repeat](#Repeat) - [Repeat](#Repeat)
- [Shuffle](#Shuffle) - [Shuffle](#Shuffle)
- [ShuffleCopy](#ShuffleCopy)
- [IsAscending](#IsAscending) - [IsAscending](#IsAscending)
- [IsDescending](#IsDescending) - [IsDescending](#IsDescending)
- [IsSorted](#IsSorted) - [IsSorted](#IsSorted)
@@ -839,6 +842,37 @@ func main() {
} }
``` ```
### <span id="EqualUnordered">EqualUnordered</span>
<p>Checks if two slices are equal: the same length and all elements value are equal (unordered).</p>
<b>Signature:</b>
```go
func EqualUnordered[T comparable](slice1, slice2 []T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/n8fSc2w8ZgX)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result1 := slice.EqualUnordered([]int{1, 2, 3}, []int{3, 2, 1})
result2 := slice.EqualUnordered([]int{1, 2, 3}, []int{4, 5, 6})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="Every">Every</span> ### <span id="Every">Every</span>
<p>Return true if all of the values in the slice pass the predicate function.</p> <p>Return true if all of the values in the slice pass the predicate function.</p>
@@ -1724,6 +1758,38 @@ func main() {
} }
``` ```
### <span id="ReverseCopy">ReverseCopy</span>
<p>Return a new slice of element order is reversed to the given slice.</p>
<b>Signature:</b>
```go
func ReverseCopy[T any](slice []T) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/c9arEaP7Cg-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "c", "d"}
reversedStrs := slice.ReverseCopy(strs)
fmt.Println(reversedStrs)
fmt.Println(strs)
// Output:
// [d c b a]
// [a b c d]
}
```
### <span id="Reduce">Reduce</span> ### <span id="Reduce">Reduce</span>
<p>Reduce slice.</p> <p>Reduce slice.</p>
@@ -1986,6 +2052,37 @@ func main() {
} }
``` ```
### <span id="ShuffleCopy">ShuffleCopy</span>
<p>Return a new slice with elements shuffled.</p>
<b>Signature:</b>
```go
func ShuffleCopy[T any](slice []T) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vqDa-Gs1vT0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
result := slice.ShuffleCopy(nums)
fmt.Println(result)
fmt.Println(nums)
// Output:
// [3 1 5 4 2] (random order)
// [1 2 3 4 5]
}
```
### <span id="IsAscending">IsAscending</span> ### <span id="IsAscending">IsAscending</span>
<p>Checks if a slice is ascending order.</p> <p>Checks if a slice is ascending order.</p>
@@ -2995,7 +3092,7 @@ func main() {
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string func JoinFunc[T any](slice []T, sep string, transform func(T) T) 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/55ib3SB5fM2)</span></b>
```go ```go
import ( import (
@@ -3025,7 +3122,7 @@ func main() {
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T func ConcatBy[T any](slice []T, sep T, connector func(T, T) 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/6QcUpcY4UMW)</span></b>
```go ```go
import ( import (

View File

@@ -950,7 +950,7 @@ func main() {
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) 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/tBV5Nc-XDX2)</span></b>
```go ```go
import ( import (
@@ -983,7 +983,7 @@ func main() {
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) 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/CjeoNw2eac_G)</span></b>
```go ```go
import ( import (

View File

@@ -68,7 +68,8 @@ import (
- [Rotate](#Rotate) - [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace) - [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups) - [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#RegexMatchAllGroups) - [ExtractContent](#ExtractContent)
- [FindAllOccurrences](#FindAllOccurrences)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1743,7 +1744,7 @@ func main() {
func ExtractContent(s, start, end string) []string func ExtractContent(s, start, end string) []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/Ay9UIk7Rum9)</span></b>
```go ```go
import ( import (
@@ -1761,4 +1762,32 @@ func main() {
// Output: // Output:
// [content1 content2 content1] // [content1 content2 content1]
} }
```
### <span id="FindAllOccurrences">FindAllOccurrences</span>
<p>Returns the positions of all occurrences of a substring in a string.</p>
<b>Signature:</b>
```go
func FindAllOccurrences(str, substr string) []int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uvyA6azGLB1)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.FindAllOccurrences("ababab", "ab")
fmt.Println(result)
// Output:
// [0 2 4]
}
``` ```

View File

@@ -49,6 +49,7 @@ import (
- [IsIp](#IsIp) - [IsIp](#IsIp)
- [IsIpV4](#IsIpV4) - [IsIpV4](#IsIpV4)
- [IsIpV6](#IsIpV6) - [IsIpV6](#IsIpV6)
- [IsIpPort](#IsIpPort)
- [IsStrongPassword](#IsStrongPassword) - [IsStrongPassword](#IsStrongPassword)
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
@@ -992,6 +993,43 @@ func main() {
} }
``` ```
### <span id="IsIpPort">IsIpPort</span>
<p>Check if the string is ip:port</p>
<b>Signature:</b>
```go
func IsIpPort(str string) bool
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/xUmls_b9L29)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsIpPort("127.0.0.1:8080")
result2 := validator.IsIpPort("[0:0:0:0:0:0:0:1]:8080")
result3 := validator.IsIpPort(":8080")
result4 := validator.IsIpPort("::0:0:0:0:")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
```
### <span id="IsStrongPassword">IsStrongPassword</span> ### <span id="IsStrongPassword">IsStrongPassword</span>
<p>Check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?gt&lt)).</p> <p>Check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?gt&lt)).</p>

View File

@@ -507,7 +507,7 @@ func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
func (tc *TryCatch) Do() func (tc *TryCatch) Do()
``` ```
<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/D5Mdb0mRj0P)</span></b>
```go ```go
package main package main

View File

@@ -10,7 +10,7 @@ outline: deep
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
``` ```
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.5. </b> 2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.6. </b>
```go ```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x

View File

@@ -10,7 +10,7 @@ outline: deep
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
``` ```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.5。</b> 2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。</b>
```go ```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x

195
eventbus/eventbus.go Normal file
View File

@@ -0,0 +1,195 @@
// Copyright 2025 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package eventbus implements a simple event bus.
package eventbus
import (
"fmt"
"sort"
"sync"
)
// Event is the struct that is passed to the event listener, now it directly uses the generic Payload type.
type Event[T any] struct {
Topic string
Payload T
}
// EventBus is the struct that holds the listeners and the error handler.
type EventBus[T any] struct {
// listeners map[string][]*EventListener[T]
listeners sync.Map
mu sync.RWMutex
errorHandler func(err error)
}
// EventListener is the struct that holds the listener function and its priority.
type EventListener[T any] struct {
priority int
listener func(eventData T)
async bool
filter func(eventData T) bool
}
// NewEventBus creates a new EventBus.
// Play: https://go.dev/play/p/gHbOPV_NUOJ
func NewEventBus[T any]() *EventBus[T] {
return &EventBus[T]{
listeners: sync.Map{},
}
}
// Subscribe subscribes to an event with a specific event topic and listener function.
// 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()
el := &EventListener[T]{
priority: priority,
listener: listener,
async: async,
filter: filter,
}
listenersInterface, _ := eb.listeners.LoadOrStore(topic, []*EventListener[T]{})
listeners := listenersInterface.([]*EventListener[T])
listeners = append(listeners, el)
sort.Slice(listeners, func(i, j int) bool {
return listeners[i].priority > listeners[j].priority
})
eb.listeners.Store(topic, listeners)
}
// Unsubscribe unsubscribes from an event with a specific event topic and listener function.
// 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()
listenersInterface, ok := eb.listeners.Load(topic)
if !ok {
return
}
listeners := listenersInterface.([]*EventListener[T])
listenerPtr := fmt.Sprintf("%p", listener)
var updatedListeners []*EventListener[T]
for _, l := range listeners {
if fmt.Sprintf("%p", l.listener) != listenerPtr {
updatedListeners = append(updatedListeners, l)
}
}
eb.listeners.Store(topic, updatedListeners)
}
// Publish publishes an event with a specific event topic and data payload.
// Play: https://go.dev/play/p/gHTtVexFSH9
func (eb *EventBus[T]) Publish(event Event[T]) {
eb.mu.RLock()
defer eb.mu.RUnlock()
listenersInterface, exists := eb.listeners.Load(event.Topic)
if !exists {
return
}
listeners := listenersInterface.([]*EventListener[T])
for _, listener := range listeners {
if listener.filter != nil && !listener.filter(event.Payload) {
continue
}
if listener.async {
go eb.publishToListener(listener, event)
} else {
eb.publishToListener(listener, event)
}
}
}
func (eb *EventBus[T]) publishToListener(listener *EventListener[T], event Event[T]) {
defer func() {
if r := recover(); r != nil && eb.errorHandler != nil {
eb.errorHandler(fmt.Errorf("%v", r))
}
}()
listener.listener(event.Payload)
}
// 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: https://go.dev/play/p/KBfBYlKPgqD
func (eb *EventBus[T]) ClearListeners() {
eb.mu.Lock()
defer eb.mu.Unlock()
eb.listeners = sync.Map{}
}
// ClearListenersByTopic clears all the listeners by topic.
// Play: https://go.dev/play/p/gvMljmJOZmU
func (eb *EventBus[T]) ClearListenersByTopic(topic string) {
eb.mu.Lock()
defer eb.mu.Unlock()
eb.listeners.Delete(topic)
}
// GetListenersCount returns the number of listeners for a specific event topic.
// Play: https://go.dev/play/p/8VPJsMQgStM
func (eb *EventBus[T]) GetListenersCount(topic string) int {
eb.mu.RLock()
defer eb.mu.RUnlock()
listenersInterface, ok := eb.listeners.Load(topic)
if !ok {
return 0
}
listeners := listenersInterface.([]*EventListener[T])
return len(listeners)
}
// GetAllListenersCount returns the total number of listeners.
// Play: https://go.dev/play/p/PUlr0xcpEOz
func (eb *EventBus[T]) GetAllListenersCount() int {
eb.mu.RLock()
defer eb.mu.RUnlock()
count := 0
eb.listeners.Range(func(key, value interface{}) bool {
count += len(value.([]*EventListener[T]))
return true
})
return count
}
// GetEvents returns all the events topics.
// Play: https://go.dev/play/p/etgjjcOtAjX
func (eb *EventBus[T]) GetEvents() []string {
eb.mu.RLock()
defer eb.mu.RUnlock()
var events []string
eb.listeners.Range(func(key, value interface{}) bool {
events = append(events, key.(string))
return true
})
return events
}

View File

@@ -0,0 +1,235 @@
package eventbus
import (
"fmt"
"sync"
"time"
)
func ExampleEventBus_Subscribe() {
eb := NewEventBus[string]()
eb.Subscribe("event1", func(eventData string) {
fmt.Println(eventData)
}, false, 0, nil)
eb.Publish(Event[string]{Topic: "event1", Payload: "hello"})
// Output:
// hello
}
func ExampleEventBus_Unsubscribe() {
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Unsubscribe("event1", listener)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 0
}
func ExampleEventBus_Subscribe_withFilter() {
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
filter := func(eventData int) bool {
return eventData == 1
}
eb.Subscribe("event1", listener, false, 0, filter)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
eb.Publish(Event[int]{Topic: "event1", Payload: 2})
fmt.Println(receivedData)
// Output:
// 1
}
func ExampleEventBus_Subscribe_withPriority() {
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {
fmt.Println(eventData)
}, false, 0, nil)
eb.Subscribe("event1", func(eventData int) {
fmt.Println(eventData)
}, false, 1, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
// Output:
// 1
// 1
}
func ExampleEventBus_Subscribe_async() {
eb := NewEventBus[int]()
var wg sync.WaitGroup
wg.Add(1)
eb.Subscribe("event1", func(eventData int) {
time.Sleep(100 * time.Millisecond)
fmt.Println(eventData)
wg.Done()
}, true, 1, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
wg.Wait()
// Output:
// 1
}
func ExampleEventBus_Publish() {
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {
fmt.Println(eventData)
}, false, 0, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
// Output:
// 1
}
func ExampleEventBus() {
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
fmt.Println(receivedData)
// Output:
// 1
}
func ExampleEventBus_ClearListeners() {
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
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)
// Output:
// 0
}
func ExampleEventBus_ClearListenersByTopic() {
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
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:
// 2
}
func ExampleEventBus_GetListenersCount() {
eb := NewEventBus[int]()
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:
// 1
}
func ExampleEventBus_SetErrorHandler() {
eb := NewEventBus[int]()
eb.SetErrorHandler(func(err error) {
fmt.Println(err)
})
eb.Subscribe("event1", func(eventData int) {
panic("error")
}, false, 0, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
// Output:
// error
}
func ExampleEventBus_GetAllListenersCount() {
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
count := eb.GetAllListenersCount()
fmt.Println(count)
// Output:
// 2
}
func ExampleEventBus_GetEvents() {
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
events := eb.GetEvents()
for _, event := range events {
fmt.Println(event)
}
// Output:
// event1
// event2
}

219
eventbus/eventbus_test.go Normal file
View File

@@ -0,0 +1,219 @@
package eventbus
import (
"sync"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
func TestEventBus_Subscribe(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_Subscribe")
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {
assert.Equal(1, eventData)
}, false, 0, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
}
func TestEventBus_Unsubscribe(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_Unsubscribe")
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
eb.Subscribe("event1", listener, false, 0, nil)
eb.Unsubscribe("event1", listener)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
assert.Equal(0, receivedData)
}
func TestEventBus_Subscribe_withFilter(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_Subscribe_withFilter")
eb := NewEventBus[int]()
receivedData := 0
listener := func(eventData int) {
receivedData = eventData
}
filter := func(eventData int) bool {
return eventData == 1
}
eb.Subscribe("event1", listener, false, 0, filter)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
eb.Publish(Event[int]{Topic: "event1", Payload: 2})
assert.Equal(1, receivedData)
}
func TestEventBus_Subscribe_withPriority(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_Subscribe_withPriority")
eb := NewEventBus[int]()
var receivedData []int
listener1 := func(eventData int) {
receivedData = append(receivedData, 1)
}
listener2 := func(eventData int) {
receivedData = append(receivedData, 2)
}
eb.Subscribe("event1", listener1, false, 1, nil)
eb.Subscribe("event1", listener2, false, 2, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
assert.Equal([]int{2, 1}, receivedData)
}
func TestEventBus_Subscribe_async(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_Subscribe_async")
eb := NewEventBus[string]()
var wg sync.WaitGroup
wg.Add(1)
eb.Subscribe("event1", func(eventData string) {
time.Sleep(100 * time.Millisecond)
assert.Equal("hello", eventData)
wg.Done()
}, true, 1, nil)
eb.Publish(Event[string]{Topic: "event1", Payload: "hello"})
wg.Wait()
}
func TestEventBus_ErrorHandler(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_ErrorHandler")
eb := NewEventBus[string]()
eb.SetErrorHandler(func(err error) {
assert.Equal("error", err.Error())
})
eb.Subscribe("event1", func(eventData string) {
panic("error")
}, false, 0, nil)
eb.Publish(Event[string]{Topic: "event1", Payload: "hello"})
}
func TestEventBus_ClearListeners(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_ClearListeners")
eb := NewEventBus[int]()
receivedData1 := 0
receivedData2 := 0
eb.Subscribe("event1", func(eventData int) {
receivedData1 = eventData
}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {
receivedData2 = eventData
}, false, 0, nil)
eb.ClearListeners()
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
eb.Publish(Event[int]{Topic: "event1", Payload: 2})
assert.Equal(0, receivedData1)
assert.Equal(0, receivedData2)
}
func TestEventBus_ClearListenersByTopic(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_ClearListenersByTopic")
eb := NewEventBus[int]()
receivedData1 := 0
receivedData2 := 0
eb.Subscribe("event1", func(eventData int) {
receivedData1 = eventData
}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {
receivedData2 = eventData
}, false, 0, nil)
eb.ClearListenersByTopic("event1")
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
eb.Publish(Event[int]{Topic: "event2", Payload: 2})
assert.Equal(0, receivedData1)
assert.Equal(2, receivedData2)
}
func TestEventBus_GetListenersCount(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_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)
assert.Equal(2, eb.GetListenersCount("event1"))
assert.Equal(1, eb.GetListenersCount("event2"))
}
func TestEventBus_GetAllListenersCount(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_GetAllListenersCount")
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)
assert.Equal(3, eb.GetAllListenersCount())
}
func TestEventBus_GetEvents(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEventBus_GetEvents")
eb := NewEventBus[int]()
eb.Subscribe("event1", func(eventData int) {}, false, 0, nil)
eb.Subscribe("event2", func(eventData int) {}, false, 0, nil)
events := eb.GetEvents()
assert.Equal(2, len(events))
assert.EqualValues([]string{"event1", "event2"}, events)
}

View File

@@ -25,6 +25,8 @@ import (
"sync" "sync"
"github.com/duke-git/lancet/v2/validator" "github.com/duke-git/lancet/v2/validator"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
) )
// FileReader is a reader supporting offset seeking and reading one // FileReader is a reader supporting offset seeking and reading one
@@ -422,8 +424,15 @@ func UnZip(zipFile string, destPath string) error {
defer zipReader.Close() defer zipReader.Close()
for _, f := range zipReader.File { for _, f := range zipReader.File {
decodeName := f.Name
if f.Flags == 0 {
i := bytes.NewReader([]byte(f.Name))
decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder())
content, _ := io.ReadAll(decoder)
decodeName = string(content)
}
// issue#62: fix ZipSlip bug // issue#62: fix ZipSlip bug
path, err := safeFilepathJoin(destPath, f.Name) path, err := safeFilepathJoin(destPath, decodeName)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -26,7 +26,7 @@ type tagVS_FIXEDFILEINFO struct {
} }
// GetExeOrDllVersion get the version of exe or dll file on windows. // GetExeOrDllVersion get the version of exe or dll file on windows.
// Play: todo // Play: https://go.dev/play/p/iLRrDBhE38E
func GetExeOrDllVersion(filePath string) (string, error) { func GetExeOrDllVersion(filePath string) (string, error) {
// 加载系统dll // 加载系统dll
versionDLL := syscall.NewLazyDLL("version.dll") versionDLL := syscall.NewLazyDLL("version.dll")

View File

@@ -145,10 +145,14 @@ func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
// Max return max value of numbers. // Max return max value of numbers.
// Play: https://go.dev/play/p/cN8DHI0rTkH // Play: https://go.dev/play/p/cN8DHI0rTkH
func Max[T constraints.Integer | constraints.Float](numbers ...T) T { func Max[T constraints.Ordered](items ...T) T {
max := numbers[0] if len(items) < 1 {
panic("mathutil.Max: empty list")
}
for _, v := range numbers { max := items[0]
for _, v := range items {
if max < v { if max < v {
max = v max = v
} }
@@ -181,10 +185,14 @@ func MaxBy[T any](slice []T, comparator func(T, T) bool) T {
// Min return min value of numbers. // Min return min value of numbers.
// Play: https://go.dev/play/p/21BER_mlGUj // Play: https://go.dev/play/p/21BER_mlGUj
func Min[T constraints.Integer | constraints.Float](numbers ...T) T { func Min[T constraints.Ordered](items ...T) T {
min := numbers[0] if len(items) < 1 {
panic("mathutil.min: empty list")
}
for _, v := range numbers { min := items[0]
for _, v := range items {
if min > v { if min > v {
min = v min = v
} }
@@ -400,7 +408,7 @@ func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
} }
// Variance returns the variance of numbers. // Variance returns the variance of numbers.
// Play: todo // Play: https://go.dev/play/p/uHuV4YgXf8F
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 { func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 {
n := len(numbers) n := len(numbers)
if n == 0 { if n == 0 {
@@ -418,13 +426,13 @@ func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 {
} }
// StdDev returns the standard deviation of numbers. // StdDev returns the standard deviation of numbers.
// Play: todo // Play: https://go.dev/play/p/FkNZDXvHD2l
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 { func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 {
return math.Sqrt(Variance(numbers)) return math.Sqrt(Variance(numbers))
} }
// Permutation calculate P(n, k). // Permutation calculate P(n, k).
// Play: todo // Play: https://go.dev/play/p/MgobwH_FOxj
func Permutation(n, k uint) uint { func Permutation(n, k uint) uint {
if n < k { if n < k {
return 0 return 0
@@ -437,7 +445,7 @@ func Permutation(n, k uint) uint {
} }
// Combination calculate C(n, k). // Combination calculate C(n, k).
// Play: todo // Play: https://go.dev/play/p/ENFQRDQUFi9
func Combination(n, k uint) uint { func Combination(n, k uint) uint {
if n < k { if n < k {
return 0 return 0

View File

@@ -178,6 +178,7 @@ func TestMax(t *testing.T) {
assert.Equal(0, Max(0, 0)) assert.Equal(0, Max(0, 0))
assert.Equal(3, Max(1, 2, 3)) assert.Equal(3, Max(1, 2, 3))
assert.Equal(1.4, Max(1.2, 1.4, 1.1, 1.4)) assert.Equal(1.4, Max(1.2, 1.4, 1.1, 1.4))
assert.Equal("abc", Max("a", "ab", "abc"))
type Integer int type Integer int
assert.Equal(Integer(1), Max(Integer(1), Integer(0))) assert.Equal(Integer(1), Max(Integer(1), Integer(0)))
@@ -212,6 +213,8 @@ func TestMin(t *testing.T) {
assert.Equal(0, Min(0, 0)) assert.Equal(0, Min(0, 0))
assert.Equal(1, Min(1, 2, 3)) assert.Equal(1, Min(1, 2, 3))
assert.Equal(1.1, Min(1.2, 1.4, 1.1, 1.4)) assert.Equal(1.1, Min(1.2, 1.4, 1.1, 1.4))
assert.Equal("a", Min("a", "ab", "abc"))
} }
func TestMinBy(t *testing.T) { func TestMinBy(t *testing.T) {

View File

@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"mime/multipart" "mime/multipart"
"net" "net"
@@ -11,6 +12,7 @@ import (
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
"regexp"
"runtime" "runtime"
"strings" "strings"
"time" "time"
@@ -305,3 +307,93 @@ func IsTelnetConnected(host string, port string) bool {
return true 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, "/") {
path = "/" + path
} else {
parsedUrl.Path = path
}
queryParams := parsedUrl.Query()
for key, value := range query {
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, v := 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)
}
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
}

View File

@@ -142,3 +142,96 @@ func TestTelnetConnected(t *testing.T) {
result2 := IsTelnetConnected("www.baidu.com", "123") result2 := IsTelnetConnected("www.baidu.com", "123")
assert.Equal(false, result2) 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: "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{},
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)
}
}

View File

@@ -126,12 +126,24 @@ func RandFloat(min, max float64, precision int) float64 {
n := rand.Float64()*(max-min) + min n := rand.Float64()*(max-min) + min
return mathutil.RoundToFloat(n, precision) return mathutil.FloorToFloat(n, precision)
} }
// RandFloats generate a slice of random float64 numbers of length that do not repeat. // RandFloats generate a slice of random float64 numbers of length that do not repeat.
// Play: https://go.dev/play/p/I3yndUQ-rhh // Play: https://go.dev/play/p/I3yndUQ-rhh
func RandFloats(length int, min, max float64, precision int) []float64 { func RandFloats(length int, min, max float64, precision int) []float64 {
if max < min {
min, max = max, min
}
maxLength := int((max - min) * math.Pow10(precision))
if maxLength == 0 {
maxLength = 1
}
if length > maxLength {
length = maxLength
}
nums := make([]float64, length) nums := make([]float64, length)
used := make(map[float64]struct{}, length) used := make(map[float64]struct{}, length)
for i := 0; i < length; { for i := 0; i < length; {
@@ -335,7 +347,7 @@ func UUIdV4() (string, error) {
} }
// RandNumberOfLength 生成一个长度为len的随机数 // RandNumberOfLength 生成一个长度为len的随机数
// Play: todo // Play: https://go.dev/play/p/oyZbuV7bu7b
func RandNumberOfLength(len int) int { func RandNumberOfLength(len int) int {
m := int(math.Pow10(len) - 1) m := int(math.Pow10(len) - 1)
i := int(math.Pow10(len - 1)) i := int(math.Pow10(len - 1))

View File

@@ -197,6 +197,14 @@ func TestRandFloats(t *testing.T) {
} }
assert.Equal(len(numbers), 5) assert.Equal(len(numbers), 5)
numbers2 := RandFloats(10, 3.14, 3.2, 2)
for _, n := range numbers2 {
assert.GreaterOrEqual(n, 3.14)
assert.Less(n, 3.2)
}
assert.Equal(len(numbers2), 6)
} }
func TestRandIntSlice(t *testing.T) { func TestRandIntSlice(t *testing.T) {

View File

@@ -220,6 +220,28 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
return true return true
} }
// EqualUnordered checks if two slices are equal: the same length and all elements' value are equal (unordered).
// Play: https://go.dev/play/p/n8fSc2w8ZgX
func EqualUnordered[T comparable](slice1, slice2 []T) bool {
if len(slice1) != len(slice2) {
return false
}
seen := make(map[T]int)
for _, v := range slice1 {
seen[v]++
}
for _, v := range slice2 {
if seen[v] == 0 {
return false
}
seen[v]--
}
return true
}
// Every return true if all of the values in the slice pass the predicate function. // Every return true if all of the values in the slice pass the predicate function.
// Play: https://go.dev/play/p/R8U6Sl-j8cD // Play: https://go.dev/play/p/R8U6Sl-j8cD
func Every[T any](slice []T, predicate func(index int, item T) bool) bool { func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
@@ -980,7 +1002,19 @@ func Reverse[T any](slice []T) {
} }
} }
// Shuffle the slice. // ReverseCopy return a new slice of element order is reversed to the given slice.
// Play: https://go.dev/play/p/c9arEaP7Cg-
func ReverseCopy[T any](slice []T) []T {
result := make([]T, len(slice))
for i, j := 0, len(slice)-1; i < len(slice); i, j = i+1, j-1 {
result[i] = slice[j]
}
return result
}
// Shuffle return a new slice with elements shuffled.
// Play: https://go.dev/play/p/YHvhnWGU3Ge // Play: https://go.dev/play/p/YHvhnWGU3Ge
func Shuffle[T any](slice []T) []T { func Shuffle[T any](slice []T) []T {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
@@ -992,6 +1026,20 @@ func Shuffle[T any](slice []T) []T {
return slice return slice
} }
// ShuffleCopy return a new slice with elements shuffled.
// Play: https://go.dev/play/p/vqDa-Gs1vT0
func ShuffleCopy[T any](slice []T) []T {
result := make([]T, len(slice))
copy(result, slice)
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(result), func(i, j int) {
result[i], result[j] = result[j], result[i]
})
return result
}
// IsAscending checks if a slice is ascending order. // IsAscending checks if a slice is ascending order.
// Play: https://go.dev/play/p/9CtsFjet4SH // Play: https://go.dev/play/p/9CtsFjet4SH
func IsAscending[T constraints.Ordered](slice []T) bool { func IsAscending[T constraints.Ordered](slice []T) bool {
@@ -1415,7 +1463,7 @@ func Frequency[T comparable](slice []T) map[T]int {
} }
// JoinFunc joins the slice elements into a single string with the given separator. // JoinFunc joins the slice elements into a single string with the given separator.
// Play: todo // Play: https://go.dev/play/p/55ib3SB5fM2
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string { func JoinFunc[T any](slice []T, sep string, transform func(T) T) string {
var buf strings.Builder var buf strings.Builder
for i, v := range slice { for i, v := range slice {
@@ -1428,7 +1476,7 @@ func JoinFunc[T any](slice []T, sep string, transform func(T) T) string {
} }
// ConcatBy concats the elements of a slice into a single value using the provided separator and connector function. // ConcatBy concats the elements of a slice into a single value using the provided separator and connector function.
// Play: todo // Play: https://go.dev/play/p/6QcUpcY4UMW
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T { func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T {
var result T var result T

View File

@@ -937,6 +937,41 @@ func ExampleReverse() {
// [d c b a] // [d c b a]
} }
func ExampleReverseCopy() {
strs := []string{"a", "b", "c", "d"}
reversedStrs := ReverseCopy(strs)
fmt.Println(reversedStrs)
fmt.Println(strs)
// Output:
// [d c b a]
// [a b c d]
}
func ExampleShuffle() {
strs := []string{"a", "b", "c", "d"}
Shuffle(strs)
fmt.Println(len(strs))
// Output:
// 4
}
func ExampleShuffleCopy() {
strs := []string{"a", "b", "c", "d"}
shuffledStrs := ShuffleCopy(strs)
fmt.Println(len(shuffledStrs))
fmt.Println(strs)
// Output:
// 4
// [a b c d]
}
func ExampleIsAscending() { func ExampleIsAscending() {
result1 := IsAscending([]int{1, 2, 3, 4, 5}) result1 := IsAscending([]int{1, 2, 3, 4, 5})
@@ -1300,3 +1335,15 @@ func ExampleConcatBy() {
// Alice | Bob | Charlie // Alice | Bob | Charlie
// 90 // 90
} }
func ExampleEqualUnordered() {
result1 := EqualUnordered([]int{1, 2, 3}, []int{3, 2, 1})
result2 := EqualUnordered([]int{1, 2, 3}, []int{4, 5, 6})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -1114,6 +1115,17 @@ func TestReverse(t *testing.T) {
assert.Equal([]string{"e", "d", "c", "b", "a"}, s2) assert.Equal([]string{"e", "d", "c", "b", "a"}, s2)
} }
func TestReverseCopy(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestReverseCopy")
numbers := []int{1, 2, 3, 4, 5}
reversedNumbers := ReverseCopy(numbers)
assert.Equal([]int{5, 4, 3, 2, 1}, reversedNumbers)
assert.Equal([]int{1, 2, 3, 4, 5}, numbers)
}
func TestDifference(t *testing.T) { func TestDifference(t *testing.T) {
t.Parallel() t.Parallel()
@@ -1329,6 +1341,18 @@ func TestShuffle(t *testing.T) {
assert.Equal(5, len(result)) assert.Equal(5, len(result))
} }
func TestShuffleCopy(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestShuffleCopy")
numbers := []int{1, 2, 3, 4, 5}
result := ShuffleCopy(numbers)
assert.Equal(5, len(result))
assert.Equal([]int{1, 2, 3, 4, 5}, numbers)
}
func TestIndexOf(t *testing.T) { func TestIndexOf(t *testing.T) {
t.Parallel() t.Parallel()
@@ -1794,6 +1818,8 @@ func TestFilterConcurrent(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6} nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{4, 5, 6} expected := []int{4, 5, 6}
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4) actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
sort.Ints(actual)
sort.Ints(expected)
assert.Equal(expected, actual) assert.Equal(expected, actual)
}) })
} }
@@ -1926,3 +1952,25 @@ func TestConcatBy(t *testing.T) {
assert.Equal(90, result.Age) assert.Equal(90, result.Age)
}) })
} }
func TestEqualUnordered(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqualUnordered")
tests := []struct {
slice1, slice2 []int
expected bool
}{
{[]int{}, []int{}, true},
{[]int{1, 2, 3}, []int{1, 2, 3}, true},
{[]int{1, 2, 3}, []int{3, 2, 1}, true},
{[]int{1, 2, 3}, []int{1, 2, 3, 4}, false},
{[]int{1, 2, 3}, []int{1, 2}, false},
{[]int{1, 2, 3}, []int{1, 2, 4}, false},
}
for _, test := range tests {
assert.Equal(test.expected, EqualUnordered(test.slice1, test.slice2))
}
}

View File

@@ -130,7 +130,6 @@ func (s Stream[T]) Distinct() Stream[T] {
distinct := map[string]bool{} distinct := map[string]bool{}
for _, v := range s.source { for _, v := range s.source {
// todo: performance issue
k := hashKey(v) k := hashKey(v)
if _, ok := distinct[k]; !ok { if _, ok := distinct[k]; !ok {
distinct[k] = true distinct[k] = true
@@ -395,6 +394,7 @@ func (s Stream[T]) Min(less func(a, b T) bool) (T, bool) {
} }
// IndexOf returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element. // IndexOf returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element.
// Play: https://go.dev/play/p/tBV5Nc-XDX2
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int { func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int {
for i, v := range s.source { for i, v := range s.source {
if equal(v, target) { if equal(v, target) {
@@ -405,6 +405,7 @@ func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int {
} }
// LastIndexOf returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element. // LastIndexOf returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element.
// Play: https://go.dev/play/p/CjeoNw2eac_G
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int { func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int {
for i := len(s.source) - 1; i >= 0; i-- { for i := len(s.source) - 1; i >= 0; i-- {
if equal(s.source[i], target) { if equal(s.source[i], target) {

View File

@@ -737,15 +737,15 @@ func RegexMatchAllGroups(pattern, str string) [][]string {
} }
// ExtractContent extracts the content between the start and end strings in the source string. // ExtractContent extracts the content between the start and end strings in the source string.
// Play: todo // Play: https://go.dev/play/p/Ay9UIk7Rum9
func ExtractContent(s, start, end string) []string { func ExtractContent(str, start, end string) []string {
result := []string{} result := []string{}
for { for {
if _, after, ok := strings.Cut(s, start); ok { if _, after, ok := strings.Cut(str, start); ok {
if before, _, ok := strings.Cut(after, end); ok { if before, _, ok := strings.Cut(after, end); ok {
result = append(result, before) result = append(result, before)
s = after str = after
} else { } else {
break break
} }
@@ -756,3 +756,20 @@ func ExtractContent(s, start, end string) []string {
return result return result
} }
// FindAllOccurrences returns the positions of all occurrences of a substring in a string.
// Play: https://go.dev/play/p/uvyA6azGLB1
func FindAllOccurrences(str, substr string) []int {
var positions []int
for i := 0; i < len(str); {
index := strings.Index(str[i:], substr)
if index == -1 {
break
}
positions = append(positions, i+index)
i += index + 1
}
return positions
}

View File

@@ -763,5 +763,13 @@ func ExampleExtractContent() {
// Output: // Output:
// [content1 content2 content1] // [content1 content2 content1]
}
func ExampleFindAllOccurrences() {
result := FindAllOccurrences("ababab", "ab")
fmt.Println(result)
// Output:
// [0 2 4]
} }

View File

@@ -307,10 +307,21 @@ func TestBeforeLast(t *testing.T) {
assert := internal.NewAssert(t, "TestBeforeLast") assert := internal.NewAssert(t, "TestBeforeLast")
assert.Equal("lancet", BeforeLast("lancet", "")) tests := []struct {
assert.Equal("lancet", BeforeLast("lancet", "abcdef")) input string
assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/")) char string
assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test")) 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) { func TestAfter(t *testing.T) {
@@ -318,11 +329,21 @@ func TestAfter(t *testing.T) {
assert := internal.NewAssert(t, "TestAfter") assert := internal.NewAssert(t, "TestAfter")
assert.Equal("lancet", After("lancet", "")) tests := []struct {
assert.Equal("", After("lancet", "lancet")) input string
assert.Equal("test/lancet", After("github.com/test/lancet", "/")) char string
assert.Equal("/lancet", After("github.com/test/lancet", "test")) expected string
assert.Equal("lancet", After("lancet", "abcdef")) }{
{"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) { func TestAfterLast(t *testing.T) {
@@ -330,11 +351,21 @@ func TestAfterLast(t *testing.T) {
assert := internal.NewAssert(t, "TestAfterLast") assert := internal.NewAssert(t, "TestAfterLast")
assert.Equal("lancet", AfterLast("lancet", "")) tests := []struct {
assert.Equal("lancet", AfterLast("github.com/test/lancet", "/")) input string
assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test")) char string
assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test")) expected string
assert.Equal("lancet", AfterLast("lancet", "abcdef")) }{
{"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) { func TestIsString(t *testing.T) {
@@ -342,11 +373,20 @@ func TestIsString(t *testing.T) {
assert := internal.NewAssert(t, "TestIsString") assert := internal.NewAssert(t, "TestIsString")
assert.Equal(true, IsString("lancet")) tests := []struct {
assert.Equal(true, IsString("")) input interface{}
assert.Equal(false, IsString(1)) expected bool
assert.Equal(false, IsString(true)) }{
assert.Equal(false, IsString([]string{})) {"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) { func TestReverse(t *testing.T) {
@@ -363,11 +403,21 @@ func TestWrap(t *testing.T) {
assert := internal.NewAssert(t, "TestWrap") assert := internal.NewAssert(t, "TestWrap")
assert.Equal("ab", Wrap("ab", "")) tests := []struct {
assert.Equal("", Wrap("", "*")) input string
assert.Equal("*ab*", Wrap("ab", "*")) wrapper string
assert.Equal("\"ab\"", Wrap("ab", "\"")) expected string
assert.Equal("'ab'", Wrap("ab", "'")) }{
{"", "", ""},
{"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) { func TestUnwrap(t *testing.T) {
@@ -485,10 +535,21 @@ func TestIsBlank(t *testing.T) {
assert := internal.NewAssert(t, "TestIsBlank") assert := internal.NewAssert(t, "TestIsBlank")
assert.Equal(IsBlank(""), true) tests := []struct {
assert.Equal(IsBlank("\t\v\f\n"), true) 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) { func TestIsNotBlank(t *testing.T) {
@@ -496,12 +557,22 @@ func TestIsNotBlank(t *testing.T) {
assert := internal.NewAssert(t, "TestIsBlank") assert := internal.NewAssert(t, "TestIsBlank")
assert.Equal(IsNotBlank(""), false) tests := []struct {
assert.Equal(IsNotBlank(" "), false) input string
assert.Equal(IsNotBlank("\t\v\f\n"), false) expected bool
}{
{"", false},
{" ", false},
{"\t\v\f\n", false},
{"\t\v\f\nabc", true},
{"abc", true},
{" 中文", true},
{" world ", true},
}
assert.Equal(IsNotBlank(" 中文"), true) for _, tt := range tests {
assert.Equal(IsNotBlank(" world "), true) assert.Equal(tt.expected, IsNotBlank(tt.input))
}
} }
func TestHasPrefixAny(t *testing.T) { func TestHasPrefixAny(t *testing.T) {
@@ -509,12 +580,19 @@ func TestHasPrefixAny(t *testing.T) {
assert := internal.NewAssert(t, "TestHasPrefixAny") assert := internal.NewAssert(t, "TestHasPrefixAny")
str := "foo bar" tests := []struct {
prefixes := []string{"fo", "xyz", "hello"} str string
notMatches := []string{"oom", "world"} 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) for _, tt := range tests {
assert.Equal(HasPrefixAny(str, notMatches), false) assert.Equal(tt.expected, HasPrefixAny(tt.str, tt.prefixes))
}
} }
func TestHasSuffixAny(t *testing.T) { func TestHasSuffixAny(t *testing.T) {
@@ -522,25 +600,44 @@ func TestHasSuffixAny(t *testing.T) {
assert := internal.NewAssert(t, "TestHasSuffixAny") assert := internal.NewAssert(t, "TestHasSuffixAny")
str := "foo bar" tests := []struct {
suffixes := []string{"bar", "xyz", "hello"} str string
notMatches := []string{"oom", "world"} 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) for _, tt := range tests {
assert.Equal(HasSuffixAny(str, notMatches), false) assert.Equal(tt.expected, HasSuffixAny(tt.str, tt.suffixes))
}
} }
func TestIndexOffset(t *testing.T) { func TestIndexOffset(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIndexOffset") assert := internal.NewAssert(t, "TestIndexOffset")
str := "foo bar hello world" str := "foo bar hello world"
assert.Equal(IndexOffset(str, "o", 5), 12) tests := []struct {
assert.Equal(IndexOffset(str, "o", 0), 1) str string
assert.Equal(IndexOffset(str, "d", len(str)-1), len(str)-1) substr string
assert.Equal(IndexOffset(str, "d", len(str)), -1) offset int
assert.Equal(IndexOffset(str, "f", -1), -1) 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) { func TestReplaceWithMap(t *testing.T) {
@@ -648,13 +745,23 @@ func TestSubInBetween(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestSubInBetween") 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, "", "")) for _, tt := range tests {
assert.Equal("ab", SubInBetween(str, "", "c")) assert.Equal(tt.expected, SubInBetween(tt.input, tt.start, tt.end))
assert.Equal("bc", SubInBetween(str, "a", "d")) }
assert.Equal("", SubInBetween(str, "a", ""))
assert.Equal("", SubInBetween(str, "a", "f"))
} }
func TestHammingDistance(t *testing.T) { func TestHammingDistance(t *testing.T) {
@@ -696,13 +803,22 @@ func TestConcat(t *testing.T) {
assert := internal.NewAssert(t, "TestConcat") assert := internal.NewAssert(t, "TestConcat")
assert.Equal("", Concat(0)) tests := []struct {
assert.Equal("a", Concat(1, "a")) args []string
assert.Equal("ab", Concat(2, "a", "b")) expected string
assert.Equal("abc", Concat(3, "a", "b", "c")) }{
assert.Equal("abc", Concat(3, "a", "", "b", "c", "")) {[]string{}, ""},
assert.Equal("你好,世界!", Concat(0, "你好", "", "", "世界!", "")) {[]string{"a"}, "a"},
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", "")) {[]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) { func TestEllipsis(t *testing.T) {
@@ -937,3 +1053,32 @@ func TestExtractContent(t *testing.T) {
}) })
} }
} }
func TestFindAllOccurrences(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFindAllOccurrences")
var empty []int
tests := []struct {
input string
substr string
expected []int
}{
{"", "", empty},
{"", "a", empty},
{"a", "", []int{0}},
{"a", "a", []int{0}},
{"aa", "a", []int{0, 1}},
{"ababab", "ab", []int{0, 2, 4}},
{"ababab", "ba", []int{1, 3}},
{"ababab", "c", empty},
{"ababab", "ababab", []int{0}},
{"ababab", "abababc", empty},
{"ababab", "abababa", empty},
}
for _, tt := range tests {
assert.Equal(tt.expected, FindAllOccurrences(tt.input, tt.substr))
}
}

View File

@@ -21,6 +21,7 @@ import (
var ( var (
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`) letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
alphaNumericMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
numberRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d`) numberRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d`)
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`) intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`) urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
@@ -181,6 +182,12 @@ func IsJSON(str string) bool {
return json.Unmarshal([]byte(str), &js) == nil return json.Unmarshal([]byte(str), &js) == nil
} }
// IsAlphaNumericStr 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. // IsNumberStr check if the string can convert to a number.
// Play: https://go.dev/play/p/LzaKocSV79u // Play: https://go.dev/play/p/LzaKocSV79u
func IsNumberStr(s string) bool { func IsNumberStr(s string) bool {
@@ -207,6 +214,18 @@ func IsIp(ipstr string) bool {
return ip != nil return ip != nil
} }
// IsIpPort check if the string is ip:port.
// Play: https://go.dev/play/p/xUmls_b9L29
func IsIpPort(str string) bool {
host, port, err := net.SplitHostPort(str)
if err != nil {
return false
}
ip := net.ParseIP(host)
return ip != nil && IsPort(port)
}
// IsIpV4 check if the string is a ipv4 address. // IsIpV4 check if the string is a ipv4 address.
// Play: https://go.dev/play/p/zBGT99EjaIu // Play: https://go.dev/play/p/zBGT99EjaIu
func IsIpV4(ipstr string) bool { func IsIpV4(ipstr string) bool {

View File

@@ -348,6 +348,24 @@ func ExampleIsIp() {
// false // false
} }
func ExampleIsIpPort() {
result1 := IsIpPort("127.0.0.1:8080")
result2 := IsIpPort("[0:0:0:0:0:0:0:1]:8080")
result3 := IsIpPort(":8080")
result4 := IsIpPort("::0:0:0:0:")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// false
// false
}
func ExampleIsIpV4() { func ExampleIsIpV4() {
result1 := IsIpV4("127.0.0.1") result1 := IsIpV4("127.0.0.1")
result2 := IsIpV4("::0:0:0:0:0:0:1") result2 := IsIpV4("::0:0:0:0:0:0:1")

View File

@@ -15,16 +15,25 @@ func TestIsAllUpper(t *testing.T) {
assert := internal.NewAssert(t, "TestIsAllUpper") assert := internal.NewAssert(t, "TestIsAllUpper")
assert.Equal(true, IsAllUpper("ABC")) tests := []struct {
assert.Equal(false, IsAllUpper("")) input string
assert.Equal(false, IsAllUpper("abc")) expected bool
assert.Equal(false, IsAllUpper("aBC")) }{
assert.Equal(false, IsAllUpper("1BC")) {"ABC", true},
assert.Equal(false, IsAllUpper("1bc")) {"", false},
assert.Equal(false, IsAllUpper("123")) {"abc", false},
assert.Equal(false, IsAllUpper("你好")) {"aBC", false},
assert.Equal(false, IsAllUpper("A&")) {"1BC", false},
assert.Equal(false, IsAllUpper("&@#$%^&*")) {"1bc", false},
{"123", false},
{"你好", false},
{"A&", false},
{"&@#$%^&*", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsAllUpper(tt.input))
}
} }
func TestIsAllLower(t *testing.T) { func TestIsAllLower(t *testing.T) {
@@ -32,16 +41,25 @@ func TestIsAllLower(t *testing.T) {
assert := internal.NewAssert(t, "TestIsAllLower") assert := internal.NewAssert(t, "TestIsAllLower")
assert.Equal(true, IsAllLower("abc")) tests := []struct {
assert.Equal(false, IsAllLower("ABC")) input string
assert.Equal(false, IsAllLower("")) expected bool
assert.Equal(false, IsAllLower("aBC")) }{
assert.Equal(false, IsAllLower("1BC")) {"abc", true},
assert.Equal(false, IsAllLower("1bc")) {"", false},
assert.Equal(false, IsAllLower("123")) {"ABC", false},
assert.Equal(false, IsAllLower("你好")) {"aBC", false},
assert.Equal(false, IsAllLower("A&")) {"1BC", false},
assert.Equal(false, IsAllLower("&@#$%^&*")) {"1bc", false},
{"123", false},
{"你好", false},
{"A&", false},
{"&@#$%^&*", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsAllLower(tt.input))
}
} }
func TestContainLower(t *testing.T) { func TestContainLower(t *testing.T) {
@@ -49,17 +67,25 @@ func TestContainLower(t *testing.T) {
assert := internal.NewAssert(t, "TestContainLower") assert := internal.NewAssert(t, "TestContainLower")
assert.Equal(true, ContainLower("abc")) tests := []struct {
assert.Equal(true, ContainLower("aBC")) input string
assert.Equal(true, ContainLower("1bc")) expected bool
assert.Equal(true, ContainLower("a&")) }{
{"abc", true},
{"aBC", true},
{"1bc", true},
{"a&", true},
{"ABC", false},
{"", false},
{"1BC", false},
{"123", false},
{"你好", false},
{"&@#$%^&*", false},
}
assert.Equal(false, ContainLower("ABC")) for _, tt := range tests {
assert.Equal(false, ContainLower("")) assert.Equal(tt.expected, ContainLower(tt.input))
assert.Equal(false, ContainLower("1BC")) }
assert.Equal(false, ContainLower("123"))
assert.Equal(false, ContainLower("你好"))
assert.Equal(false, ContainLower("&@#$%^&*"))
} }
func TestContainUpper(t *testing.T) { func TestContainUpper(t *testing.T) {
@@ -67,17 +93,25 @@ func TestContainUpper(t *testing.T) {
assert := internal.NewAssert(t, "TestContainUpper") assert := internal.NewAssert(t, "TestContainUpper")
assert.Equal(true, ContainUpper("ABC")) tests := []struct {
assert.Equal(true, ContainUpper("aBC")) input string
assert.Equal(true, ContainUpper("1BC")) expected bool
assert.Equal(true, ContainUpper("A&")) }{
{"ABC", true},
{"aBC", true},
{"1BC", true},
{"A&", true},
{"abc", false},
{"", false},
{"1bc", false},
{"123", false},
{"你好", false},
{"&@#$%^&*", false},
}
assert.Equal(false, ContainUpper("abc")) for _, tt := range tests {
assert.Equal(false, ContainUpper("")) assert.Equal(tt.expected, ContainUpper(tt.input))
assert.Equal(false, ContainUpper("1bc")) }
assert.Equal(false, ContainUpper("123"))
assert.Equal(false, ContainUpper("你好"))
assert.Equal(false, ContainUpper("&@#$%^&*"))
} }
func TestContainLetter(t *testing.T) { func TestContainLetter(t *testing.T) {
@@ -85,15 +119,23 @@ func TestContainLetter(t *testing.T) {
assert := internal.NewAssert(t, "TestContainLetter") assert := internal.NewAssert(t, "TestContainLetter")
assert.Equal(true, ContainLetter("ABC")) tests := []struct {
assert.Equal(true, ContainLetter("1Bc")) input string
assert.Equal(true, ContainLetter("1ab")) expected bool
assert.Equal(true, ContainLetter("A&")) }{
{"ABC", true},
{"1Bc", true},
{"1ab", true},
{"A&", true},
{"", false},
{"123", false},
{"你好", false},
{"&@#$%^&*", false},
}
assert.Equal(false, ContainLetter("")) for _, tt := range tests {
assert.Equal(false, ContainLetter("123")) assert.Equal(tt.expected, ContainLetter(tt.input))
assert.Equal(false, ContainLetter("你好")) }
assert.Equal(false, ContainLetter("&@#$%^&*"))
} }
func TestContainNumber(t *testing.T) { func TestContainNumber(t *testing.T) {
@@ -101,18 +143,26 @@ func TestContainNumber(t *testing.T) {
assert := internal.NewAssert(t, "TestContainNumber") assert := internal.NewAssert(t, "TestContainNumber")
assert.Equal(true, ContainNumber("123")) tests := []struct {
assert.Equal(true, ContainNumber("1Bc")) input string
assert.Equal(true, ContainNumber("a2c")) expected bool
assert.Equal(true, ContainNumber("ab3")) }{
assert.Equal(true, ContainNumber("a23")) {"123", true},
assert.Equal(true, ContainNumber("a23c")) {"1Bc", true},
assert.Equal(true, ContainNumber("1%%%")) {"a2c", true},
{"ab3", true},
{"a23", true},
{"a23c", true},
{"1%%%", true},
{"ABC", false},
{"", false},
{"你好", false},
{"&@#$%^&*", false},
}
assert.Equal(false, ContainNumber("ABC")) for _, tt := range tests {
assert.Equal(false, ContainNumber("")) assert.Equal(tt.expected, ContainNumber(tt.input))
assert.Equal(false, ContainNumber("你好")) }
assert.Equal(false, ContainNumber("&@#$%^&*"))
} }
func TestIsJSON(t *testing.T) { func TestIsJSON(t *testing.T) {
@@ -120,15 +170,23 @@ func TestIsJSON(t *testing.T) {
assert := internal.NewAssert(t, "TestIsJSON") assert := internal.NewAssert(t, "TestIsJSON")
assert.Equal(true, IsJSON("{}")) tests := []struct {
assert.Equal(true, IsJSON("{\"name\": \"test\"}")) input string
assert.Equal(true, IsJSON("[]")) expected bool
assert.Equal(true, IsJSON("123")) }{
{"{}", true},
{"{\"name\": \"test\"}", true},
{"[]", true},
{"123", true},
{"", false},
{"abc", false},
{"你好", false},
{"&@#$%^&*", false},
}
assert.Equal(false, IsJSON("")) for _, tt := range tests {
assert.Equal(false, IsJSON("abc")) assert.Equal(tt.expected, IsJSON(tt.input))
assert.Equal(false, IsJSON("你好")) }
assert.Equal(false, IsJSON("&@#$%^&*"))
} }
func TestIsNumber(t *testing.T) { func TestIsNumber(t *testing.T) {
@@ -136,10 +194,19 @@ func TestIsNumber(t *testing.T) {
assert := internal.NewAssert(t, "TestIsNumber") assert := internal.NewAssert(t, "TestIsNumber")
assert.Equal(false, IsNumber("")) tests := []struct {
assert.Equal(false, IsNumber("3")) input interface{}
assert.Equal(true, IsNumber(0)) expected bool
assert.Equal(true, IsNumber(0.1)) }{
{"", false},
{"3", false},
{0, true},
{0.1, true},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsNumber(tt.input))
}
} }
func TestIsFloat(t *testing.T) { func TestIsFloat(t *testing.T) {
@@ -147,10 +214,19 @@ func TestIsFloat(t *testing.T) {
assert := internal.NewAssert(t, "TestIsFloat") assert := internal.NewAssert(t, "TestIsFloat")
assert.Equal(false, IsFloat("")) tests := []struct {
assert.Equal(false, IsFloat("3")) input interface{}
assert.Equal(false, IsFloat(0)) expected bool
assert.Equal(true, IsFloat(0.1)) }{
{"", false},
{"3", false},
{0, false},
{0.1, true},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsFloat(tt.input))
}
} }
func TestIsInt(t *testing.T) { func TestIsInt(t *testing.T) {
@@ -158,11 +234,20 @@ func TestIsInt(t *testing.T) {
assert := internal.NewAssert(t, "TestIsInt") assert := internal.NewAssert(t, "TestIsInt")
assert.Equal(false, IsInt("")) tests := []struct {
assert.Equal(false, IsInt("3")) input interface{}
assert.Equal(false, IsInt(0.1)) expected bool
assert.Equal(true, IsInt(0)) }{
assert.Equal(true, IsInt(-1)) {"", 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) { func TestIsNumberStr(t *testing.T) {
@@ -170,11 +255,21 @@ func TestIsNumberStr(t *testing.T) {
assert := internal.NewAssert(t, "TestIsNumberStr") assert := internal.NewAssert(t, "TestIsNumberStr")
assert.Equal(true, IsNumberStr("3.")) tests := []struct {
assert.Equal(true, IsNumberStr("+3.")) input string
assert.Equal(true, IsNumberStr("-3.")) expected bool
assert.Equal(true, IsNumberStr("+3e2")) }{
assert.Equal(false, IsNumberStr("abc")) {"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) { func TestIsFloatStr(t *testing.T) {
@@ -182,11 +277,21 @@ func TestIsFloatStr(t *testing.T) {
assert := internal.NewAssert(t, "TestIsFloatStr") assert := internal.NewAssert(t, "TestIsFloatStr")
assert.Equal(true, IsFloatStr("3.")) tests := []struct {
assert.Equal(true, IsFloatStr("+3.")) input string
assert.Equal(true, IsFloatStr("-3.")) expected bool
assert.Equal(true, IsFloatStr("12")) }{
assert.Equal(false, IsFloatStr("abc")) {"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) { func TestIsIntStr(t *testing.T) {
@@ -194,10 +299,20 @@ func TestIsIntStr(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIntStr") assert := internal.NewAssert(t, "TestIsIntStr")
assert.Equal(true, IsIntStr("+3")) tests := []struct {
assert.Equal(true, IsIntStr("-3")) input string
assert.Equal(false, IsIntStr("3.")) expected bool
assert.Equal(false, IsIntStr("abc")) }{
{"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) { func TestIsPort(t *testing.T) {
@@ -205,13 +320,22 @@ func TestIsPort(t *testing.T) {
assert := internal.NewAssert(t, "TestIsPort") assert := internal.NewAssert(t, "TestIsPort")
assert.Equal(true, IsPort("1")) tests := []struct {
assert.Equal(true, IsPort("65535")) input string
assert.Equal(false, IsPort("abc")) expected bool
assert.Equal(false, IsPort("123abc")) }{
assert.Equal(false, IsPort("")) {"1", true},
assert.Equal(false, IsPort("-1")) {"65535", true},
assert.Equal(false, IsPort("65536")) {"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) { func TestIsIp(t *testing.T) {
@@ -219,10 +343,45 @@ func TestIsIp(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIntStr") assert := internal.NewAssert(t, "TestIsIntStr")
assert.Equal(true, IsIp("127.0.0.1")) tests := []struct {
assert.Equal(true, IsIp("::0:0:0:0:0:0:1")) input string
assert.Equal(false, IsIp("127.0.0")) expected bool
assert.Equal(false, IsIp("127")) }{
{"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) {
t.Parallel()
assert := internal.NewAssert(t, "TestIsIpPort")
tests := []struct {
input string
expected bool
}{
{"[::0:0:0:0:0:0:1]:8080", true},
{"127.0.0.1:8080", true},
{"", 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) { func TestIsIpV4(t *testing.T) {
@@ -230,13 +389,23 @@ func TestIsIpV4(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIpV4") 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")) {"::0:0:0:0:0:0:1", false},
assert.Equal(false, IsIpV4("127.0.0.1.1")) {"127.0.0.1.1", false},
assert.Equal(false, IsIpV4("256.0.0.1")) {"256.0.0.1", false},
assert.Equal(false, IsIpV4("127.0.0.a")) {"127.0.0.a", false},
assert.Equal(false, IsIpV4("")) {"", false},
}
for _, tt := range tests {
assert.Equal(tt.expected, IsIpV4(tt.input))
}
} }
func TestIsIpV6(t *testing.T) { func TestIsIpV6(t *testing.T) {
@@ -244,14 +413,23 @@ func TestIsIpV6(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIpV6") assert := internal.NewAssert(t, "TestIsIpV6")
assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1")) tests := []struct {
assert.Equal(true, IsIpV6("::1")) input string
assert.Equal(true, IsIpV6("::")) 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) { func TestIsUrl(t *testing.T) {
@@ -270,13 +448,21 @@ func TestIsDns(t *testing.T) {
assert := internal.NewAssert(t, "TestIsDns") assert := internal.NewAssert(t, "TestIsDns")
assert.Equal(true, IsDns("abc.com")) tests := []struct {
assert.Equal(true, IsDns("123.cn")) input string
assert.Equal(true, IsDns("a.b.com")) 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")) for _, tt := range tests {
assert.Equal(false, IsDns("a@b.com")) assert.Equal(tt.expected, IsDns(tt.input))
assert.Equal(false, IsDns("http://abc.com")) }
} }
func TestIsEmail(t *testing.T) { func TestIsEmail(t *testing.T) {
@@ -313,11 +499,19 @@ func TestIsChinesePhone(t *testing.T) {
assert := internal.NewAssert(t, "TestIsChinesePhone") assert := internal.NewAssert(t, "TestIsChinesePhone")
assert.Equal(true, IsChinesePhone("010-32116675")) tests := []struct {
assert.Equal(true, IsChinesePhone("0464-8756213")) input string
assert.Equal(true, IsChinesePhone("0731-82251545")) //长沙晚报电话 expected bool
assert.Equal(false, IsChinesePhone("123-87562")) }{
{"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) { func TestIsChineseIdNum(t *testing.T) {
@@ -325,13 +519,23 @@ func TestIsChineseIdNum(t *testing.T) {
assert := internal.NewAssert(t, "TestIsChineseIdNum") assert := internal.NewAssert(t, "TestIsChineseIdNum")
assert.Equal(true, IsChineseIdNum("210911192105130714")) tests := []struct {
assert.Equal(true, IsChineseIdNum("11010519491231002X")) input string
assert.Equal(true, IsChineseIdNum("11010519491231002x")) expected bool
assert.Equal(false, IsChineseIdNum("123456")) }{
assert.Equal(false, IsChineseIdNum("990911192105130714")) {"210911192105130714", true},
assert.Equal(false, IsChineseIdNum("990911189905130714")) {"11010519491231002X", true},
assert.Equal(false, IsChineseIdNum("210911222205130714")) {"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) { func TestIsCreditCard(t *testing.T) {
@@ -390,13 +594,23 @@ func TestIsStrongPassword(t *testing.T) {
assert := internal.NewAssert(t, "TestIsStrongPassword") assert := internal.NewAssert(t, "TestIsStrongPassword")
assert.Equal(false, IsStrongPassword("abc", 3)) tests := []struct {
assert.Equal(false, IsStrongPassword("abc123", 6)) input string
assert.Equal(false, IsStrongPassword("abcABC", 6)) length int
assert.Equal(false, IsStrongPassword("abc123@#$", 9)) expected bool
assert.Equal(false, IsStrongPassword("abcABC123@#$", 16)) }{
assert.Equal(true, IsStrongPassword("abcABC123@#$", 12)) {"abc", 3, false},
assert.Equal(true, IsStrongPassword("abcABC123@#$", 10)) {"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) { func TestIsWeakPassword(t *testing.T) {
@@ -404,11 +618,20 @@ func TestIsWeakPassword(t *testing.T) {
assert := internal.NewAssert(t, "TestIsWeakPassword") assert := internal.NewAssert(t, "TestIsWeakPassword")
assert.Equal(true, IsWeakPassword("abc")) tests := []struct {
assert.Equal(true, IsWeakPassword("123")) input string
assert.Equal(true, IsWeakPassword("abc123")) expected bool
assert.Equal(true, IsWeakPassword("abcABC123")) }{
assert.Equal(false, IsWeakPassword("abc123@#$")) {"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) { func TestIsZeroValue(t *testing.T) {
@@ -549,50 +772,83 @@ func TestIsPrintable(t *testing.T) {
assert := internal.NewAssert(t, "TestIsPrintable") assert := internal.NewAssert(t, "TestIsPrintable")
assert.Equal(true, IsPrintable("ABC")) tests := []struct {
assert.Equal(true, IsPrintable("{id: 123}")) input string
assert.Equal(true, IsPrintable("")) expected bool
assert.Equal(true, IsPrintable("😄")) }{
assert.Equal(false, IsPrintable("\u0000")) {"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) { func TestIsBin(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIsBin") assert := internal.NewAssert(t, "TestIsBin")
assert.Equal(true, IsBin("0101")) tests := []struct {
assert.Equal(true, IsBin("0b1101")) input string
expected bool
}{
{"0101", true},
{"0b1101", true},
{"b1101", false},
{"1201", false},
{"", false},
}
assert.Equal(false, IsBin("b1101")) for _, tt := range tests {
assert.Equal(false, IsBin("1201")) assert.Equal(tt.expected, IsBin(tt.input))
assert.Equal(false, IsBin("")) }
} }
func TestIsHex(t *testing.T) { func TestIsHex(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIsHex") assert := internal.NewAssert(t, "TestIsHex")
assert.Equal(true, IsHex("ABCDE")) tests := []struct {
assert.Equal(true, IsHex("abcde")) input string
assert.Equal(true, IsHex("0xabcde")) expected bool
assert.Equal(true, IsHex("0Xabcde")) }{
assert.Equal(true, IsHex("#abcde")) {"ABCDE", true},
{"abcde", true},
{"0xabcde", true},
{"0Xabcde", true},
{"#abcde", true},
{"cdfeg", false},
{"0xcdfeg", false},
{"", false},
}
assert.Equal(false, IsHex("cdfeg")) for _, tt := range tests {
assert.Equal(false, IsHex("0xcdfeg")) assert.Equal(tt.expected, IsHex(tt.input))
assert.Equal(false, IsHex("")) }
} }
func TestIsBase64URL(t *testing.T) { func TestIsBase64URL(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestIsBase64URL") assert := internal.NewAssert(t, "TestIsBase64URL")
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ")) tests := []struct {
assert.Equal(true, IsBase64URL("SAGsbG8sIHdvcmxkIQ==")) input string
expected bool
}{
{"SAGsbG8sIHdvcmxkIQ", true},
{"SAGsbG8sIHdvcmxkIQ==", true},
{"SAGsbG8sIHdvcmxkIQ=", false},
{"SAGsbG8sIHdvcmxkIQ===", false},
}
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ=")) for _, tt := range tests {
assert.Equal(false, IsBase64URL("SAGsbG8sIHdvcmxkIQ===")) assert.Equal(tt.expected, IsBase64URL(tt.input))
// assert.Equal(false, IsBase64URL("")) }
} }
func TestIsJWT(t *testing.T) { func TestIsJWT(t *testing.T) {
@@ -642,3 +898,29 @@ func TestIsChinaUnionPay(t *testing.T) {
assert.Equal(true, IsChinaUnionPay("6250941006528599")) assert.Equal(true, IsChinaUnionPay("6250941006528599"))
assert.Equal(false, IsChinaUnionPay("3782822463100007")) 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))
}
}