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

Compare commits

..

122 Commits

Author SHA1 Message Date
dudaodong
889d0cc3d6 doc: update readme file 2025-11-01 21:45:56 +08:00
dudaodong
aa05027cab doc: update readme file 2025-11-01 21:44:03 +08:00
dudaodong
fabc3483ed doc: update readme file 2025-11-01 21:37:15 +08:00
dudaodong
30363242bb doc: update document for version v2.3.8 2025-11-01 21:25:10 +08:00
dudaodong
5d3964d81a release v2.3.8 2025-11-01 19:39:46 +08:00
dudaodong
6c3dc3e7d6 Merge branch 'rc' of github.com:duke-git/lancet into rc 2025-10-31 14:59:25 +08:00
dudaodong
2a2e1ca551 doc: add enum and update overview index 2025-10-31 14:57:54 +08:00
dudaodong
e1821eed2c doc: add docment for enum package 2025-10-31 14:48:32 +08:00
dudaodong
62f0a96d91 doc: add docment for enum package 2025-10-31 14:48:22 +08:00
dudaodong
cbdc3971dd doc: add docment for new or fixed function 2025-10-31 13:37:41 +08:00
Javen
350450bb67 feat: add ContainAny (#338)
Co-authored-by: Jiawen <im@linjiawen.com>
2025-10-30 19:24:45 +08:00
dudaodong
f407e51b24 fix: fix issue #335 2025-10-30 17:09:48 +08:00
dudaodong
3c6c3a14cf Merge branch 'rc' into v2 2025-10-29 21:10:54 +08:00
dudaodong
41bafdef92 refact: remove unused code 2025-10-29 21:10:30 +08:00
wangxc
fc624195c7 feat:add map to markdown conversion in map.go (#336)
* feat(file): add map to markdown conversion in map.go

* feat(file): add map to markdown conversion in map.go
2025-10-29 21:07:18 +08:00
残念
5b3a59e785 Merge pull request #334 from duke-git/v2
Add enum package
2025-10-25 12:32:12 +08:00
chentong
74abb2d3f1 feat(struct): add struct name function (#328)
* feat(struct): add struct name function

- Add Name() method to Struct type to return the struct name
- Implement unit tests for the new Name() method

* refactor(structs): rename Struct.Name to Struct.TypeName

- Rename method Name to TypeName for clarity and accuracy
- Update corresponding test cases
2025-10-23 11:20:43 +08:00
dudaodong
3f12b34eea fix: fix issue for PR #334, usge Pair struct to instance enum items 2025-10-16 18:43:24 +08:00
dudaodong
cd43004a91 feat: add example for enum package 2025-10-14 16:37:02 +08:00
dudaodong
3ac9461c00 Merge branch 'rc' into v2 2025-09-30 14:57:01 +08:00
dudaodong
309b07ae8a feat: a simple enum implementation 2025-09-30 14:56:25 +08:00
chentong
8fe56b6dc7 fix(eventbus): update error handler to include topic information (#333)
* fix(eventbus): update error handler to include topic information

- Modified error handler signature to accept topic string and error
- Updated panic recovery logic to pass event topic to error handler
- Adjusted SetErrorHandler method parameter signature
- Updated example test to demonstrate new error handler usage
- Enhanced error handling test to verify topic information

* fix(eventbus): unit test
2025-09-28 20:10:12 +08:00
残念
15a0dad0d8 Merge pull request #330 from zoulux/main
fix: return 0 when Average is called with empty slice
2025-09-23 11:22:30 +08:00
jake
93c777a418 Update mathutil.go
fix: return 0 when Average is called with empty slice
2025-09-22 10:04:23 +08:00
jake
5ff1c6578f Merge branch 'duke-git:main' into main 2025-09-22 09:53:09 +08:00
dudaodong
7d4b9510a2 feat: add IsChineseHMPassport 2025-08-21 14:13:43 +08:00
dudaodong
9f0ad2354a feat: update some test fucntions 2025-08-21 10:52:44 +08:00
dudaodong
55b66dee99 Merge branch 'rc' into v2 2025-08-21 10:08:21 +08:00
Idichekop
4c64a16204 fix(package): [slice] functions with inconsistent return behaviour (#326)
* Some functions modified

* Fixes in all the functions of slice.go

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

* Tests cover padding empty, nil, and negative lenght

* Implemented repeat, concat, and grow functionalities as internal.
2025-08-04 10:45:29 +08:00
Idichekop
cb8d93c499 Simple refactor of ForEach functions (#323) 2025-07-30 14:28:27 +08:00
idichekop
ae1014c572 fix(package):[function] Corrected behaviour of Nand predicate (#319) 2025-07-22 10:08:50 +08:00
dudaodong
d5b9e67330 fix: fix go lint issue 2025-07-07 11:18:34 +08:00
dudaodong
a81403766f feat: add ToPointers, FromPointer, FromPointers 2025-07-07 11:16:44 +08:00
dudaodong
6307d624cb Merge branch 'rc' of github.com:duke-git/lancet into rc 2025-07-07 10:18:37 +08:00
dudaodong
2f9f8b3f3d release v2.3.7 2025-07-07 10:18:05 +08:00
jake
db5d9407bb Update slice_concurrent.go (#316) 2025-06-25 15:33:07 +08:00
jake
83c069e234 Update slice_concurrent.go 2025-06-24 19:14:37 +08:00
dudaodong
55ee000684 merge rc 2025-06-23 11:32:51 +08:00
dudaodong
fc7f2509ca fix: fix issue #314 2025-06-23 11:31:54 +08:00
残念
a97d27c32e perf(retry): the error returned by the Retry function contains the last error (#315) 2025-06-23 11:04:54 +08:00
Grigoris Thanasoulas
c176ba378e arrayqueue: Fix bug in Back() method (#313) 2025-06-20 23:05:14 +08:00
dudaodong
a3a24fc381 doc: update doc styles 2025-06-06 14:33:54 +08:00
Axiss
9caf2ffb1c fix one typo in doc (#310) 2025-06-03 10:10:51 +08:00
残念
1a5c31fd02 perf(retry): remove the waiting time after the last retry (#309) 2025-06-03 09:57:29 +08:00
dudaodong
c175b202de doc: update cryptordoc 2025-05-29 14:04:09 +08:00
dudaodong
d818219672 doc: update cryptordoc 2025-05-29 14:01:12 +08:00
dudaodong
539078e6b8 doc: add play ground demo for v2.3.6 2025-05-29 11:50:17 +08:00
dudaodong
cdefbde9f5 release v2.3.6 2025-05-29 10:07:19 +08:00
dudaodong
1e57a743af Merge branch 'rc' into v2 2025-05-29 10:05:13 +08:00
Yurun
7d4184a365 doc: fix typo (#308) 2025-05-29 10:01:38 +08:00
dudaodong
1b73483945 doc: update doc for v2.3.6 2025-05-28 11:30:12 +08:00
RigelShrimp
622aacaf44 doc: fix typo in chinese translation (#305) 2025-05-14 15:09:10 +08:00
燕归来
e78ac65605 fix: Fixed the issue of missing cap when StringToBytes returns result (#306)
Co-authored-by: 燕归来 <dylan@infinni.io>
2025-05-14 15:08:12 +08:00
dudaodong
03f0d4d905 fix: fix ExampleFindValuesBy 2025-04-29 10:18:51 +08:00
dudaodong
b2ae71c983 update rsa crypto file 2025-04-29 10:04:11 +08:00
dudaodong
093f4a2286 doc: add documention for keyed locker 2025-04-28 19:32:51 +08:00
dudaodong
f7ada6093c feat: add example for keyed_locker 2025-04-27 19:44:15 +08:00
dudaodong
47e82aad39 refactor: refact some sliceutil functions 2025-04-24 10:38:43 +08:00
dudaodong
9e813d236b fix: fix issue #159 2025-04-22 14:46:03 +08:00
dudaodong
72a23d2cb9 fix: fix issue #159 and refact crypto function 2025-04-22 14:22:01 +08:00
dudaodong
27667f8b3a fix: fix bugs in test 2025-04-21 15:08:16 +08:00
dudaodong
0b5dc86d70 refactor: refact some strutil functions 2025-04-21 14:07:52 +08:00
dudaodong
d88bba07dd feat: add TryKeyedLocker 2025-04-21 10:49:48 +08:00
dudaodong
d7f3354b98 feat: add RWKeyedLocker 2025-04-17 14:25:19 +08:00
dudaodong
f4dee28ebb feat: add KeyedLocker 2025-04-17 14:06:59 +08:00
残念
c841a5b88c Merge pull request #301 from ppd324/main
feat: add ToMap for stream
2025-04-14 10:34:17 +08:00
Peidong Pei
333038634b Update stream.go 2025-04-07 11:15:39 +08:00
Peidong Pei
ef0fed23b2 Update stream_example_test.go 2025-04-07 11:14:42 +08:00
dudaodong
1e0ee1fac1 fix: fix issue #302 2025-04-03 10:23:03 +08:00
Pei PeiDong
4947327ed6 feat: add ToMap for stream 2025-04-02 10:53:46 +08:00
dudaodong
ceb706b874 feat: add FindValuesBy 2025-04-01 11:35:24 +08:00
dudaodong
3849337919 doc: update example link for BeginOfWeek and EndOfWeek 2025-03-31 10:27:09 +08:00
CyJaySong
f4427b9fbc BeginOfWeek,EndOfWeek 的beginFrom和endWith改为必传 (#300) 2025-03-31 10:18:50 +08:00
dudaodong
0ef45b533b fix: fix issue #292, change query params type to map[string][]string 2025-03-28 14:00:11 +08:00
dudaodong
169f280c9c feat: add RemoveDir 2025-03-28 11:06:52 +08:00
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
dudaodong
4ffff0e3f3 update pem file 2024-12-04 11:26:49 +08:00
dudaodong
8ebb8a028e doc: update doc for v2.3.4 2024-12-04 11:24:53 +08:00
dudaodong
995ffb799f fix: github action failed 2024-12-02 17:19:47 +08:00
dudaodong
3cd546d7f2 fix: fix issue #274 2024-12-02 17:06:06 +08:00
dudaodong
7fb49515ce feat: add ToBigInt 2024-12-02 11:25:16 +08:00
116 changed files with 12988 additions and 2021 deletions

331
README.md
View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.8-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)
[![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)
@@ -39,7 +39,7 @@
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.4. </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 get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
@@ -84,6 +84,8 @@ func main() {
- [Cryptor](#user-content-cryptor)
- [Datetime](#user-content-datetime)
- [Datastructure](#user-content-datastructure)
- [EventBus](#user-content-eventbus)
- [Enum](#user-content-enum)
- [Fileutil](#user-content-fileutil)
- [Formatter](#user-content-formatter)
- [Function](#user-content-function)
@@ -214,6 +216,30 @@ import "github.com/duke-git/lancet/v2/concurrency"
- **<big>Tee</big>** : split one chanel into two channels, until cancel the context.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Tee)]
[[play](https://go.dev/play/p/3TQPKnCirrP)]
- **<big>NewKeyedLocker</big>** : KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewKeyedLocker)]
[[play](https://go.dev/play/p/GzeyC33T5rw)]
- **<big>Do</big>** :acquires a lock for the specified key and executes the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Do)]
[[play](https://go.dev/play/p/GzeyC33T5rw)]
- **<big>NewRWKeyedLocker</big>** :RRWKeyedLocker is a read-write version of KeyedLocker.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewRWKeyedLocker)]
[[play](https://go.dev/play/p/ZrCr8sMo77T)]
- **<big>RLock</big>** : acquires a read lock for the specified key and executes the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RLock)]
[[play](https://go.dev/play/p/ZrCr8sMo77T)]
- **<big>Lock</big>** : acquires a write lock for the specified key and executes the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Lock)]
[[play](https://go.dev/play/p/WgAcXbOPKGk)]
- **<big>NewTryKeyedLocker</big>** : TryKeyedLocker is a non-blocking version of KeyedLocker.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewTryKeyedLocker)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
- **<big>TryLock</big>** : TryLock tries to acquire a lock for the specified key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#TryLock)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
- **<big>Unlock</big>** : Unlock releases the lock for the specified key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Unlock)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
<h3 id="condition"> 4. Condition package contains some functions for conditional judgment. eg. And, Or, TernaryOperator...&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a> </h3>
@@ -289,6 +315,15 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>ToPointer</big>** : return a pointer of passed value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToPointer)]
[[play](https://go.dev/play/p/ASf_etHNlw1)]
- **<big>ToPointers</big>** : convert a slice of values to a slice of pointers.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToPointers)]
[[play](https://go.dev/play/p/ZUoXd2i5ZkV)]
- **<big>FromPointer</big>** : returns the value pointed to by the pointer.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#FromPointer)]
[[play](https://go.dev/play/p/wAp90V7Zu6g)]
- **<big>FromPointers</big>** : convert a slice of pointers to a slice of values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#FromPointers)]
[[play](https://go.dev/play/p/qIPsyYtNy3Q)]
- **<big>ToString</big>** : convert value to string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToString)]
[[play](https://go.dev/play/p/nF1zOOslpQq)]
@@ -321,12 +356,19 @@ import "github.com/duke-git/lancet/v2/convertor"
[[play](https://go.dev/play/p/OphmHCN_9u8)]
- **<big>ToStdBase64</big>** : converts a value to a string encoded in standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToStdBase64)]
[[play](https://go.dev/play/p/_fLJqJD3NMo)]
- **<big>ToUrlBase64</big>** : converts a value to a string encoded in url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToUrlBase64)]
[[play](https://go.dev/play/p/C_d0GlvEeUR)]
- **<big>ToRawStdBase64</big>** : converts a value to a string encoded in raw standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawStdBase64)]
[[play](https://go.dev/play/p/wSAr3sfkDcv)]
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
[[play](https://go.dev/play/p/HwdDPFcza1O)]
- **<big>ToBigInt</big>** : converts an integer of any supported type (int, int64, uint64, etc.) to \*big.Int.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToBigInt)]
[[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>
@@ -462,10 +504,10 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/zutRHrDqs0X)]
- **<big>RsaEncrypt</big>** : encrypt data with ras algorithm.
[[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.
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
@@ -475,7 +517,12 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>RsaDecryptOAEP</big>** : decrypts the data with RSA-OAEP
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaDecryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaSign</big>** : signs the data with RSA.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaSign)]
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
- **<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)]
[[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>
@@ -494,9 +541,24 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<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)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddYear)]
[[play](https://go.dev/play/p/MqW2ujnBx10)]
- **<big>AddDaySafe</big>** : add or sub days to the time and ensure that the returned date does not exceed the valid date of the target year and month.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddDaySafe)]
[[play](https://go.dev/play/p/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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfMinute)]
[[play](https://go.dev/play/p/ieOLVJ9CiFT)]
@@ -508,7 +570,7 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/94m_UT6cWs9)]
- **<big>BeginOfWeek</big>** : return the date time at the begin of week of specific date.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfWeek)]
[[play](https://go.dev/play/p/ynjoJPz7VNV)]
[[play](https://go.dev/play/p/DCHdcL6gnfV)]
- **<big>BeginOfMonth</big>** : return the date time at the begin of month of specific date.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfMonth)]
[[play](https://go.dev/play/p/bWXVFsmmzwL)]
@@ -526,7 +588,7 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/eMBOvmq5Ih1)]
- **<big>EndOfWeek</big>** : return the date time at the end of week of specific date.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfWeek)]
[[play](https://go.dev/play/p/i08qKXD9flf)]
[[play](https://go.dev/play/p/mGSA162YgX9)]
- **<big>EndOfMonth</big>** : return the date time at the end of month of specific date.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfMonth)]
[[play](https://go.dev/play/p/_GWh10B3Nqi)]
@@ -619,7 +681,15 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>GenerateDatetimesBetween</big>** : returns a slice of strings between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](https://go.dev/play/p/6kHBpAxD9ZC)]
- **<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)]
[[play](https://go.dev/play/p/MCIDvHNOGGb)]
- **<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)]
[[play](https://go.dev/play/p/9m6JMk1LB7-)]
- **<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)]
[[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>
@@ -658,9 +728,110 @@ import optional "github.com/duke-git/lancet/v2/datastructure/optional"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
- **<big>Optional</big>** : Optional container.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<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"
```
#### Function list:
- **<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="enum"> 10. Package enum provides a simple enum implementation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">Index</a></h3>
```go
import "github.com/duke-git/lancet/v2/enum"
```
#### Function list:
- **<big>NewItem</big>** : Creates a new enum item.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#NewEventBus)]
[[play](https://go.dev/play/p/8qNsLw01HD5)]
- **<big>NewItemsFromPairs</big>** : Creates enum items from a slice of Pair structs.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#NewItemsFromPairs)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Value</big>** : Returns the value of the enum item.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Value)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Name</big>** : Returns the name of the enum item.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Name)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Valid</big>** : Checks if the enum item is valid. If a custom check function is provided, it will be used to validate the value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Valid)]
[[play](https://go.dev/play/p/pA3lYY2VSm3)]
- **<big>MarshalJSON</big>** : Implementation of json.Marshaler interface.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#MarshalJSON)]
[[play](https://go.dev/play/p/zIZEdAnneB5)]
- **<big>NewRegistry</big>** : Creates a new enum registry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#NewRegistry)]
[[play](https://go.dev/play/p/ABEXsYfJKMo)]
- **<big>Add</big>** : Adds enum items to the registry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Add)]
[[play](https://go.dev/play/p/ABEXsYfJKMo)]
- **<big>Remove</big>** : Removes an enum item from the registry by its value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Remove)]
[[play](https://go.dev/play/p/dSG84wQ3TuC)]
- **<big>Update</big>** : Updates the name of an enum item in the registry by its value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Update)]
[[play](https://go.dev/play/p/Ol0moT1J9Xl)]
- **<big>GetByValue</big>** : Retrieves an enum item by its value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#GetByValue)]
[[play](https://go.dev/play/p/niJ1U2KlE_m)]
- **<big>GetByName</big>** : Retrieves an enum item by its name.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#GetByName)]
[[play](https://go.dev/play/p/49ie_gpqH0m)]
- **<big>Items</big>** : Returns a slice of all enum items in the registry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Items)]
[[play](https://go.dev/play/p/lAJFAradbvQ)]
- **<big>Contains</big>** : Checks if an enum item with the given value exists in the registry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Contains)]
[[play](https://go.dev/play/p/_T-lPYkZn2j)]
- **<big>Size</big>** : Returns the number of enum items in the registry.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Size)]
[[play](https://go.dev/play/p/TeDArWhlQe2)]
- **<big>Range</big>** : Iterates over all enum items in the registry and applies the given function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Range)]
[[play](https://go.dev/play/p/GPsZbQbefWN)]
- **<big>SortedItems</big>** : Returns a slice of all enum items sorted by the given less function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#SortedItems)]
[[play](https://go.dev/play/p/tN9RE_m_WEI)]
- **<big>Filter</big>** : Returns a slice of enum items that satisfy the given predicate function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/enum.md#Filter)]
[[play](https://go.dev/play/p/uTUpTdcyoCU)]
<h3 id="fileutil"> 11. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/fileutil"
@@ -704,6 +875,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>RemoveFile</big>** : remove file, param should be file path.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#RemoveFile)]
[[play](https://go.dev/play/p/P2y0XW8a1SH)]
- **<big>RemoveDir</big>** : delete directory.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#RemoveDir)]
[[play](https://go.dev/play/p/Oa6KnPek2uy)]
- **<big>ReadFileToString</big>** : return string of file content.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFileToString)]
[[play](https://go.dev/play/p/cmfwp_5SQTp)]
@@ -755,8 +929,11 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>ParallelChunkRead</big>** : reads the file in parallel and send each chunk of lines to the specified channel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
- **<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)]
[[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"> 12. Formatter contains some functions for data formatting. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/formatter"
@@ -786,7 +963,7 @@ import "github.com/duke-git/lancet/v2/formatter"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#ParseBinaryBytes)]
[[play](https://go.dev/play/p/69v1tTT62x8)]
<h3 id="function"> 11. Function package can control the flow of function execution and support part of functional programming.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="function"> 13. Function package can control the flow of function execution and support part of functional programming.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/function"
@@ -849,8 +1026,7 @@ import "github.com/duke-git/lancet/v2/function"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="maputil"> 14. Maputil package includes some functions to manipulate map.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/maputil"
@@ -1020,9 +1196,14 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>GetOrDefault</big>** : returns the value of the given key or a default value if the key is not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrDefault)]
[[play](https://go.dev/play/p/99QjSYSBdiM)]
- **<big>FindValuesBy</big>** : returns a slice of values from the map that satisfy the given predicate function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#FindValuesBy)]
[[play](https://go.dev/play/p/bvNwNBZDm6v)]
- **<big>ToMarkdownTable</big>** : Convert a map slice data to a Markdown table string. It supports custom header display names and column display order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToMarkdownTable)]
[[play](https://go.dev/play/p/w_pSLfeyEB5)]
<h3 id="mathutil"> 13. Mathutil package implements some functions for math calculation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="mathutil"> 15. Mathutil package implements some functions for math calculation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/mathutil"
@@ -1120,9 +1301,20 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Div</big>** : returns the result of x divided by y.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Div)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
- **<big>Variance</big>** : returns the variance of numbers.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Variance)]
[[play](https://go.dev/play/p/uHuV4YgXf8F)]
- **<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)]
[[play](https://go.dev/play/p/FkNZDXvHD2l)]
- **<big>Permutation</big>** : calculates P(n, k).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Permutation)]
[[play](https://go.dev/play/p/MgobwH_FOxj)]
- **<big>Combination</big>** : calculates C(n, k).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Combination)]
[[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"> 16. Netutil package contains functions to get net information and send http request. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/netutil"
@@ -1194,8 +1386,14 @@ import "github.com/duke-git/lancet/v2/netutil"
- **<big>IsTelnetConnected</big>** : checks if can if can telnet the specified host or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#IsTelnetConnected)]
[[play](https://go.dev/play/p/yiLCGtQv_ZG)]
- **<big>BuildUrl</big>** : builds a URL from the given params.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#BuildUrl)]
[[play](https://go.dev/play/p/JLXl1hZK7l4)]
- **<big>AddQueryParams</big>** : adds query parameters to the given URL.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#AddQueryParams)]
[[play](https://go.dev/play/p/JLXl1hZK7l4)]
<h3 id="pointer"> 15. Pointer package contains some util functions to operate go pointer. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="pointer"> 17. Pointer package contains some util functions to operate go pointer. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/pointer"
@@ -1219,7 +1417,7 @@ import "github.com/duke-git/lancet/v2/pointer"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOrDefault)]
[[play](https://go.dev/play/p/ZnGIHf8_o4E)]
<h3 id="random"> 16. Random package implements some basic functions to generate random int and string. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="random"> 18. Random package implements some basic functions to generate random int and string. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/random"
@@ -1281,9 +1479,11 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandSliceFromGivenSlice</big>** : generate a random slice of length num from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](https://go.dev/play/p/68UikN9d6VT)]
- **<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)]
[[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"> 19. 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>
```go
import "github.com/duke-git/lancet/v2/retry"
@@ -1317,9 +1517,8 @@ import "github.com/duke-git/lancet/v2/retry"
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="slice"> 20. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/slice"
@@ -1339,6 +1538,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ContainSubSlice</big>** : check if the slice contain a given subslice or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ContainSubSlice)]
[[play](https://go.dev/play/p/bcuQ3UT6Sev)]
- **<big>ContainAny</big>** : check if the slice contains any element from the targets slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ContainAny)]
[[play](https://go.dev/play/p/4xoxhc9XSSw)]
- **<big>Chunk</big>** : creates a slice of elements split into groups the length of size.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Chunk)]
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
@@ -1387,6 +1589,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<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)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Every)]
[[play](https://go.dev/play/p/R8U6Sl-j8cD)]
@@ -1486,6 +1691,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Shuffle</big>** : shuffle the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Shuffle)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsAscending)]
[[play](https://go.dev/play/p/9CtsFjet4SH)]
@@ -1558,7 +1766,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Partition</big>** : partition all slice elements with the evaluation of the given predicate functions.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Partition)]
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
@@ -1575,9 +1783,14 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Frequency</big>** : counts the frequency of each element in the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Frequency)]
[[play](https://go.dev/play/p/CW3UVNdUZOq)]
- **<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)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ConcatBy)]
[[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"> 21. 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>
```go
import "github.com/duke-git/lancet/v2/stream"
@@ -1627,6 +1840,9 @@ import "github.com/duke-git/lancet/v2/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)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Range)]
[[play](https://go.dev/play/p/indZY5V2f4j)]
@@ -1666,8 +1882,14 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>ToSlice</big>** : returns the elements in the stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#ToSlice)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#IndexOf)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#LastIndexOf)]
[[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"> 22. Structs package provides several high level functions to manipulate struct, tag, and field. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/structs"
@@ -1677,32 +1899,51 @@ import "github.com/duke-git/lancet/v2/structs"
- **<big>New</big>** : creates a `Struct` instance.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#New)]
[[play](https://go.dev/play/p/O29l8kk-Z17)]
- **<big>ToMap</big>** : converts a valid struct to a map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#ToMap)]
[[play](https://go.dev/play/p/qQbLySBgerZ)]
- **<big>Fields</big>** : get all fields of a given struct, that the fields are abstract struct field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Fields)]
[[play](https://go.dev/play/p/w3Kk_CyVY7D)]
- **<big>Field</big>** : get an abstract field of a struct by given field name
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Field)]
[[play](https://go.dev/play/p/KocZMSYarza)]
- **<big>IsStruct</big>** : check if the struct is valid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsStruct)]
[[play](https://go.dev/play/p/bU2FSdkbK1C)]
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Tag)]
[[play](https://go.dev/play/p/DVrx5HvvUJr)]
- **<big>Name</big>** : get the field name.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Name)]
[[play](https://go.dev/play/p/zfIGlqsatee)]
- **<big>Value</big>** : get the `Field` underlying value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Value)]
[[play](https://go.dev/play/p/qufYEU2o4Oi)]
- **<big>Kind</big>** : get the field's kind.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Kind)]
[[play](https://go.dev/play/p/wg4NlcUNG5o)]
- **<big>IsEmbedded</big>** : check if the field is an embedded field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsEmbedded)]
[[play](https://go.dev/play/p/wV2PrbYm3Ec)]
- **<big>IsExported</big>** : check if the field is exported.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsExported)]
[[play](https://go.dev/play/p/csK4AXYaNbJ)]
- **<big>IsZero</big>** : check if the field is zero value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsZero)]
[[play](https://go.dev/play/p/RzqpGISf87r)]
- **<big>IsSlice</big>** : check if the field is a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsSlice)]
[[play](https://go.dev/play/p/MKz4CgBIUrU)]
- **<big>IsTargetType</big>** : check if the field is target type.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsTargetType)]
[[play](https://go.dev/play/p/Ig75P-agN39)]
- **<big>TypeName</big>** : Return struct type name.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#TypeName)]
[[play](https://go.dev/play/p/SWLWd0XBaBb)]
<h3 id="strutil"> 21. Strutil package contains some functions to manipulate string. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="strutil"> 23. Strutil package contains some functions to manipulate string. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/strutil"
@@ -1847,9 +2088,14 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>RegexMatchAllGroups</big>** : matches all subgroups in a string using a regular expression and returns the result.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RegexMatchAllGroups)]
[[play](https://go.dev/play/p/JZiu0RXpgN-)]
- **<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)]
[[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"> 24. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/system"
@@ -1897,8 +2143,7 @@ import "github.com/duke-git/lancet/v2/system"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)]
[[play](https://go.dev/play/p/NQDVywEYYx7)]
<h3 id="tuple"> 23. Tuple package implements tuple data type and some operations on it. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="tuple"> 25. Tuple package implements tuple data type and some operations on it. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/tuple"
@@ -2015,7 +2260,7 @@ import "github.com/duke-git/lancet/v2/tuple"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip10)]
[[play](https://go.dev/play/p/-taQB6Wfre_z)]
<h3 id="validator"> 24. Validator package contains some functions for data validation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="validator"> 26. Validator package contains some functions for data validation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/validator"
@@ -2080,6 +2325,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsNumberStr</big>** : check if the string can convert to a number.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsNumberStr)]
[[play](https://go.dev/play/p/LzaKocSV79u)]
- **<big>IsAlphaNumeric</big>** : check if the string is alphanumeric.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAlphaNumeric)]
[[play](https://go.dev/play/p/RHeESLrLg9c)]
- **<big>IsJSON</big>** : check if the string is valid JSON.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJSON)]
[[play](https://go.dev/play/p/8Kip1Itjiil)]
@@ -2101,6 +2349,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<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)]
[[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.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsStrongPassword)]
[[play](https://go.dev/play/p/QHdVcSQ3uDg)]
@@ -2149,8 +2400,14 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsChinaUnionPay</big>** : check if a give string is a valid china union pay number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChinaUnionPay)]
[[play](https://go.dev/play/p/yafpdxLiymu)]
- **<big>IsPassport</big>** : Passport validation(using regex).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsPassport)]
[[play](https://go.dev/play/p/dvOiV2BW7Aw)]
- **<big>IsChineseHMPassport</big>** : Mainland travel permit for Hong Kong, Macao validation (using regex).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChineseHMPassport)]
[[play](https://go.dev/play/p/xKG6spQTcY0)]
<h3 id="xerror"> 25. Xerror package implements helpers for errors. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="xerror"> 27. Xerror package implements helpers for errors. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import "github.com/duke-git/lancet/v2/xerror"
@@ -2197,12 +2454,16 @@ import "github.com/duke-git/lancet/v2/xerror"
- **<big>TryUnwrap</big>** : check if err is nil then it returns a valid value. If err is not nil, TryUnwrap panics with err.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#TryUnwrap)]
[[play](https://go.dev/play/p/acyZVkNZEeW)]
- **<big>TryCatch</big>** : simple simulation of Java-style try-catch.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#TryCatch)]
[[play](https://go.dev/play/p/D5Mdb0mRj0P)]
## How to Contribute
#### [Contribution Guide](./CONTRIBUTION.md)
## Contributors
Thank you to all the people who contributed to lancet!
<a href="https://github.com/duke-git/lancet/graphs/contributors">

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.3-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.8-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)
[![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)
@@ -38,7 +38,7 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.4。</b>
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。</b>
```go
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
@@ -83,6 +83,8 @@ func main() {
- [Cryptor](#user-content-cryptor)
- [Datetime](#user-content-datetime)
- [Datastructure](#user-content-datastructure)
- [EventBus](#user-content-eventbus)
- [Enum](#user-content-enum)
- [Fileutil](#user-content-fileutil)
- [Formatter](#user-content-formatter)
- [Function](#user-content-function)
@@ -101,7 +103,6 @@ func main() {
- [Validator](#user-content-validator)
- [Xerror](#user-content-xerror)
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -214,6 +215,30 @@ import "github.com/duke-git/lancet/v2/concurrency"
- **<big>Tee</big>** : 将一个 channel 分成两个 channel直到取消上下文。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Tee)]
[[play](https://go.dev/play/p/3TQPKnCirrP)]
- **<big>NewKeyedLocker</big>** : NewKeyedLocker 创建一个新的 KeyedLocker并为锁的过期设置指定的 TTL。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewKeyedLocker)]
[[play](https://go.dev/play/p/GzeyC33T5rw)]
- **<big>Do</big>** :为指定的键获取锁并执行提供的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Do)]
[[play](https://go.dev/play/p/GzeyC33T5rw)]
- **<big>NewRWKeyedLocker</big>** :RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewRWKeyedLocker)]
[[play](https://go.dev/play/p/ZrCr8sMo77T)]
- **<big>RLock</big>** : 为指定的键获取读锁并执行提供的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#RLock)]
[[play](https://go.dev/play/p/ZrCr8sMo77T)]
- **<big>Lock</big>** : 为指定的键获取锁并执行提供的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Lock)]
[[play](https://go.dev/play/p/WgAcXbOPKGk)]
- **<big>NewTryKeyedLocker</big>** : 创建一个 TryKeyedLocker 实例TryKeyedLocker 是 KeyedLocker 的非阻塞版本。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewTryKeyedLocker)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
- **<big>TryLock</big>** : TryLock 尝试获取指定键的锁。如果锁成功获取,则返回 true否则返回 false。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#TryLock)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
- **<big>Unlock</big>** : 释放指定键的锁。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Unlock)]
[[play](https://go.dev/play/p/VG9qLvyetE2)]
<h3 id="condition"> 4. condition 包含一些用于条件判断的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -289,6 +314,15 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>ToPointer</big>** : 返回传入值的指针。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToPointer)]
[[play](https://go.dev/play/p/ASf_etHNlw1)]
- **<big>ToPointers</big>** : 将值的切片转换为指针的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToPointers)]
[[play](https://go.dev/play/p/ZUoXd2i5ZkV)]
- **<big>FromPointer</big>** : 返回指针所指向的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#FromPointer)]
[[play](https://go.dev/play/p/wAp90V7Zu6g)]
- **<big>FromPointers</big>** : 将指针的切片转换为值的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#FromPointers)]
[[play](https://go.dev/play/p/qIPsyYtNy3Q)]
- **<big>ToString</big>** : 将值转换为字符串,对于数字、字符串、[]byte将转换为字符串。 对于其他类型(切片、映射、数组、结构)将调用 json.Marshal。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToString)]
[[play](https://go.dev/play/p/nF1zOOslpQq)]
@@ -319,15 +353,21 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
[[play](https://go.dev/play/p/OphmHCN_9u8)]
- **<big>ToStdBase64</big>** : 将值转换为StdBase64编码的字符串。
- **<big>ToStdBase64</big>** : 将值转换为 StdBase64 编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)]
- **<big>ToUrlBase64</big>** : 将值转换为url Base64编码的字符串。
[[play](https://go.dev/play/p/_fLJqJD3NMo)]
- **<big>ToUrlBase64</big>** : 将值转换为 url Base64 编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)]
- **<big>ToRawStdBase64</big>** : 将值转换为RawStdBase64编码的字符串。
[[play](https://go.dev/play/p/C_d0GlvEeUR)]
- **<big>ToRawStdBase64</big>** : 将值转换为 RawStdBase64 编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)]
- **<big>ToRawUrlBase64</big>** : 将值转换为RawUrlBase64编码的字符串。
[[play](https://go.dev/play/p/wSAr3sfkDcv)]
- **<big>ToRawUrlBase64</big>** : 将值转换为 RawUrlBase64 编码的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)]
[[play](https://go.dev/play/p/HwdDPFcza1O)]
- **<big>ToBigInt</big>** : 将整数转为\*big.Int。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToBigInt)]
[[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>
@@ -349,9 +389,15 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>AesCbcDecrypt</big>** : 使用 AES CBC 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCbcDecrypt)]
[[play](https://go.dev/play/p/IOq_g8_lKZD)]
- **<big>AesCtrCrypt</big>** : 使用 AES CTR 算法模式加密/解密数据。
- **<big>AesCtrCrypt<sup>deprecated</sup></big>** : 使用 AES CTR 算法模式加密/解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCtrCrypt)]
[[play](https://go.dev/play/p/SpaZO0-5Nsp)]
- **<big>AesCtrEncrypt</big>** : 使用 AES CTR 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCtrCrypt)]
[[play](https://go.dev/play/p/x6pjPAvThRz)]
- **<big>AesCtrDecrypt</big>** : 使用 AES CTR 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCtrCrypt)]
[[play](https://go.dev/play/p/x6pjPAvThRz)]
- **<big>AesCfbEncrypt</big>** : 使用 AES CFB 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCfbEncrypt)]
[[play](https://go.dev/play/p/tfkF10B13kH)]
@@ -388,9 +434,15 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>DesCbcDecrypt</big>** : 使用 DES CBC 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCbcDecrypt)]
[[play](https://go.dev/play/p/4cC4QvWfe3_1)]
- **<big>DesCtrCrypt</big>** : 使用 DES CTR 算法模式加密/解密数据。
- **<big>DesCtrCrypt<sup>deprecated</sup></big>** : 使用 DES CTR 算法模式加密/解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCtrCrypt)]
[[play](https://go.dev/play/p/9-T6OjKpcdw)]
- **<big>DesCtrEncrypt</big>** : 使用 DES CTR 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCtrEncrypt)]
[[play](https://go.dev/play/p/S6p_WHCgH1d)]
- **<big>DesCtrDecrypt</big>** : 使用 DES CTR 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCtrDecrypt)]
[[play](https://go.dev/play/p/S6p_WHCgH1d)]
- **<big>DesCfbEncrypt</big>** : 使用 DES CFB 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCfbEncrypt)]
[[play](https://go.dev/play/p/y-eNxcFBlxL)]
@@ -464,20 +516,25 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/zutRHrDqs0X)]
- **<big>RsaEncrypt</big>** : 用公钥文件 ras 加密数据。
[[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 解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)]
- **<big>GenerateRsaKeyPair</big>** : 创建rsa公钥私钥和key。
[[play](https://go.dev/play/p/7_zo6mrx-eX)]
- **<big>GenerateRsaKeyPair</big>** : 创建 rsa 公钥私钥和 key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaEncryptOAEP</big>** : rsa OAEP加密。
- **<big>RsaEncryptOAEP</big>** : rsa OAEP 加密。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaDecryptOAEP</big>** : rsa OAEP解密。
- **<big>RsaDecryptOAEP</big>** : rsa OAEP 解密。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaSign</big>** : 应用 RSA 算法签名数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaSign)]
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
- **<big>RsaVerifySign</big>** : 验证数据的签名是否符合 RSA 算法。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)]
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -496,9 +553,24 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>AddMinute</big>** : 将日期加/减分钟数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMinute)]
[[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>** : 将日期加/减分年数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYear)]
[[play](https://go.dev/play/p/MqW2ujnBx10)]
- **<big>AddDaySafe</big>** : 增加/减少指定的天数,并确保日期是有效日期。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddDaySafe)]
[[play](https://go.dev/play/p/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>** : 返回指定时间的分钟开始时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMinute)]
[[play](https://go.dev/play/p/ieOLVJ9CiFT)]
@@ -510,7 +582,7 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/94m_UT6cWs9)]
- **<big>BeginOfWeek</big>** : 返回指定时间的每周开始时间,默认开始时间星期日。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfWeek)]
[[play](https://go.dev/play/p/ynjoJPz7VNV)]
[[play](https://go.dev/play/p/DCHdcL6gnfV)]
- **<big>BeginOfMonth</big>** : 返回指定时间的当月开始时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMonth)]
[[play](https://go.dev/play/p/bWXVFsmmzwL)]
@@ -528,7 +600,7 @@ import "github.com/duke-git/lancet/v2/datetime"
[[play](https://go.dev/play/p/eMBOvmq5Ih1)]
- **<big>EndOfWeek</big>** : 返回指定时间的星期结束时间,默认结束时间星期六。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfWeek)]
[[play](https://go.dev/play/p/i08qKXD9flf)]
[[play](https://go.dev/play/p/mGSA162YgX9)]
- **<big>EndOfMonth</big>** : 返回指定时间的月份结束时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfMonth)]
[[play](https://go.dev/play/p/_GWh10B3Nqi)]
@@ -619,10 +691,18 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>DaysBetween</big>** : 返回两个日期之间的天数差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)]
[[play](https://go.dev/play/p/qD6qGb3TbOy)]
- **<big>GenerateDatetimesBetween</big>** : 生成从startend的所有日期时间的字符串列表。
- **<big>GenerateDatetimesBetween</big>** : 生成从 startend 的所有日期时间的字符串列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](https://go.dev/play/p/6kHBpAxD9ZC)]
- **<big>Min</big>** : 返回最早时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Min)]
[[play](https://go.dev/play/p/MCIDvHNOGGb)]
- **<big>Max</big>** : 返回最晚时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Max)]
[[play](https://go.dev/play/p/9m6JMk1LB7-)]
- **<big>MaxMin</big>** : 返回最早和最晚时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#MaxMin)]
[[play](https://go.dev/play/p/rbW51cDtM_2)]
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -659,7 +739,109 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
- **<big>Hashmap</big>** : 哈希映射。
[[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="enum"> 10. Enum实现一个简单枚举工具包。. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">Index</a></h3>
```go
import "github.com/duke-git/lancet/v2/enum"
```
#### Function list:
- **<big>NewItem</big>** : 创建枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#NewItem)]
[[play](https://go.dev/play/p/8qNsLw01HD5)]
- **<big>NewItemsFromPairs</big>** : 从 Pair 结构体的切片创建枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#NewItemsFromPairs)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Value</big>** : 返回枚举项的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Value)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Name</big>** : 返回枚举项的名称。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Name)]
[[play](https://go.dev/play/p/xKnoGa7gnev)]
- **<big>Valid</big>** : 检查枚举项是否有效。如果提供了自定义检查函数,将使用该函数验证值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Valid)]
[[play](https://go.dev/play/p/pA3lYY2VSm3)]
- **<big>MarshalJSON</big>** : 枚举项实现 json.Marshaler 接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#MarshalJSON)]
[[play](https://go.dev/play/p/zIZEdAnneB5)]
- **<big>NewRegistry</big>** : Registry 定义了一个通用的枚举注册表结构体。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#NewRegistry)]
[[play](https://go.dev/play/p/ABEXsYfJKMo)]
- **<big>Add</big>** : 向枚举注册表添加枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Add)]
[[play](https://go.dev/play/p/ABEXsYfJKMo)]
- **<big>Remove</big>** : 在枚举注册表中删除枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Remove)]
[[play](https://go.dev/play/p/dSG84wQ3TuC)]
- **<big>Update</big>** : 在枚举注册表中更新枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Update)]
[[play](https://go.dev/play/p/Ol0moT1J9Xl)]
- **<big>GetByValue</big>** : 在枚举注册表中通过值获取枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#GetByValue)]
[[play](https://go.dev/play/p/niJ1U2KlE_m)]
- **<big>GetByName</big>** : 在枚举注册表中通过名称获取枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#GetByName)]
[[play](https://go.dev/play/p/49ie_gpqH0m)]
- **<big>Items</big>** : 返回枚举注册表中的枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Items)]
[[play](https://go.dev/play/p/lAJFAradbvQ)]
- **<big>Contains</big>** : 检查注册表中是否存在具有给定值的枚举项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Contains)]
[[play](https://go.dev/play/p/_T-lPYkZn2j)]
- **<big>Size</big>** : 返回注册表中枚举项的数目。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Size)]
[[play](https://go.dev/play/p/TeDArWhlQe2)]
- **<big>Range</big>** : 遍历注册表中的所有枚举项,并应用给定的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Range)]
[[play](https://go.dev/play/p/GPsZbQbefWN)]
- **<big>SortedItems</big>** : 返回按给定比较函数排序的所有枚举项的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#SortedItems)]
[[play](https://go.dev/play/p/tN9RE_m_WEI)]
- **<big>Filter</big>** : 返回满足给定谓词函数的枚举项切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/enum.md#Filter)]
[[play](https://go.dev/play/p/uTUpTdcyoCU)]
<h3 id="fileutil"> 11. fileutil 包含文件基本操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/fileutil"
@@ -703,6 +885,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>RemoveFile</big>** : 删除文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveFile)]
[[play](https://go.dev/play/p/P2y0XW8a1SH)]
- **<big>RemoveDir</big>** : 删除目录,支持传入删除前的回调函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveDir)]
[[play](https://go.dev/play/p/Oa6KnPek2uy)]
- **<big>ReadFileToString</big>** : 读取文件内容并返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFileToString)]
[[play](https://go.dev/play/p/cmfwp_5SQTp)]
@@ -735,9 +920,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
- **<big>WriteCsvFile</big>** : 向csv文件写入切片数据。
- **<big>WriteCsvFile</big>** : 向 csv 文件写入切片数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
- **<big>WriteMapsToCsv</big>** : 将 map 切片写入 csv 文件中。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
[[play](https://go.dev/play/p/umAIomZFV1c)]
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
@@ -746,7 +931,7 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>WriteStringToFile</big>** : 将字符串写入文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteStringToFile)]
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
- **<big>ReadFile</big>** : 读取文件或者URL。
- **<big>ReadFile</big>** : 读取文件或者 URL。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
@@ -754,10 +939,11 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
- **<big>GetExeOrDllVersion</big>** : 返回 exe,dll 文件版本号(仅 Window 平台)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#GetExeOrDllVersion)]
[[play](https://go.dev/play/p/iLRrDBhE38E)]
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="formatter"> 12. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/formatter"
@@ -787,7 +973,7 @@ import "github.com/duke-git/lancet/v2/formatter"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#ParseBinaryBytes)]
[[play](https://go.dev/play/p/69v1tTT62x8)]
<h3 id="function"> 11. function 函数包控制函数执行流程,包含部分函数式编程。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="function"> 13. function 函数包控制函数执行流程,包含部分函数式编程。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/function"
@@ -825,34 +1011,32 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Pipeline</big>** : 从右至左执行函数列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数该函数的签名与apply函数相同但同时还会包含一个布尔值来表示成功或失败。
- **<big>AcceptIf</big>** : AcceptIf 函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
- **<big>And</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑and操作。
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 and 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
- **<big>Or</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑or操作。
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 or 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
[[play](https://go.dev/play/p/LitCIsDFNDA)]
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
- **<big>Nor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非或nor的操作。
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或 nor 的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
[[play](https://go.dev/play/p/2KdCoBEOq84)]
- **<big>Nand</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非与nand的操作。
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与 nand 的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
- **<big>Xnor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑异或xnor的操作。
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或 xnor 的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
[[play](https://go.dev/play/p/FJxko8SFbqc)]
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="maputil"> 14. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/maputil"
@@ -929,13 +1113,13 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : 将map转成struct。
- **<big>MapToStruct</big>** : 将 map 转成 struct。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。
- **<big>ToSortedSlicesDefault</big>** : 将 map 的 key 和 value 转化成两个根据 key 的值从小到大排序的切片value 切片中元素的位置与 key 对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
[[play](https://go.dev/play/p/43gEM2po-qy)]
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。
- **<big>ToSortedSlicesWithComparator</big>** : 将 map 的 key 和 value 转化成两个使用比较器函数根据 key 的值自定义排序规则的切片value 切片中元素的位置与 key 对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewOrderedMap</big>** : 创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。
@@ -950,7 +1134,7 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>OrderedMap_Delete</big>** : 删除给定键的键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Delete)]
[[play](ttps://go.dev/play/p/5bIi4yaZ3K-)]
- **<big>OrderedMap_Clear</big>** : 清空map数据。
- **<big>OrderedMap_Clear</big>** : 清空 map 数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Clear)]
[[play](https://go.dev/play/p/8LwoJyEfuFr)]
- **<big>OrderedMap_Front</big>** : 返回第一个键值对。
@@ -974,7 +1158,7 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>OrderedMap_Len</big>** : 返回键值对的数量。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Len)]
[[play](https://go.dev/play/p/cLe6z2VX5N-)]
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回true。
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回 true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Contains)]
[[play](https://go.dev/play/p/QuwqqnzwDNX)]
- **<big>OrderedMap_Iter</big>** : 返回按顺序产生键值对的通道。
@@ -983,13 +1167,13 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>OrderedMap_ReverseIter</big>** : 返回以相反顺序产生键值对的通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_ReverseIter)]
[[play](https://go.dev/play/p/8Q0ssg6hZzO)]
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序map key。
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序 map key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_SortByKey)]
[[play](https://go.dev/play/p/N7hjD_alZPq)]
- **<big>OrderedMap_MarshalJSON</big>** : 实现json.Marshaler接口。
- **<big>OrderedMap_MarshalJSON</big>** : 实现 json.Marshaler 接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_MarshalJSON)]
[[play](https://go.dev/play/p/C-wAwydIAC7)]
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现json.Unmarshaler接口。
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现 json.Unmarshaler 接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
[[play](https://go.dev/play/p/8C3MvJ3-mut)]
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
@@ -1016,14 +1200,20 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : 为 map 中每个键和值顺序调用迭代器。 如果 iterator 返回 false则停止迭代。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : 对传入的map根据key进行排序。
- **<big>SortByKey</big>** : 对传入的 map 根据 key 进行排序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#SortByKey)]
[[play](https://go.dev/play/p/PVdmBSnm6P_W)]
- **<big>GetOrDefault</big>** : 返回给定键的值,如果键不存在,则返回默认值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
[[play](https://go.dev/play/p/99QjSYSBdiM)]
- **<big>FindValuesBy</big>** : 返回一个切片,包含满足给定谓词判断函数的 map 中的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#FindValuesBy)]
[[play](https://go.dev/play/p/bvNwNBZDm6v)]
- **<big>ToMarkdownTable</big>** : 将一个 map 切片数据转换为 Markdown 表格字符串。支持自定义表头显示名称和列的显示顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToMarkdownTable)]
[[play](https://go.dev/play/p/w_pSLfeyEB5)]
<h3 id="mathutil"> 13. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="mathutil"> 15. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/mathutil"
@@ -1067,16 +1257,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
[[play](https://go.dev/play/p/aumarSHIGzP)]
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留 n 位小数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
[[play](https://go.dev/play/p/8hOeSADZPCo)]
- **<big>CeilToString</big>** : 向上舍入(进一法),保留n位小数,返回字符串。
- **<big>CeilToString</big>** : 向上舍入(进一法),保留 n 位小数,返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留n位小数。
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留 n 位小数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
[[play](https://go.dev/play/p/vbCBrQHZEED)]
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留n位小数,返回字符串。
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留 n 位小数,返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
@@ -1121,9 +1311,20 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Div</big>** : 除法运算。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
- **<big>Variance</big>** : 计算方差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Variance)]
[[play](https://go.dev/play/p/uHuV4YgXf8F)]
- **<big>StdDev</big>** : 计算标准差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#StdDev)]
[[play](https://go.dev/play/p/FkNZDXvHD2l)]
- **<big>Permutation</big>** : 计算排列数 P(n, k)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Permutation)]
[[play](https://go.dev/play/p/MgobwH_FOxj)]
- **<big>Combination</big>** : 计算组合数 C(n, k)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Combination)]
[[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"> 16. netutil 网络包支持获取 ip 地址,发送 http 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/netutil"
@@ -1195,8 +1396,14 @@ import "github.com/duke-git/lancet/v2/netutil"
- **<big>IsTelnetConnected</big>** : 检查能否 telnet 到主机。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsTelnetConnected)]
[[play](https://go.dev/play/p/yiLCGtQv_ZG)]
- **<big>BuildUrl</big>** : 创建 url 字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#BuildUrl)]
[[play](https://go.dev/play/p/JLXl1hZK7l4)]
- **<big>AddQueryParams</big>** : 向 url 添加查询参数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#AddQueryParams)]
[[play](https://go.dev/play/p/JLXl1hZK7l4)]
<h3 id="pointer"> 15. pointer 包支持一些指针类型的操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="pointer"> 17. pointer 包支持一些指针类型的操作。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/pointer"
@@ -1220,7 +1427,7 @@ import "github.com/duke-git/lancet/v2/pointer"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#UnwrapOrDefault)]
[[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"> 18. random 随机数生成器包,可以生成随机[]bytes, int, string。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/random"
@@ -1252,28 +1459,28 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机 int 切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSymbolChar)]
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
- **<big>RandFloat</big>** : 生成随机float64数字可以指定范围和精度。
- **<big>RandFloat</big>** : 生成随机 float64 数字,可以指定范围和精度。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloat)]
[[play](https://go.dev/play/p/zbD_tuobJtr)]
- **<big>RandFloats</big>** : 生成随机float64数字切片可以指定长度范围和精度.
- **<big>RandFloats</big>** : 生成随机 float64 数字切片,可以指定长度,范围和精度.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandStringSlice</big>** : 生成随机字符串slice。
- **<big>RandStringSlice</big>** : 生成随机字符串 slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)]
[[play](https://go.dev/play/p/2_-PiDv3tGn)]
- **<big>RandBool</big>** : 生成随机bool值(true or false)。
- **<big>RandBool</big>** : 生成随机 bool 值(true or false)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBool)]
[[play](https://go.dev/play/p/to6BLc26wBv)]
- **<big>RandBoolSlice</big>** : 生成特定长度的随机bool slice。
- **<big>RandBoolSlice</big>** : 生成特定长度的随机 bool slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBoolSlice)]
[[play](https://go.dev/play/p/o-VSjPjnILI)]
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机int切片数值范围[min, max)。
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机 int 切片,数值范围[min, max)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandIntSlice)]
[[play](https://go.dev/play/p/GATTQ5xTEG8)]
- **<big>RandFromGivenSlice</big>** : 从给定切片中随机生成元素。
@@ -1282,9 +1489,11 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandSliceFromGivenSlice</big>** : 从给定切片中生成长度为 num 的随机切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](https://go.dev/play/p/68UikN9d6VT)]
- **<big>RandNumberOfLength</big>** : 生成指定长度的随机数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumberOfLength)]
[[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"> 19. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/retry"
@@ -1316,9 +1525,7 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
<h3 id="slice"> 20. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/slice"
@@ -1338,6 +1545,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ContainSubSlice</big>** : 判断 slice 是否包含 subslice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ContainSubSlice)]
[[play](https://go.dev/play/p/bcuQ3UT6Sev)]
- **<big>ContainAny</big>** : 判断 slice 是否包含 targets 切片中的任意一个元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ContainAny)]
[[play](https://go.dev/play/p/4xoxhc9XSSw)]
- **<big>Chunk</big>** : 按照 size 参数均分 slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Chunk)]
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
@@ -1386,6 +1596,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>EqualWith</big>** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数 comparator返回 true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualWith)]
[[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。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Every)]
[[play](https://go.dev/play/p/R8U6Sl-j8cD)]
@@ -1419,7 +1632,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEach)]
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
- **<big>ForEachConcurrent</big>** : 对slice并发执行foreach操作。
- **<big>ForEachConcurrent</big>** : 对 slice 并发执行 foreach 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachConcurrent)]
[[play](https://go.dev/play/p/kT4XW7DKVoV)]
- **<big>ForEachWithBreak</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。
@@ -1452,7 +1665,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Map</big>** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Map)]
[[play](https://go.dev/play/p/biaTefqPquw)]
- **<big>MapConcurrent</big>** : 对slice并发执行map操作。
- **<big>MapConcurrent</big>** : 对 slice 并发执行 map 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#MapConcurrent)]
[[play](https://go.dev/play/p/H1ehfPkPen0)]
- **<big>Merge</big>** : 合并多个切片(不会消除重复元素)。
@@ -1461,6 +1674,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Reverse</big>** : 反转切片中的元素顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reverse)]
[[play](https://go.dev/play/p/8uI8f1lwNrQ)]
- **<big>ReverseCopy</big>** : 反转切片中的元素顺序, 不改变原 slice。
[[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)
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reduce)]
[[play](https://go.dev/play/p/_RfXJJWIsIm)]
@@ -1470,7 +1686,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ReduceRight</big>** : 类似 ReduceBy 操作,迭代切片元素顺序从右至左。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceRight)]
[[play](https://go.dev/play/p/qT9dZC03A1K)]
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发reduce操作。
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发 reduce 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceConcurrent)]
[[play](https://go.dev/play/p/Tjwe6OtaG07)]
- **<big>Replace</big>** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。
@@ -1485,6 +1701,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Shuffle</big>** : 随机打乱切片中的元素顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Shuffle)]
[[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>** : 检查切片元素是否按升序排列。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsAscending)]
[[play](https://go.dev/play/p/9CtsFjet4SH)]
@@ -1530,7 +1749,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
[[play](https://go.dev/play/p/rwSacr-ZHsR)]
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
- **<big>UniqueByField</big>** : 根据 struct 字段对 struct 切片去重复。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>UniqueByConcurrent</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
@@ -1553,13 +1772,13 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Join</big>** : 用指定的分隔符链接切片元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Join)]
[[play](https://go.dev/play/p/huKzqwNDD7V)]
- **<big>Partition</big>** : 根据给定的predicate判断函数分组切片元素。
- **<big>Partition</big>** : 根据给定的 predicate 判断函数分组切片元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Partition)]
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为 0 时返回下标-1。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
- **<big>SetToDefaultIf</big>** : 根据给定给定的 predicate 判定函数来修改切片中的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
- **<big>Break</big>** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。
@@ -1573,9 +1792,14 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Frequency</big>** : 计算切片中每个元素出现的频率。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Frequency)]
[[play](https://go.dev/play/p/CW3UVNdUZOq)]
- **<big>JoinFunc</big>** : 将切片元素用给定的分隔符连接成一个单一的字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#JoinFunc)]
[[play](https://go.dev/play/p/55ib3SB5fM2)]
- **<big>ConcatBy</big>** : 将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ConcatBy)]
[[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"> 21. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/stream"
@@ -1601,13 +1825,13 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Concat</big>** : 创建一个延迟连接 stream其元素是第一个 stream 的所有元素,后跟第二个 stream 的全部元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Concat)]
[[play](https://go.dev/play/p/HM4OlYk_OUC)]
- **<big>Distinct</big>** : 创建并返回一个stream用于删除重复的项。
- **<big>Distinct</big>** : 创建并返回一个 stream用于删除重复的项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Distinct)]
[[play](https://go.dev/play/p/eGkOSrm64cB)]
- **<big>Filter</big>** : 返回一个通过判定函数的stream。
- **<big>Filter</big>** : 返回一个通过判定函数的 stream。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Filter)]
[[play](https://go.dev/play/p/MFlSANo-buc)]
- **<big>FilterConcurrent</big>** : 对slice并发执行filter操作。
- **<big>FilterConcurrent</big>** : 对 slice 并发执行 filter 操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FilterConcurrent)]
[[play](https://go.dev/play/p/t_pkwerIRVx)]
- **<big>Map</big>** : 返回一个 stream该 stream 由将给定函数应用于源 stream 元素的元素组成。
@@ -1664,9 +1888,14 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>ToSlice</big>** : 返回 stream 中的元素切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
- **<big>IndexOf</big>** : 返回在 stream 中找到值的第一个匹配项的索引,如果找不到值,则返回-1。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#IndexOf)]
[[play](https://go.dev/play/p/tBV5Nc-XDX2)]
- **<big>LastIndexOf</big>** : 返回在 stream 中找到值的最后一个匹配项的索引,如果找不到值,则返回-1。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#LastIndexOf)]
[[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"> 22. structs 提供操作 struct, tag, field 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/structs"
@@ -1676,34 +1905,51 @@ import "github.com/duke-git/lancet/v2/structs"
- **<big>New</big>** : `Struct`结构体的构造函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#New)]
[[play](https://go.dev/play/p/O29l8kk-Z17)]
- **<big>ToMap</big>** : 将一个合法的 struct 对象转换为 map[string]any。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#ToMap)]
[[play](https://go.dev/play/p/qQbLySBgerZ)]
- **<big>Fields</big>** : 获取一个 struct 对象的属性列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)]
[[play](https://go.dev/play/p/w3Kk_CyVY7D)]
- **<big>Field</big>** : 根据属性名获取一个 struct 对象的属性。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)]
[[play](https://go.dev/play/p/KocZMSYarza)]
- **<big>IsStruct</big>** : 判断是否为一个合法的 struct 对象。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsStruct)]
[[play](https://go.dev/play/p/bU2FSdkbK1C)]
- **<big>Tag</big>** : 获取`Field``Tag`,默认的 tag key 是 json。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Tag)]
[[play](https://go.dev/play/p/DVrx5HvvUJr)]
- **<big>Name</big>** : 获取属性名。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Name)]
[[play](https://go.dev/play/p/zfIGlqsatee)]
- **<big>Value</big>** : 获取`Field`属性的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Value)]
[[play](https://go.dev/play/p/qufYEU2o4Oi)]
- **<big>Kind</big>** : 获取属性 Kind。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Kind)]
[[play](https://go.dev/play/p/wg4NlcUNG5o)]
- **<big>IsEmbedded</big>** : 判断属性是否为嵌入。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsEmbedded)]
[[play](https://go.dev/play/p/wV2PrbYm3Ec)]
- **<big>IsExported</big>** : 判断属性是否导出。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsExported)]
[[play](https://go.dev/play/p/csK4AXYaNbJ)]
- **<big>IsZero</big>** : 判断属性是否为零值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsZero)]
[[play](https://go.dev/play/p/RzqpGISf87r)]
- **<big>IsSlice</big>** : 判断属性是否是切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsSlice)]
[[play](https://go.dev/play/p/MKz4CgBIUrU)]
- **<big>IsTargetType</big>** : 判断属性是否是目标类型。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsTargetType)]
[[play](https://go.dev/play/p/Ig75P-agN39)]
- **<big>TypeName</big>** : 获取结构体类型名。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#TypeName)]
[[play](https://go.dev/play/p/SWLWd0XBaBb)]
<h3 id="strutil"> 21. strutil 包含字符串处理的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="strutil"> 23. strutil 包含字符串处理的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/strutil"
@@ -1825,7 +2071,7 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串 start 和终止字符串 end 直接的子字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
@@ -1843,14 +2089,20 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>Rotate</big>** : 按指定的字符数旋转字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)]
[[play](https://go.dev/play/p/Kf03iOeT5bd)]
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为map中的相应值。
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为 map 中的相应值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)]
[[play](https://go.dev/play/p/cXSuFvyZqv9)]
- **<big>RegexMatchAllGroups</big>** : 使用正则表达式匹配字符串中的所有子组并返回结果。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RegexMatchAllGroups)]
[[play](https://go.dev/play/p/JZiu0RXpgN-)]
- **<big>ExtractContent</big>** : 提取两个标记之间的内容。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ExtractContent)]
[[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"> 24. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/system"
@@ -1894,13 +2146,11 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>KillProcess</big>** : 杀掉进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)]
[[play](https://go.dev/play/p/XKmvV-ExBWa)]
- **<big>GetProcessInfo</big>** : 根据进程id获取进程信息。
- **<big>GetProcessInfo</big>** : 根据进程 id 获取进程信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)]
[[play](https://go.dev/play/p/NQDVywEYYx7)]
<h3 id="tuple"> 23. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="tuple"> 25. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/tuple"
@@ -2017,7 +2267,7 @@ import "github.com/duke-git/lancet/v2/tuple"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip10)]
[[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"> 26. validator 验证器包,包含常用字符串格式验证函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/validator"
@@ -2082,6 +2332,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsNumberStr</big>** : 验证字符串是否是可以转换为数字。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsNumberStr)]
[[play](https://go.dev/play/p/LzaKocSV79u)]
- **<big>IsAlphaNumeric</big>** : 验证字符串是字母或数字。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAlphaNumeric)]
[[play](https://go.dev/play/p/RHeESLrLg9c)]
- **<big>IsJSON</big>** : 验证字符串是否是有效 json。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJSON)]
[[play](https://go.dev/play/p/8Kip1Itjiil)]
@@ -2103,6 +2356,9 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsIpV6</big>** : 验证字符串是否是 ipv6 地址。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV6)]
[[play](https://go.dev/play/p/AHA0r0AzIdC)]
- **<big>IsIpPort</big>** : 检查字符串是否是 ip:port 格式。
[[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>** : 验证字符串是否是强密码:(字母+数字+特殊字符)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsStrongPassword)]
[[play](https://go.dev/play/p/QHdVcSQ3uDg)]
@@ -2151,8 +2407,14 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsChinaUnionPay</big>** : 检查字符串是否是有效的中国银联卡号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChinaUnionPay)]
[[play](https://go.dev/play/p/yafpdxLiymu)]
- **<big>IsPassport</big>** : 判断护照(正则判断)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsPassport)]
[[play](https://go.dev/play/p/dvOiV2BW7Aw)]
- **<big>IsChineseHMPassport</big>** : 判断港澳台通行证(正则判断)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChineseHMPassport)]
[[play](https://go.dev/play/p/xKG6spQTcY0)]
<h3 id="xerror"> 25. xerror 包实现一些错误处理函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="xerror"> 27. xerror 包实现一些错误处理函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/xerror"
@@ -2199,6 +2461,9 @@ import "github.com/duke-git/lancet/v2/xerror"
- **<big>TryUnwrap</big>** : 检查 error, 如果 err 为 nil 则展开,则它返回一个有效值,如果 err 不是 nil 则 Unwrap 使用 err 发生 panic。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#TryUnwrap)]
[[play](https://go.dev/play/p/acyZVkNZEeW)]
- **<big>TryCatch</big>** : 简单实现的 java 风格异常处理try-catch-finally
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#TryCatch)]
[[play](https://go.dev/play/p/D5Mdb0mRj0P)]
## 如何贡献代码
@@ -2206,7 +2471,7 @@ import "github.com/duke-git/lancet/v2/xerror"
## 贡献者
感谢所有为lancet贡献过代码的人
感谢所有为 lancet 贡献过代码的人!
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
@@ -2214,4 +2479,4 @@ import "github.com/duke-git/lancet/v2/xerror"
## GitHub Stars
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -3,6 +3,7 @@ package compare
import (
"bytes"
"encoding/json"
"math/big"
"reflect"
"time"
@@ -24,6 +25,13 @@ func compareValue(operator string, left, right any) bool {
case reflect.Struct, reflect.Slice, reflect.Map:
return compareRefValue(operator, left, right, leftType.Kind())
case reflect.Ptr:
if leftVal, ok := left.(*big.Int); ok {
if rightVal, ok := right.(*big.Int); ok {
return compareBigInt(operator, leftVal, rightVal)
}
}
}
return false
@@ -155,169 +163,129 @@ func compareBasicValue(operator string, leftValue, rightValue any) bool {
}
switch leftVal := leftValue.(type) {
case json.Number:
if left, err := leftVal.Float64(); err == nil {
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
left, err := convertor.ToBigInt(leftValue)
if err != nil {
return false
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToBigInt(rightValue)
if err != nil {
return false
}
return compareBigInt(operator, left, right)
case float32, float64:
left, err := convertor.ToFloat(leftValue)
if err != nil {
return false
}
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
}
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
return compareFloats(operator, left, right)
case string:
left := leftVal
switch right := rightValue.(type) {
case string:
switch operator {
case equal:
if left == right {
return true
}
case lessThan:
if left < right {
return true
}
case greaterThan:
if left > right {
return true
}
case lessOrEqual:
if left <= right {
return true
}
case greaterOrEqual:
if left >= right {
return true
}
}
return compareStrings(operator, left, right)
}
case bool:
left := leftVal
switch right := rightValue.(type) {
case bool:
switch operator {
case equal:
if left == right {
return true
}
}
return compareBools(operator, left, right)
}
case json.Number:
if left, err := leftVal.Float64(); err == nil {
switch rightVal := rightValue.(type) {
case json.Number:
if right, err := rightVal.Float64(); err == nil {
return compareFloats(operator, left, right)
}
case float32, float64:
right, err := convertor.ToFloat(rightValue)
if err != nil {
return false
}
return compareFloats(operator, left, right)
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
right, err := convertor.ToBigInt(rightValue)
if err != nil {
return false
}
left, err := convertor.ToBigInt(left)
return compareBigInt(operator, left, right)
}
}
}
return false
}
// compareBigInt compares two big.Int values based on the operator
func compareBigInt(operator string, left, right *big.Int) bool {
switch operator {
case equal:
return left.Cmp(right) == 0
case lessThan:
return left.Cmp(right) < 0
case greaterThan:
return left.Cmp(right) > 0
case lessOrEqual:
return left.Cmp(right) <= 0
case greaterOrEqual:
return left.Cmp(right) >= 0
}
return false
}
// compareFloats compares two float64 values based on the operator
func compareFloats(operator string, left, right float64) bool {
switch operator {
case equal:
return left == right
case lessThan:
return left < right
case greaterThan:
return left > right
case lessOrEqual:
return left <= right
case greaterOrEqual:
return left >= right
}
return false
}
// compareStrings compares two string values based on the operator
func compareStrings(operator string, left, right string) bool {
switch operator {
case equal:
return left == right
case lessThan:
return left < right
case greaterThan:
return left > right
case lessOrEqual:
return left <= right
case greaterOrEqual:
return left >= right
}
return false
}
// compareBools compares two boolean values based on the operator
func compareBools(operator string, left, right bool) bool {
switch operator {
case equal:
return left == right
}
return false
}

View File

@@ -2,6 +2,7 @@ package compare
import (
"encoding/json"
"math/big"
"testing"
"time"
@@ -12,213 +13,191 @@ func TestEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqual")
// basic type
assert.Equal(true, Equal(1, 1))
assert.Equal(true, Equal(int64(1), int64(1)))
assert.Equal(true, Equal("a", "a"))
assert.Equal(true, Equal(true, true))
assert.Equal(true, Equal([]int{1, 2, 3}, []int{1, 2, 3}))
assert.Equal(true, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}))
assert.Equal(false, Equal(1, 2))
assert.Equal(false, Equal(1, int64(1)))
assert.Equal(false, Equal("a", "b"))
assert.Equal(false, Equal(true, false))
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
// time
time1 := time.Now()
time2 := time1.Add(time.Second)
time3 := time1.Add(time.Second)
assert.Equal(false, Equal(time1, time2))
assert.Equal(true, Equal(time2, time3))
// struct
st1 := struct {
A string
B string
tests := []struct {
left any
right any
want bool
}{
A: "a",
B: "b",
{1, 1, true},
{int64(1), int64(1), true},
{"a", "a", true},
{true, true, true},
{[]int{1, 2, 3}, []int{1, 2, 3}, true},
{map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}, true},
{1, 2, false},
{1, int64(1), false},
{"a", "b", false},
{true, false, false},
{[]int{1, 2}, []int{1, 2, 3}, false},
{map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}, false},
// {time.Now(), time.Now(), true},
// {time.Now(), time.Now().Add(time.Second), false},
{[]byte("hello"), []byte("hello"), true},
{[]byte("hello"), []byte("world"), false},
{json.Number("123"), json.Number("123"), true},
{json.Number("123"), json.Number("124"), false},
{big.NewInt(123), big.NewInt(123), true},
}
st2 := struct {
A string
B string
}{
A: "a",
B: "b",
for _, tt := range tests {
assert.Equal(tt.want, Equal(tt.left, tt.right))
}
st3 := struct {
A string
B string
}{
A: "a1",
B: "b",
}
assert.Equal(true, Equal(st1, st2))
assert.Equal(false, Equal(st1, st3))
//byte slice
bs1 := []byte("hello")
bs2 := []byte("hello")
assert.Equal(true, Equal(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, Equal(jsonNumber1, jsonNumber2))
}
func TestEqualValue(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqualValue")
assert.Equal(true, EqualValue(1, 1))
assert.Equal(true, EqualValue(int(1), int64(1)))
assert.Equal(true, EqualValue(1, "1"))
tests := []struct {
left any
right any
want bool
}{
{1, 1, true},
{int64(1), int64(1), true},
{"a", "a", true},
{true, true, true},
{[]int{1, 2, 3}, []int{1, 2, 3}, true},
{map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}, true},
{1, 2, false},
{1, int64(1), true},
{"a", "b", false},
{true, false, false},
{[]int{1, 2}, []int{1, 2, 3}, false},
}
assert.Equal(false, EqualValue(1, "2"))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, EqualValue(jsonNumber1, 123))
for _, tt := range tests {
assert.Equal(tt.want, EqualValue(tt.left, tt.right))
}
}
func TestLessThan(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLessThan")
assert.Equal(true, LessThan(1, 2))
assert.Equal(true, LessThan(1.1, 2.2))
assert.Equal(true, LessThan("a", "b"))
tests := []struct {
left any
right any
want bool
}{
{1, 2, true},
{1.1, 2.2, true},
{"a", "b", true},
{time.Now(), time.Now().Add(time.Second), true},
{[]byte("hello1"), []byte("hello2"), true},
{json.Number("123"), json.Number("124"), true},
{645680099112988673, 645680099112988675, true},
{1, 1, false},
{1, int64(1), false},
}
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, LessThan(time1, time2))
assert.Equal(false, LessThan(1, 1))
assert.Equal(false, LessThan(1, int64(1)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessThan(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessThan(jsonNumber1, jsonNumber2))
for _, tt := range tests {
assert.Equal(tt.want, LessThan(tt.left, tt.right))
}
}
func TestGreaterThan(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGreaterThan")
assert.Equal(true, GreaterThan(2, 1))
assert.Equal(true, GreaterThan(2.2, 1.1))
assert.Equal(true, GreaterThan("b", "a"))
tests := []struct {
left any
right any
want bool
}{
{2, 1, true},
{2.2, 1.1, true},
{"b", "a", true},
{time.Now().Add(time.Second), time.Now(), true},
{[]byte("hello2"), []byte("hello1"), true},
{json.Number("124"), json.Number("123"), true},
{645680099112988675, 645680099112988673, true},
{1, 1, false},
{1, int64(1), false},
}
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, GreaterThan(time2, time1))
for _, tt := range tests {
assert.Equal(tt.want, GreaterThan(tt.left, tt.right))
}
assert.Equal(false, GreaterThan(1, 2))
assert.Equal(false, GreaterThan(int64(2), 1))
assert.Equal(false, GreaterThan("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterThan(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterThan(jsonNumber2, jsonNumber1))
}
func TestLessOrEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLessOrEqual")
assert.Equal(true, LessOrEqual(1, 2))
assert.Equal(true, LessOrEqual(1, 1))
assert.Equal(true, LessOrEqual(1.1, 2.2))
assert.Equal(true, LessOrEqual("a", "b"))
tests := []struct {
left any
right any
want bool
}{
{1, 2, true},
{1, 1, true},
{1.1, 2.2, true},
{"a", "b", true},
{time.Now(), time.Now().Add(time.Second), true},
{[]byte("hello1"), []byte("hello2"), true},
{json.Number("123"), json.Number("124"), true},
{645680099112988673, 645680099112988675, true},
{2, 1, false},
{1, int64(2), false},
}
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, LessOrEqual(time1, time2))
assert.Equal(false, LessOrEqual(2, 1))
assert.Equal(false, LessOrEqual(1, int64(2)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessOrEqual(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessOrEqual(jsonNumber1, jsonNumber2))
for _, tt := range tests {
assert.Equal(tt.want, LessOrEqual(tt.left, tt.right))
}
}
func TestGreaterOrEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGreaterThan")
assert.Equal(true, GreaterOrEqual(2, 1))
assert.Equal(true, GreaterOrEqual(1, 1))
assert.Equal(true, GreaterOrEqual(2.2, 1.1))
assert.Equal(true, GreaterOrEqual("b", "b"))
tests := []struct {
left any
right any
want bool
}{
{2, 1, true},
{1, 1, true},
{2.2, 1.1, true},
{"b", "b", true},
{time.Now().Add(time.Second), time.Now(), true},
{[]byte("hello2"), []byte("hello1"), true},
{json.Number("124"), json.Number("123"), true},
{645680099112988675, 645680099112988673, true},
{1, 2, false},
{int64(2), 1, false},
{"b", "c", false},
}
time1 := time.Now()
time2 := time1.Add(time.Second)
assert.Equal(true, GreaterOrEqual(time2, time1))
assert.Equal(false, GreaterOrEqual(1, 2))
assert.Equal(false, GreaterOrEqual(int64(2), 1))
assert.Equal(false, GreaterOrEqual("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterOrEqual(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterOrEqual(jsonNumber2, jsonNumber1))
for _, tt := range tests {
assert.Equal(tt.want, GreaterOrEqual(tt.left, tt.right))
}
}
func TestInDelta(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestInDelta")
assert.Equal(true, InDelta(1, 1, 0))
assert.Equal(false, InDelta(1, 2, 0))
tests := []struct {
left float64
right float64
delta float64
want bool
}{
{1, 1, 0, true},
{1, 2, 0, false},
{2.0 / 3.0, 0.66667, 0.001, true},
{2.0 / 3.0, 0.0, 0.001, false},
{float64(74.96) - float64(20.48), 54.48, 0, false},
{float64(74.96) - float64(20.48), 54.48, 1e-14, true},
{float64(float32(80.45)), float64(80.45), 0, false},
{float64(float32(80.45)), float64(80.45), 1e-5, true},
}
assert.Equal(true, InDelta(2.0/3.0, 0.66667, 0.001))
assert.Equal(false, InDelta(2.0/3.0, 0.0, 0.001))
assert.Equal(false, InDelta(float64(74.96)-float64(20.48), 54.48, 0))
assert.Equal(true, InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14))
assert.Equal(false, InDelta(float64(float32(80.45)), float64(80.45), 0))
assert.Equal(true, InDelta(float64(float32(80.45)), float64(80.45), 1e-5))
for _, tt := range tests {
assert.Equal(tt.want, InDelta(tt.left, tt.right, tt.delta))
}
}

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
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T {
valStream := make(chan T)
go func() {
defer close(valStream)
wg := sync.WaitGroup{}
defer wg.Wait()
for {
var stream <-chan T
select {
@@ -169,19 +169,22 @@ func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-c
return
}
stream = maybeStream
wg.Add(1)
case <-ctx.Done():
return
}
for val := range c.OrDone(ctx, stream) {
select {
case valStream <- val:
case <-ctx.Done():
go func() {
defer wg.Done()
for val := range c.OrDone(ctx, stream) {
select {
case valStream <- val:
case <-ctx.Done():
}
}
}
}()
}
}()
return valStream
}

View File

@@ -3,6 +3,7 @@ package concurrency
import (
"context"
"fmt"
"log"
"time"
)
@@ -168,7 +169,8 @@ func ExampleChannel_Tee() {
func ExampleChannel_Bridge() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m1 := make(map[int]int)
m2 := make(map[int]int)
c := NewChannel[int]()
genVals := func() <-chan <-chan int {
out := make(chan (<-chan int))
@@ -177,6 +179,7 @@ func ExampleChannel_Bridge() {
for i := 1; i <= 5; i++ {
stream := make(chan int, 1)
stream <- i
m1[i]++
close(stream)
out <- stream
}
@@ -185,12 +188,171 @@ func ExampleChannel_Bridge() {
}
for v := range c.Bridge(ctx, genVals()) {
fmt.Println(v)
m2[v]++
}
for k, v := range m1 {
fmt.Println(m2[k] == v)
}
// Output:
// 1
// 2
// 3
// 4
// 5
// true
// true
// true
// true
// true
}
func ExampleKeyedLocker_Do() {
locker := NewKeyedLocker[string](2 * time.Second)
task := func() {
fmt.Println("Executing task...")
time.Sleep(1 * time.Second)
fmt.Println("Task completed.")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := locker.Do(ctx, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel2()
if err := locker.Do(ctx2, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
// Output:
// Executing task...
// Task completed.
// Task successfully executed.
// Executing task...
// Task completed.
// Task successfully executed.
}
func ExampleRWKeyedLocker_Lock() {
locker := NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.Lock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
func ExampleRWKeyedLocker_RLock() {
locker := NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.RLock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
func ExampleTryKeyedLocker() {
locker := NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
func ExampleTryKeyedLocker_TryLock() {
locker := NewTryKeyedLocker[string]()
key := "resource_key"
done := make(chan struct{})
go func() {
if locker.TryLock(key) {
time.Sleep(2 * time.Second)
locker.Unlock(key)
}
close(done)
}()
time.Sleep(100 * time.Millisecond)
if locker.TryLock(key) {
fmt.Println("Lock acquired")
locker.Unlock(key)
} else {
fmt.Println("Lock failed")
}
// wait for the goroutine to finish
<-done
fmt.Println("Retrying...")
time.Sleep(100 * time.Millisecond)
if locker.TryLock(key) {
fmt.Println("Lock acquired")
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
// Output:
// Lock failed
// Retrying...
// Lock acquired
// Lock released
}

View File

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

257
concurrency/keyed_locker.go Normal file
View File

@@ -0,0 +1,257 @@
// Copyright 2025 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, locker.
package concurrency
import (
"context"
"sync"
"sync/atomic"
"time"
)
// KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.
type KeyedLocker[K comparable] struct {
locks sync.Map
ttl time.Duration
}
type lockEntry struct {
mu sync.Mutex
ref int32
timer atomic.Pointer[time.Timer]
}
// NewKeyedLocker creates a new KeyedLocker with the specified TTL for lock expiration.
// The TTL is used to automatically release locks that are no longer held.
// Play: https://go.dev/play/p/GzeyC33T5rw
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] {
return &KeyedLocker[K]{ttl: ttl}
}
// Do acquires a lock for the specified key and executes the provided function.
// It returns an error if the context is canceled before the function completes.
// Play: https://go.dev/play/p/GzeyC33T5rw
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error {
entry := l.acquire(key)
defer l.release(key, entry, key)
done := make(chan struct{})
go func() {
entry.mu.Lock()
defer entry.mu.Unlock()
select {
case <-ctx.Done():
default:
fn()
}
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
return nil
}
}
// acquire tries to acquire a lock for the specified key.
func (l *KeyedLocker[K]) acquire(key K) *lockEntry {
lock, _ := l.locks.LoadOrStore(key, &lockEntry{})
entry := lock.(*lockEntry)
atomic.AddInt32(&entry.ref, 1)
if t := entry.timer.Swap(nil); t != nil {
t.Stop()
}
return entry
}
// release releases the lock for the specified key.
func (l *KeyedLocker[K]) release(key K, entry *lockEntry, rawKey K) {
if atomic.AddInt32(&entry.ref, -1) == 0 {
entry.mu.Lock()
defer entry.mu.Unlock()
if entry.ref == 0 {
if t := entry.timer.Swap(nil); t != nil {
t.Stop()
}
l.locks.Delete(rawKey)
} else {
if entry.timer.Load() == nil {
t := time.AfterFunc(l.ttl, func() {
l.release(key, entry, rawKey)
})
entry.timer.Store(t)
}
}
}
}
// RWKeyedLocker is a read-write version of KeyedLocker.
type RWKeyedLocker[K comparable] struct {
locks sync.Map
ttl time.Duration
}
type rwLockEntry struct {
mu sync.RWMutex
ref int32
timer atomic.Pointer[time.Timer]
}
// NewRWKeyedLocker creates a new RWKeyedLocker with the specified TTL for lock expiration.
// The TTL is used to automatically release locks that are no longer held.
// Play: https://go.dev/play/p/CkaJWWwZm9
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] {
return &RWKeyedLocker[K]{ttl: ttl}
}
// RLock acquires a read lock for the specified key and executes the provided function.
// It returns an error if the context is canceled before the function completes.
// Play: https://go.dev/play/p/ZrCr8sMo77T
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error {
entry := l.acquire(key)
defer l.release(entry, key)
done := make(chan struct{})
go func() {
entry.mu.RLock()
defer entry.mu.RUnlock()
select {
case <-ctx.Done():
default:
fn()
}
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
return nil
}
}
// Lock acquires a write lock for the specified key and executes the provided function.
// It returns an error if the context is canceled before the function completes.
// Play: https://go.dev/play/p/WgAcXbOPKGk
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error {
entry := l.acquire(key)
defer l.release(entry, key)
done := make(chan struct{})
go func() {
entry.mu.Lock()
defer entry.mu.Unlock()
select {
case <-ctx.Done():
default:
fn()
}
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
return nil
}
}
// acquire tries to acquire a read lock for the specified key.
func (l *RWKeyedLocker[K]) acquire(key K) *rwLockEntry {
actual, _ := l.locks.LoadOrStore(key, &rwLockEntry{})
entry := actual.(*rwLockEntry)
atomic.AddInt32(&entry.ref, 1)
if t := entry.timer.Swap(nil); t != nil {
t.Stop()
}
return entry
}
// release releases the lock for the specified key.
func (l *RWKeyedLocker[K]) release(entry *rwLockEntry, rawKey K) {
if atomic.AddInt32(&entry.ref, -1) == 0 {
timer := time.AfterFunc(l.ttl, func() {
if atomic.LoadInt32(&entry.ref) == 0 {
l.locks.Delete(rawKey)
}
})
entry.timer.Store(timer)
}
}
// TryKeyedLocker is a non-blocking version of KeyedLocker.
// It allows for trying to acquire a lock without blocking if the lock is already held.
type TryKeyedLocker[K comparable] struct {
mu sync.Mutex
locks map[K]*casMutex
}
// NewTryKeyedLocker creates a new TryKeyedLocker.
// Play: https://go.dev/play/p/VG9qLvyetE2
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] {
return &TryKeyedLocker[K]{locks: make(map[K]*casMutex)}
}
// TryLock tries to acquire a lock for the specified key.
// It returns true if the lock was acquired, false otherwise.
// Play: https://go.dev/play/p/VG9qLvyetE2
func (l *TryKeyedLocker[K]) TryLock(key K) bool {
l.mu.Lock()
lock, ok := l.locks[key]
if !ok {
lock = &casMutex{}
l.locks[key] = lock
}
l.mu.Unlock()
return lock.TryLock()
}
// Unlock releases the lock for the specified key.
// Play: https://go.dev/play/p/VG9qLvyetE2
func (l *TryKeyedLocker[K]) Unlock(key K) {
l.mu.Lock()
defer l.mu.Unlock()
lock, ok := l.locks[key]
if ok {
lock.Unlock()
if lock.lock == 0 {
delete(l.locks, key)
}
}
}
// casMutex is a simple mutex that uses atomic operations to provide a non-blocking lock.
type casMutex struct {
lock int32
}
// TryLock tries to acquire the lock without blocking.
// It returns true if the lock was acquired, false otherwise.
func (m *casMutex) TryLock() bool {
return atomic.CompareAndSwapInt32(&m.lock, 0, 1)
}
// Unlock releases the lock.
func (m *casMutex) Unlock() {
atomic.StoreInt32(&m.lock, 0)
}

View File

@@ -0,0 +1,230 @@
package concurrency
import (
"context"
"strconv"
"sync"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
func TestKeyedLocker_SerialExecutionSameKey(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKeyedLocker_SerialExecutionSameKey")
locker := NewKeyedLocker[string](100 * time.Millisecond)
var result []int
var mu sync.Mutex
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
err := locker.Do(context.Background(), "key1", func() {
time.Sleep(10 * time.Millisecond)
mu.Lock()
defer mu.Unlock()
result = append(result, i)
})
assert.IsNil(err)
}(i)
}
wg.Wait()
assert.Equal(5, len(result))
}
func TestKeyedLocker_ParallelExecutionDifferentKeys(t *testing.T) {
locker := NewKeyedLocker[string](100 * time.Millisecond)
start := time.Now()
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := "key" + strconv.Itoa(i)
locker.Do(context.Background(), key, func() {
time.Sleep(50 * time.Millisecond)
})
}(i)
}
wg.Wait()
elapsed := time.Since(start)
if elapsed > 100*time.Millisecond {
t.Errorf("parallel execution took too long: %s", elapsed)
}
}
func TestKeyedLocker_ContextTimeout(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKeyedLocker_ContextTimeout")
locker := NewKeyedLocker[string](100 * time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
// Lock key before calling
go func() {
_ = locker.Do(context.Background(), "key-timeout", func() {
time.Sleep(50 * time.Millisecond)
})
}()
time.Sleep(1 * time.Millisecond) // ensure lock is acquired first
err := locker.Do(ctx, "key-timeout", func() {
t.Error("should not execute")
})
assert.IsNotNil(err)
}
func TestKeyedLocker_LockReleaseAfterTTL(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
locker := NewKeyedLocker[string](50 * time.Millisecond)
err := locker.Do(context.Background(), "ttl-key", func() {})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
// Wait for TTL to pass
time.Sleep(100 * time.Millisecond)
err = locker.Do(context.Background(), "ttl-key", func() {})
assert.IsNil(err)
}
func TestRWKeyedLocker_LockAndUnlock(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
locker := NewRWKeyedLocker[string](500 * time.Millisecond)
var locked bool
err := locker.Lock(context.Background(), "key1", func() {
locked = true
})
assert.IsNil(err)
assert.Equal(true, locked)
}
func TestRWKeyedLocker_RLockParallel(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestKeyedLocker_LockReleaseAfterTTL")
locker := NewRWKeyedLocker[string](1 * time.Second)
var mu sync.Mutex
var count int
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
err := locker.RLock(context.Background(), "shared-key", func() {
time.Sleep(10 * time.Millisecond)
mu.Lock()
count++
mu.Unlock()
})
assert.IsNil(err)
}()
}
wg.Wait()
assert.Equal(5, count)
}
func TestRWKeyedLocker_LockTimeout(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRWKeyedLocker_LockTimeout")
locker := NewRWKeyedLocker[string](1 * time.Second)
start := make(chan struct{})
go func() {
locker.Lock(context.Background(), "key-timeout", func() {
close(start)
time.Sleep(200 * time.Millisecond)
})
}()
<-start
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
err := locker.Lock(ctx, "key-timeout", func() {
t.Error("should not reach here")
})
assert.IsNotNil(err)
}
func TestTryKeyedLocker_SimpleLockUnlock(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTryKeyedLocker_SimpleLockUnlock")
locker := NewTryKeyedLocker[string]()
ok := locker.TryLock("key1")
assert.Equal(true, ok)
ok = locker.TryLock("key1")
assert.Equal(false, ok)
locker.Unlock("key1")
ok = locker.TryLock("key1")
assert.Equal(true, ok)
locker.Unlock("key1")
}
func TestTryKeyedLocker_ParallelTry(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestTryKeyedLocker_ParallelTry")
locker := NewTryKeyedLocker[string]()
var wg sync.WaitGroup
var mu sync.Mutex
var count int
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ok := locker.TryLock("key" + strconv.Itoa(i))
mu.Lock()
if ok {
count++
}
mu.Unlock()
time.Sleep(10 * time.Millisecond)
if ok {
locker.Unlock("key" + strconv.Itoa(i))
}
}(i)
}
wg.Wait()
assert.Equal(5, count)
assert.Equal(0, len(locker.locks))
}

View File

@@ -14,6 +14,7 @@ import (
"fmt"
"io"
"math"
"math/big"
"reflect"
"strconv"
"strings"
@@ -107,6 +108,13 @@ func ToString(value any) string {
if value == nil {
return ""
}
rv := reflect.ValueOf(value)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return ""
}
return ToString(rv.Elem().Interface())
}
switch val := value.(type) {
case float32:
@@ -142,12 +150,8 @@ func ToString(value any) string {
if err != nil {
return ""
}
return string(b)
// todo: maybe we should't supprt other type conversion
// v := reflect.ValueOf(value)
// log.Panicf("Unsupported data type: %s ", v.String())
// return ""
return string(b)
}
}
@@ -224,6 +228,42 @@ func ToPointer[T any](value T) *T {
return &value
}
// ToPointers convert a slice of values to a slice of pointers.
// Play: https://go.dev/play/p/ZUoXd2i5ZkV
func ToPointers[T any](values []T) []*T {
result := make([]*T, len(values))
for i := range values {
result[i] = &values[i]
}
return result
}
// FromPointer returns the value pointed to by the pointer.
// Play: https://go.dev/play/p/wAp90V7Zu6g
func FromPointer[T any](ptr *T) T {
if ptr == nil {
var zeroValue T
return zeroValue
}
return *ptr
}
// FromPointers convert a slice of pointers to a slice of values.
// Play: https://go.dev/play/p/qIPsyYtNy3Q
func FromPointers[T any](pointers []*T) []T {
result := make([]T, len(pointers))
for i, ptr := range pointers {
if ptr == nil {
var zeroValue T
result[i] = zeroValue
} else {
result[i] = *ptr
}
}
return result
}
// ToMap convert a slice of structs to a map based on iteratee function.
// Play: https://go.dev/play/p/tVFy7E-t24l
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
@@ -402,15 +442,15 @@ func ToStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
switch v := value.(type) {
case []byte:
return base64.StdEncoding.EncodeToString(value.([]byte))
return base64.StdEncoding.EncodeToString(v)
case string:
return base64.StdEncoding.EncodeToString([]byte(value.(string)))
return base64.StdEncoding.EncodeToString([]byte(v))
case error:
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error()))
return base64.StdEncoding.EncodeToString([]byte(v.Error()))
default:
marshal, err := json.Marshal(value)
marshal, err := json.Marshal(v)
if err != nil {
return ""
}
@@ -424,15 +464,15 @@ func ToUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
switch v := value.(type) {
case []byte:
return base64.URLEncoding.EncodeToString(value.([]byte))
return base64.URLEncoding.EncodeToString(v)
case string:
return base64.URLEncoding.EncodeToString([]byte(value.(string)))
return base64.URLEncoding.EncodeToString([]byte(v))
case error:
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error()))
return base64.URLEncoding.EncodeToString([]byte(v.Error()))
default:
marshal, err := json.Marshal(value)
marshal, err := json.Marshal(v)
if err != nil {
return ""
}
@@ -446,7 +486,7 @@ func ToRawStdBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
switch v := value.(type) {
case []byte:
return base64.RawStdEncoding.EncodeToString(value.([]byte))
case string:
@@ -454,7 +494,7 @@ func ToRawStdBase64(value any) string {
case error:
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
marshal, err := json.Marshal(v)
if err != nil {
return ""
}
@@ -468,7 +508,7 @@ func ToRawUrlBase64(value any) string {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return ""
}
switch value.(type) {
switch v := value.(type) {
case []byte:
return base64.RawURLEncoding.EncodeToString(value.([]byte))
case string:
@@ -476,10 +516,43 @@ func ToRawUrlBase64(value any) string {
case error:
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
default:
marshal, err := json.Marshal(value)
marshal, err := json.Marshal(v)
if err != nil {
return ""
}
return base64.RawURLEncoding.EncodeToString(marshal)
}
}
// ToBigInt converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int
// Play: https://go.dev/play/p/X3itkCxwB_x
func ToBigInt[T any](v T) (*big.Int, error) {
result := new(big.Int)
switch v := any(v).(type) {
case int:
result.SetInt64(int64(v)) // Convert to int64 for big.Int
case int8:
result.SetInt64(int64(v))
case int16:
result.SetInt64(int64(v))
case int32:
result.SetInt64(int64(v))
case int64:
result.SetInt64(v)
case uint:
result.SetUint64(uint64(v)) // Convert to uint64 for big.Int
case uint8:
result.SetUint64(uint64(v))
case uint16:
result.SetUint64(uint64(v))
case uint32:
result.SetUint64(uint64(v))
case uint64:
result.SetUint64(v)
default:
return nil, fmt.Errorf("unsupported type: %T", v)
}
return result, nil
}

View File

@@ -169,6 +169,45 @@ func ExampleToPointer() {
// 123
}
func ExampleToPointers() {
strs := []string{"a", "b", "c"}
pointerStrs := ToPointers(strs)
fmt.Println(*pointerStrs[0])
fmt.Println(*pointerStrs[1])
fmt.Println(*pointerStrs[2])
// Output:
// a
// b
// c
}
func ExampleFromPointer() {
str := "abc"
strPtr := &str
result := FromPointer(strPtr)
fmt.Println(result)
// Output:
// abc
}
func ExampleFromPointers() {
strs := []string{"a", "b", "c"}
strPtr := []*string{&strs[0], &strs[1], &strs[2]}
result := FromPointers(strPtr)
fmt.Println(result[0])
fmt.Println(result[1])
fmt.Println(result[2])
// Output:
// a
// b
// c
}
func ExampleToMap() {
type Message struct {
name string
@@ -571,3 +610,12 @@ func ExampleToRawUrlBase64() {
// dHJ1ZQ
// ZXJy
}
func ExampleToBigInt() {
n := 9876543210
bigInt, _ := ToBigInt(n)
fmt.Println(bigInt)
// Output:
// 9876543210
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"math/big"
"reflect"
"strconv"
"testing"
@@ -141,13 +142,24 @@ func TestToString(t *testing.T) {
}
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{
"", nil,
int(0), int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float64(12.3), float32(12.3),
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{
"", "",
@@ -156,6 +168,8 @@ func TestToString(t *testing.T) {
"12.3", "12.3",
"true", "false",
"[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++ {
@@ -288,6 +302,75 @@ func TestToPointer(t *testing.T) {
assert.Equal(*result, 123)
}
func TestToPointers(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToPointers")
intVals := []int{1, 2, 3}
result := ToPointers(intVals)
assert.Equal(3, len(result))
assert.Equal(1, *result[0])
assert.Equal(2, *result[1])
assert.Equal(3, *result[2])
stringVals := []string{"a", "b", "c"}
resultStr := ToPointers(stringVals)
assert.Equal(3, len(resultStr))
assert.Equal("a", *resultStr[0])
assert.Equal("b", *resultStr[1])
assert.Equal("c", *resultStr[2])
}
func TestFromPointer(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFromPointer")
intVal := 123
pointer := &intVal
result := FromPointer(pointer)
assert.Equal(123, result)
stringVal := "abc"
stringPointer := &stringVal
resultStr := FromPointer(stringPointer)
assert.Equal("abc", resultStr)
}
func TestFromPointers(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFromPointers")
intPointers := []*int{new(int), new(int), new(int)}
*intPointers[0] = 1
*intPointers[1] = 2
*intPointers[2] = 3
result := FromPointers(intPointers)
assert.Equal(3, len(result))
assert.Equal(1, result[0])
assert.Equal(2, result[1])
assert.Equal(3, result[2])
stringPointers := []*string{new(string), new(string), new(string)}
*stringPointers[0] = "a"
*stringPointers[1] = "b"
*stringPointers[2] = "c"
resultStr := FromPointers(stringPointers)
assert.Equal(3, len(resultStr))
assert.Equal("a", resultStr[0])
assert.Equal("b", resultStr[1])
assert.Equal("c", resultStr[2])
}
func TestEncodeByte(t *testing.T) {
t.Parallel()
@@ -741,3 +824,83 @@ func TestToRawUrlBase64(t *testing.T) {
d15, _ := base64.RawURLEncoding.DecodeString(r15)
assert.Equal("4+3/4?=", string(d15))
}
func TestToBigInt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToBigInt")
tests := []struct {
name string
input any
want *big.Int
hasErr bool
}{
{
name: "int",
input: 42,
want: big.NewInt(42),
},
{
name: "int8",
input: int8(127),
want: big.NewInt(127),
},
{
name: "int16",
input: int16(32000),
want: big.NewInt(32000),
},
{
name: "int32",
input: int32(123456),
want: big.NewInt(123456),
},
{
name: "int64",
input: int64(987654321),
want: big.NewInt(987654321),
},
{
name: "uint",
input: uint(987654321),
want: big.NewInt(987654321),
},
{
name: "uint8",
input: uint8(255),
want: big.NewInt(255),
},
{
name: "uint16",
input: uint16(65535),
want: big.NewInt(65535),
},
{
name: "uint32",
input: uint32(4294967295),
want: big.NewInt(4294967295),
},
{
name: "uint64",
input: uint64(18446744073709551615),
want: new(big.Int).SetUint64(18446744073709551615),
},
{
name: "unsupported type",
input: 3.14, // Unsupported type
hasErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ToBigInt(tt.input)
if (err != nil) != tt.hasErr {
t.Errorf("ToBigInt() error = %v, hasErr %v", err, tt.hasErr)
return
}
assert.Equal(tt.want, got)
})
}
}

View File

@@ -6,7 +6,6 @@
package cryptor
import (
"bufio"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
@@ -66,34 +65,37 @@ func Md5ByteWithBase64(data []byte) string {
// Md5File return the md5 value of file.
func Md5File(filename string) (string, error) {
if fileInfo, err := os.Stat(filename); err != nil {
return "", err
} else if fileInfo.IsDir() {
return "", nil
}
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
hash := md5.New()
chunkSize := 65536
for buf, reader := make([]byte, chunkSize), bufio.NewReader(file); ; {
n, err := reader.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return "", err
}
hash.Write(buf[:n])
stat, err := file.Stat()
if err != nil {
return "", err
}
if stat.IsDir() {
return "", nil
}
checksum := fmt.Sprintf("%x", hash.Sum(nil))
return checksum, nil
hash := md5.New()
buf := make([]byte, 65536) // 64KB
for {
n, err := file.Read(buf)
if err != nil && err != io.EOF {
return "", err
}
if n > 0 {
hash.Write(buf[:n])
}
if err == io.EOF {
break
}
}
return fmt.Sprintf("%x", hash.Sum(nil)), nil
}
// HmacMd5 return the hmac hash of string use md5.

View File

@@ -15,39 +15,40 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"os"
"strings"
)
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/jT5irszHx-j
func AesEcbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
length := (len(data) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
blockSize := aes.BlockSize
dataLen := len(data)
padding := blockSize - (dataLen % blockSize)
paddedLen := dataLen + padding
copy(plain, data)
paddedData := make([]byte, paddedLen)
copy(paddedData, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
for i := dataLen; i < paddedLen; i++ {
paddedData[i] = byte(padding)
}
encrypted := make([]byte, len(plain))
cipher, _ := aes.NewCipher(generateAesKey(key, size))
cipher, err := aes.NewCipher(generateAesKey(key, len(key)))
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
encrypted := make([]byte, paddedLen)
for bs := 0; bs < paddedLen; bs += blockSize {
cipher.Encrypt(encrypted[bs:], paddedData[bs:])
}
return encrypted
@@ -57,77 +58,107 @@ func AesEcbEncrypt(data, key []byte) []byte {
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/jT5irszHx-j
func AesEcbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
cipher, _ := aes.NewCipher(generateAesKey(key, size))
blockSize := aes.BlockSize
if len(encrypted)%blockSize != 0 {
panic("aes: encrypted data length is not a multiple of block size")
}
cipher, err := aes.NewCipher(generateAesKey(key, len(key)))
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
for i := 0; i < len(encrypted); i += blockSize {
cipher.Decrypt(decrypted[i:], encrypted[i:])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
if len(decrypted) == 0 {
return nil
}
padding := int(decrypted[len(decrypted)-1])
if padding == 0 || padding > blockSize {
panic("aes: invalid PKCS#7 padding")
}
for i := len(decrypted) - padding; i < len(decrypted); i++ {
if decrypted[i] != byte(padding) {
panic("aes: invalid PKCS#7 padding content")
}
}
return decrypted[:trim]
return decrypted[:len(decrypted)-padding]
}
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, _ := aes.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
padding := aes.BlockSize - len(data)%aes.BlockSize
padded := append(data, bytes.Repeat([]byte{byte(padding)}, padding)...)
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
panic("aes: failed to generate IV: " + err.Error())
}
encrypted := make([]byte, len(padded))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encrypted[aes.BlockSize:], data)
mode.CryptBlocks(encrypted, padded)
return encrypted
return append(iv, encrypted...)
}
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/IOq_g8_lKZD
func AesCbcDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, _ := aes.NewCipher(key)
if len(encrypted) < aes.BlockSize {
panic("aes: ciphertext too short")
}
if len(encrypted)%aes.BlockSize != 0 {
panic("aes: ciphertext is not a multiple of the block size")
}
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
ciphertext := encrypted[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
decrypted := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encrypted, encrypted)
mode.CryptBlocks(decrypted, ciphertext)
decrypted := pkcs7UnPadding(encrypted)
return decrypted
return pkcs7UnPadding(decrypted)
}
// AesCtrCrypt encrypt data with key use AES CTR algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/SpaZO0-5Nsp
// deprecated: use AesCtrEncrypt and AesCtrDecrypt instead.
func AesCtrCrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, _ := aes.NewCipher(key)
@@ -141,158 +172,214 @@ func AesCtrCrypt(data, key []byte) []byte {
return dst
}
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
// AesCtrEncrypt encrypt data with key use AES CTR algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
// Play: https://go.dev/play/p/x6pjPAvThRz
func AesCtrEncrypt(data, key []byte) []byte {
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
panic("aes: failed to create cipher: " + err.Error())
}
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
panic("aes: failed to generate IV: " + err.Error())
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
stream := cipher.NewCTR(block, iv)
ciphertext := make([]byte, len(data))
stream.XORKeyStream(ciphertext, data)
return encrypted
return append(iv, ciphertext...)
}
// AesCtrDecrypt decrypt data with key use AES CTR algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/x6pjPAvThRz
func AesCtrDecrypt(encrypted, key []byte) []byte {
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
if len(encrypted) < aes.BlockSize {
panic("aes: invalid ciphertext length")
}
iv := encrypted[:aes.BlockSize]
ciphertext := encrypted[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
stream := cipher.NewCTR(block, iv)
plaintext := make([]byte, len(ciphertext))
stream.XORKeyStream(plaintext, ciphertext)
return plaintext
}
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbEncrypt(data, key []byte) []byte {
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("aes: failed to generate IV: " + err.Error())
}
ciphertext := make([]byte, len(data))
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext, data)
return append(iv, ciphertext...)
}
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/tfkF10B13kH
func AesCfbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short")
panic("aes: encrypted data too short")
}
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
ciphertext := encrypted[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
plaintext := make([]byte, len(ciphertext))
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(plaintext, ciphertext)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
return plaintext
}
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
panic("aes: failed to create cipher: " + err.Error())
}
data = pkcs7Padding(data, aes.BlockSize)
encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize]
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
panic("aes: failed to generate IV: " + err.Error())
}
ciphertext := make([]byte, len(data))
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
stream.XORKeyStream(ciphertext, data)
return encrypted
return append(iv, ciphertext...)
}
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
// len(key) should be 16, 24 or 32.
// Play: https://go.dev/play/p/VtHxtkUj-3F
func AesOfbDecrypt(data, key []byte) []byte {
size := len(key)
if size != 16 && size != 24 && size != 32 {
panic("key length shoud be 16 or 24 or 32")
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
if len(data) < aes.BlockSize {
panic("aes: encrypted data too short")
}
iv := data[:aes.BlockSize]
data = data[aes.BlockSize:]
if len(data)%aes.BlockSize != 0 {
return nil
ciphertext := data[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
panic("aes: failed to create cipher: " + err.Error())
}
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
plaintext := make([]byte, len(ciphertext))
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(plaintext, ciphertext)
decrypted = pkcs7UnPadding(decrypted)
return decrypted
return plaintext
}
// AesGcmEncrypt encrypt data with key use AES GCM algorithm
// Play: https://go.dev/play/p/rUt0-DmsPCs
func AesGcmEncrypt(data, key []byte) []byte {
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
panic("aes: failed to create cipher: " + err.Error())
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
panic("aes: failed to create GCM: " + err.Error())
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
panic(err)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic("aes: failed to generate nonce: " + err.Error())
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
ciphertext := gcm.Seal(nil, nonce, data, nil)
return ciphertext
return append(nonce, ciphertext...)
}
// AesGcmDecrypt decrypt data with key use AES GCM algorithm
// Play: https://go.dev/play/p/rUt0-DmsPCs
func AesGcmDecrypt(data, key []byte) []byte {
if !isAesKeyLengthValid(len(key)) {
panic("aes: invalid key length (must be 16, 24, or 32 bytes)")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
panic("aes: failed to create cipher: " + err.Error())
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
panic("aes: failed to create GCM: " + err.Error())
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
panic("ciphertext too short")
panic("aes: ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err)
panic("aes: decryption failed: " + err.Error())
}
return plaintext
@@ -302,20 +389,17 @@ func AesGcmDecrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P
func DesEcbEncrypt(data, key []byte) []byte {
length := (len(data) + des.BlockSize) / des.BlockSize
plain := make([]byte, length*des.BlockSize)
copy(plain, data)
pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ {
plain[i] = pad
cipher, err := des.NewCipher(generateDesKey(key))
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
encrypted := make([]byte, len(plain))
cipher, _ := des.NewCipher(generateDesKey(key))
blockSize := cipher.BlockSize()
padded := pkcs5Padding(data, blockSize)
encrypted := make([]byte, len(padded))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
for i := 0; i < len(padded); i += blockSize {
cipher.Encrypt(encrypted[i:], padded[i:])
}
return encrypted
@@ -325,42 +409,50 @@ func DesEcbEncrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P
func DesEcbDecrypt(encrypted, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
cipher, err := des.NewCipher(generateDesKey(key))
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
blockSize := cipher.BlockSize()
if len(encrypted)%blockSize != 0 {
panic("des: invalid encrypted data length")
}
decrypted := make([]byte, len(encrypted))
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
for i := 0; i < len(encrypted); i += blockSize {
cipher.Decrypt(decrypted[i:], encrypted[i:])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
// Remove padding
return pkcs5UnPadding(decrypted)
}
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, _ := des.NewCipher(key)
data = pkcs7Padding(data, block.BlockSize())
block, err := des.NewCipher(key)
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
blockSize := block.BlockSize()
data = pkcs7Padding(data, blockSize)
encrypted := make([]byte, blockSize+len(data))
iv := encrypted[:blockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
panic("des: failed to generate IV: " + err.Error())
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encrypted[des.BlockSize:], data)
mode.CryptBlocks(encrypted[blockSize:], data)
return encrypted
}
@@ -369,26 +461,33 @@ func DesCbcEncrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/4cC4QvWfe3_1
func DesCbcDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, _ := des.NewCipher(key)
block, err := des.NewCipher(key)
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
iv := encrypted[:des.BlockSize]
encrypted = encrypted[des.BlockSize:]
blockSize := block.BlockSize()
if len(encrypted) < blockSize || len(encrypted)%blockSize != 0 {
panic("des: invalid encrypted data length")
}
iv := encrypted[:blockSize]
ciphertext := encrypted[blockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encrypted, encrypted)
mode.CryptBlocks(ciphertext, ciphertext)
decrypted := pkcs7UnPadding(encrypted)
return decrypted
return pkcs7UnPadding(ciphertext)
}
// DesCtrCrypt encrypt data with key use DES CTR algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/9-T6OjKpcdw
// deprecated: use DesCtrEncrypt and DesCtrDecrypt instead.
func DesCtrCrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
@@ -406,25 +505,83 @@ func DesCtrCrypt(data, key []byte) []byte {
return dst
}
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
// DesCtrEncrypt encrypt data with key use DES CTR algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
// Play: https://go.dev/play/p/S6p_WHCgH1d
func DesCtrEncrypt(data, key []byte) []byte {
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)
panic("des: failed to create cipher: " + err.Error())
}
iv := make([]byte, block.BlockSize())
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("des: failed to generate IV: " + err.Error())
}
stream := cipher.NewCTR(block, iv)
encrypted := make([]byte, len(data))
stream.XORKeyStream(encrypted, data)
// 返回前缀包含 IV便于解密
return append(iv, encrypted...)
}
// DesCtrDecrypt decrypt data with key use DES CTR algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/S6p_WHCgH1d
func DesCtrDecrypt(encrypted, key []byte) []byte {
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, err := des.NewCipher(key)
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
blockSize := block.BlockSize()
if len(encrypted) < blockSize {
panic("des: ciphertext too short")
}
iv := encrypted[:blockSize]
ciphertext := encrypted[blockSize:]
stream := cipher.NewCTR(block, iv)
decrypted := make([]byte, len(ciphertext))
stream.XORKeyStream(decrypted, ciphertext)
return decrypted
}
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbEncrypt(data, key []byte) []byte {
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, err := des.NewCipher(key)
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
iv := make([]byte, des.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("des: failed to generate IV: " + err.Error())
}
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
copy(encrypted[:des.BlockSize], iv)
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
@@ -436,44 +593,51 @@ func DesCfbEncrypt(data, key []byte) []byte {
// len(encrypted) should be great than 16, len(key) should be 8.
// Play: https://go.dev/play/p/y-eNxcFBlxL
func DesCfbDecrypt(encrypted, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, _ := des.NewCipher(key)
if len(encrypted) < des.BlockSize {
panic("encrypted data is too short")
block, err := des.NewCipher(key)
if err != nil {
panic("des: failed to create cipher: " + err.Error())
}
if len(encrypted) < des.BlockSize {
panic("des: encrypted data too short")
}
iv := encrypted[:des.BlockSize]
encrypted = encrypted[des.BlockSize:]
ciphertext := encrypted[des.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
stream.XORKeyStream(ciphertext, ciphertext)
return encrypted
return ciphertext
}
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbEncrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)
panic("des: failed to create cipher: " + err.Error())
}
data = pkcs7Padding(data, des.BlockSize)
encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize]
iv := make([]byte, des.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
panic("des: failed to generate IV: " + err.Error())
}
encrypted := make([]byte, des.BlockSize+len(data))
copy(encrypted[:des.BlockSize], iv)
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[des.BlockSize:], data)
@@ -484,25 +648,25 @@ func DesOfbEncrypt(data, key []byte) []byte {
// len(key) should be 8.
// Play: https://go.dev/play/p/74KmNadjN1J
func DesOfbDecrypt(data, key []byte) []byte {
size := len(key)
if size != 8 {
panic("key length shoud be 8")
if len(key) != 8 {
panic("des: key length must be 8 bytes")
}
block, err := des.NewCipher(key)
if err != nil {
panic(err)
panic("des: failed to create cipher: " + err.Error())
}
if len(data) < des.BlockSize {
panic("des: encrypted data too short")
}
iv := data[:des.BlockSize]
data = data[des.BlockSize:]
if len(data)%des.BlockSize != 0 {
return nil
}
ciphertext := data[des.BlockSize:]
decrypted := make([]byte, len(data))
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(decrypted, data)
stream := cipher.NewOFB(block, iv)
decrypted := make([]byte, len(ciphertext))
stream.XORKeyStream(decrypted, ciphertext)
decrypted = pkcs7UnPadding(decrypted)
@@ -565,7 +729,7 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
}
// 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 {
file, err := os.Open(pubKeyFileName)
if err != nil {
@@ -600,7 +764,7 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
}
// 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 {
file, err := os.Open(privateKeyFileName)
if err != nil {
@@ -663,7 +827,7 @@ func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte
}
// 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) {
privateKey, err := loadRasPrivateKey(privateKeyFileName)
if err != nil {
@@ -679,7 +843,7 @@ func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte,
}
// 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 {
publicKey, err := loadRsaPublicKey(pubKeyFileName)
if err != nil {
@@ -693,118 +857,3 @@ func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName stri
return rsa.VerifyPKCS1v15(publicKey, hash, hashed, signature)
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
pubKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pubKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the public key")
}
var pubKey *rsa.PublicKey
blockType := strings.ToUpper(block.Type)
if blockType == "RSA PUBLIC KEY" {
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
// todo: here should be a bug, should return nil, err
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
}
} else if blockType == "PUBLIC KEY" {
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return pubKey, nil
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
priKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(priKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the private key")
}
var privateKey *rsa.PrivateKey
blockType := strings.ToUpper(block.Type)
// PKCS#1 format
if blockType == "RSA PRIVATE KEY" {
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
privateKey, ok = priKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return privateKey, nil
}
// hashData returns the hash value of the data, using the specified hash function
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
if !hash.Available() {
return nil, errors.New("unsupported hash algorithm")
}
var hashed []byte
switch hash {
case crypto.SHA224:
h := sha256.Sum224(data)
hashed = h[:]
case crypto.SHA256:
h := sha256.Sum256(data)
hashed = h[:]
case crypto.SHA384:
h := sha512.Sum384(data)
hashed = h[:]
case crypto.SHA512:
h := sha512.Sum512(data)
hashed = h[:]
default:
return nil, errors.New("unsupported hash algorithm")
}
return hashed, nil
}

View File

@@ -74,6 +74,32 @@ func ExampleAesCtrCrypt() {
// hello
}
func ExampleAesCtrEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
enCrypt := AesCtrEncrypt([]byte(data), []byte(key))
deCrypt := AesCtrDecrypt(enCrypt, []byte(key))
fmt.Println(string(deCrypt))
// Output:
// hello
}
func ExampleAesCtrDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
enCrypt := AesCtrEncrypt([]byte(data), []byte(key))
deCrypt := AesCtrDecrypt(enCrypt, []byte(key))
fmt.Println(string(deCrypt))
// Output:
// hello
}
func ExampleAesCfbEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
@@ -227,6 +253,19 @@ func ExampleDesCtrCrypt() {
// hello
}
func ExampleDesCtrDecrypt() {
data := "hello"
key := "abcdefgh"
enCrypt := DesCtrEncrypt([]byte(data), []byte(key))
deCrypt := DesCtrDecrypt(enCrypt, []byte(key))
fmt.Println(string(deCrypt))
// Output:
// hello
}
func ExampleDesCfbEncrypt() {
data := "hello"
key := "abcdefgh"

View File

@@ -1,6 +1,17 @@
package cryptor
import "bytes"
import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"errors"
"os"
"strings"
)
func generateAesKey(key []byte, size int) []byte {
genKey := make([]byte, size)
@@ -35,3 +46,139 @@ func pkcs7UnPadding(src []byte) []byte {
unPadding := int(src[length-1])
return src[:(length - unPadding)]
}
func pkcs5Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
func pkcs5UnPadding(data []byte) []byte {
length := len(data)
if length == 0 {
return nil
}
padLen := int(data[length-1])
if padLen == 0 || padLen > length {
return nil
}
return data[:length-padLen]
}
func isAesKeyLengthValid(n int) bool {
return n == 16 || n == 24 || n == 32
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
pubKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pubKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the public key")
}
var pubKey *rsa.PublicKey
blockType := strings.ToUpper(block.Type)
if blockType == "RSA PUBLIC KEY" {
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
}
} else if blockType == "PUBLIC KEY" {
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return pubKey, nil
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
priKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(priKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the private key")
}
var privateKey *rsa.PrivateKey
blockType := strings.ToUpper(block.Type)
// PKCS#1 format
if blockType == "RSA PRIVATE KEY" {
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
privateKey, ok = priKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return privateKey, nil
}
// hashData returns the hash value of the data, using the specified hash function
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
if !hash.Available() {
return nil, errors.New("unsupported hash algorithm")
}
var hashed []byte
switch hash {
case crypto.SHA224:
h := sha256.Sum224(data)
hashed = h[:]
case crypto.SHA256:
h := sha256.Sum256(data)
hashed = h[:]
case crypto.SHA384:
h := sha512.Sum384(data)
hashed = h[:]
case crypto.SHA512:
h := sha512.Sum512(data)
hashed = h[:]
default:
return nil, errors.New("unsupported hash algorithm")
}
return hashed, nil
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/duke-git/lancet/v2/internal"
)
func TestAesEcbEncrypt(t *testing.T) {
func TestAesEcbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -16,11 +16,11 @@ func TestAesEcbEncrypt(t *testing.T) {
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
assert := internal.NewAssert(t, "TestAesEcbCrypt")
assert.Equal(data, string(aesEcbDecrypt))
}
func TestAesCbcEncrypt(t *testing.T) {
func TestAesCbcCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -29,7 +29,7 @@ func TestAesCbcEncrypt(t *testing.T) {
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
assert := internal.NewAssert(t, "TestAesCbcCrypt")
assert.Equal(data, string(aesCbcDecrypt))
}
@@ -39,14 +39,14 @@ func TestAesCtrCrypt(t *testing.T) {
data := "hello world"
key := "abcdefghijklmnop"
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
aesCtrCrypt := AesCtrEncrypt([]byte(data), []byte(key))
aesCtrDeCrypt := AesCtrDecrypt(aesCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCtrCrypt")
assert.Equal(data, string(aesCtrDeCrypt))
}
func TestAesCfbEncrypt(t *testing.T) {
func TestAesCfbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -55,11 +55,11 @@ func TestAesCfbEncrypt(t *testing.T) {
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
assert := internal.NewAssert(t, "TestAesCfbCrypt")
assert.Equal(data, string(aesCfbDecrypt))
}
func TestAesOfbEncrypt(t *testing.T) {
func TestAesOfbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -72,7 +72,7 @@ func TestAesOfbEncrypt(t *testing.T) {
assert.Equal(data, string(aesOfbDecrypt))
}
func TestDesEcbEncrypt(t *testing.T) {
func TestDesEcbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -85,7 +85,7 @@ func TestDesEcbEncrypt(t *testing.T) {
assert.Equal(data, string(desEcbDecrypt))
}
func TestDesCbcEncrypt(t *testing.T) {
func TestDesCbcCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -104,14 +104,14 @@ func TestDesCtrCrypt(t *testing.T) {
data := "hello world"
key := "abcdefgh"
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
desCtrCrypt := DesCtrEncrypt([]byte(data), []byte(key))
desCtrDeCrypt := DesCtrDecrypt(desCtrCrypt, []byte(key))
assert := internal.NewAssert(t, "TestDesCtrCrypt")
assert.Equal(data, string(desCtrDeCrypt))
}
func TestDesCfbEncrypt(t *testing.T) {
func TestDesCfbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"
@@ -124,7 +124,7 @@ func TestDesCfbEncrypt(t *testing.T) {
assert.Equal(data, string(desCfbDecrypt))
}
func TestDesOfbEncrypt(t *testing.T) {
func TestDesOfbCrypt(t *testing.T) {
t.Parallel()
data := "hello world"

51
cryptor/rsa_private.pem Normal file
View File

@@ -0,0 +1,51 @@
-----BEGIN rsa private key-----
MIIJKAIBAAKCAgEA5IqWfYbW1NlTDWE2plFWqD6CTquA0Ar/1E66lY8OMtrdpxTm
chirmoISWN0BD7r2tV9T/kHs44Sy3raAfkR5ixYF5FRkb63FdIAtoynsxS6MEE26
fgWuDAa1xwNt+t/uivnpfJk25htpBNkKGT8ii0TPPLn1N15hMHenT6PzWVjGjQxW
cp4dUA4gXTDuqaeu7Oy7Ku2yP90/ra+cjTV8DnBJ9enQhAfu1LivJO3FgmXeuC3u
qhhM3t9ZbNy7tvJI5PXDCaS8iesBvCp5carTSXWLpFEgvIhXQIsXtxezKAP3gRd8
r5JPQI9FZlxyUcyf+Htkn2A4qyKhFElnP1z9j0YLFhL/gQMdtNIY7yhSb29PyuCG
7noz25swrfxbA7ZVppM0J19JNhlpqmusBKLBNqN+KMD8EDli7NLX17S9Dj2pQF9I
8fVXPEhkdTb67rr7y3OUANKjh0Opnt3JLoj9u1X7BfCeUSR/qq3E8RFPeyxvP1tc
5QVqKBG6UPBo8nLQqdzJhUd8a1YEfXIUbdHLs2q5WVFu2MDavIp634+fzfcIFRoB
tdxf5aCdxLaLocsA/aLLyL1pK6K0rpdrSUtcEBMST26qIlU0Ht+SQtaMdE2gyjkM
M5RpLh1q/gpow+0j3zu6f5tDsu3qwgjLIx78YCTGUuCpsVMzFK4tahYSPNkCAwEA
AQKCAgEAuXz97X2uCW0lqjtXhp+HrN+nFUC/OJtkziTj7RUBmibnNX+SFdKOHMYr
K/KbtO+y4rwvSLKrGHIQVxBas6DR4SALwij4p2erVgXehIo3gEZqKaVckoH7pAki
KhdPgQmU6zkw1w7nbtWaY/Pf7WO/nrdHV+s56ilwykyi/9F6Ze7Wn43+7+ICuoHs
pJZdblcJc4Qj2RC41nq0/zwD3NwnBvT+IlgWA8MIhaArjtZospAJtwSYq3czlMRE
KUXyGOcGYMZS+RW6bFnPu6/hh271M67ymne6ESq7XkhGBDV5FCY8EItGiJ1AM47U
7eJkap2gzKUhovUOqV9eyz9UTComJETl2kmcjpZeaxrZZxtPap23IzBu6PJoPuDg
hgzRWh3BrkakLdq6Km2z+jDFEQhWeHsksozuKzln5USx9wovb9LDKKFSpKm4vzW+
7YVLZnH4Z1m4wvWQJShvZur7kkM7aNfK+xcS81OBMtqKerjWhdHqfMRVvLvtG/ev
ftPTwRy+u02w+FoYcassTS+lW+Pnhj0ZuOewsWoNvgWg1TpPEDBkY4UJOYQPkTrS
bixTBVI3teSjMXmjEAif+BG2LBCl7pFY5SW9Jk6eRvmxt9rEly13C+4tcBHEJnXu
eVUMurRuB/9BAKgj4qGHPQlG455mKHQzWJxJXBpRurh+U478xm0CggEBAOgduMra
tPDhyhy01iIAzYsh0PW9p11AcCoJkDUE/5IUf03fVt3WKdSNauHZBKTulqaCHkvz
+pjmSwPr4JdJKcJzwk5ncResFBYF177JmYXzJzwpIPRQFNP34heaUd9cVsFckddV
G3jR7c1vD33VElexUM94BubZFEqF2lO3/y0sLwhd7prKhwy6mctJyvyDFk02psDc
6XzBG/sMiZ6meWA1sP4QIM6+sYdZ9ihvTNWGb1+TGojvgOHCEQ3Hv9u/qwdkzFKj
qo25pRMV4VNMQUvYIywSB7K5c0w1ccfOINzB8kgqpHhpimAHqZw9ix4H98YmvaXS
rr0LP8ES9xVvelcCggEBAPwOs7pWvm/R3RAXZ8Erk9x68Fsn8wzoofsGP/BG5QCC
r1fDJr9aJAVggXOVNDktWRKkN7Cnib+Z7ymL5br3FfYPy945cF2e8nkx2ri0VveF
glkPCFLb3lF3iLKD8nJNtiv1rpu2v4Dj09+SzQYSIF3h55BaZGUCHLskR7Mhdd74
iTBzPaQ3NC+n0xrv8o1EXjy9w/nBt07sZojrf82Ae3mIbEFYdEHUVDaCZKKeBT4b
9W8Q5aAt56DIkSkaBAZZZhzbfxollmeKiwJ32+finR7LZ1Fj2cTHia19Ef85U8bE
Ow7E2cTDZkqaqgi+pFescl518DQ47PCeXfnFP0g65E8CggEAUApHubO3J0VE06dM
G8eZGTwc+VBf0RkyVFyd3JqPoojs6SZ1puN94yysyZpzLoiTbHF8DwbfyC/JeF2z
QZfaDZKrUyv6ZIZTGtEC92g/R2B0jBtGoNiohft5fFgbmWEXDXBlXhKb+YqybN+6
QNLjk1eynQgvoRUEGTqU8b+F/8a3pTP23muuLCaAeAhHNdHiM9f/oovK+9j/VA+b
uRiAzDtXgBSBq6k4QIs2BfVzUkIcT6HDSasFD1RDWzQhJZ6vVEpe5rRHUL3OfYlS
/M1TytqKLl09SFUIvCPFy3d5/4XljRsfQeJq8/hQdW8HdOCcgTjEttSyqr+hSWvH
xh192wKCAQB0uSY3u2XTCH9zrTMJ/HErn+7gd76REsW4JmvDjEEOHHawkJnH8SlP
KCKqcMTPWZWvEUcM0njytolPVw6ap0OPQD9reHP1lt64iwK7mB/R3gy/yztSi6kH
VvCBoqLKlfwvnUUvrNBAEsER/rxc/FXqw+tlKMbnE7RUYXeml28rQzLcsfEws7PC
Adi717QeATQWstYnObL2pHjTHSOA+ee0Hx3qoNith3M8DuQlfkH1QiNFPLDpnXhv
N5IpU3fbrNihsm/InvFon3rCONkoKAQUt6LvyOqWusSiB5Im+9g06rhinXwvJ0Ge
eMMW65nVU/FelwUWWeo3f08LlHE6tLL3AoIBABcc549sTCMhBmQS9prESurzhPVk
HCFYlR/GdlfNmRtTYssk25xaG+tiaEzXi7DXBq20TGNOATgoX7l9EWIRXkDuR6hS
aRXyn+CVU3y2dhpGZBf+EUWP0u9TNSJ/RFO0ViAriL5J9kjF9hFvG36B9kRSiMrn
mCp9NCdtRYOHjzkvbY5uRMqP8H4/nAXNMA+odPnOPDUOIcH2ztaFOtQgZxa9x/eS
eGmnJ/V/rLvS04uVw5d7juJstEspnM4MxPHSFEDN7NG13bN+c1jTY1/85icIeRBi
nP47M2kDQ6RrEdgYfPbdlx1wGdTUIeAm3duptTiwpwa5Q2Js22TMJYRTnbM=
-----END rsa private key-----

View File

@@ -1,51 +1,51 @@
-----BEGIN rsa private key-----
MIIJJwIBAAKCAgEAxZGG6vSSETKwW7bxx7NA1tXmNh+0AAsOmdwOyd77C/F7bmf3
iacdzsdJgumWpBZLpo7Xdke/6NWbzizdbawbBKkLH5vdZpEYJsbIx9vQYPRrAL1t
aRHpWwqC86aZHhJ8afo5KARkW8U1BB+MCfJ0oGOgkpHPbbs7jRNhrUP52BPbdRUw
7ZwwJHEseuAvw+nv80UmHdI5mrgKj//8r+cB1JnErqJi0yTOgYwKqOPFcMoaCoyL
RdMV8en1QZ7nY6KWF32K/OBGM8++lRsZGttFThx3Zs54QU2GFgthH5rkMtucuZUX
DN2k+QqV1XrSC/a+IDZ1zhx0+NUuExktOpOqkvc4IslIOPAcBJ4MoUWShOy91rW3
4QSV2ZgD6VMTYMce6xpH4Oh680I4ZtewZiRdBnQ4EADoxPbiBHfTQJfzUBtaZDXq
OFNN+friCITTKice0dByDlCN85bL7et0hyIsFU1xi0SmFbt3IBFwsJf5B/aorLhZ
qwCIUFvf7EahOH51PlGr7M2ltZtpgHmFBzB0zYAoYOnDU1+avzVP5l9fObH4uLl8
/fjeAr8pSB0drjaKf0Bj434aKrrftDyeGuMWpXhFu9rr/eXXUVqU/jBfHR+60m+M
BK9h8efkqtVW3EqNVApsapRFo/LNNDAC9lCKWuFBjh0igdZNjSuatG9O9rMCAwEA
AQKCAgAkwpkZ6nurG4VY60c9YwrzbkUxWODDCUukMR/HS+fmJv5SYqHaSO/w43+4
DgJrcYKN8RIB/gOlLOmaiRqCJ323dkSNkxC03tZhkKcXGL1C7Pdj4dsTq7OBrwkC
kML4p3PivB4aZgbOVHky1TPXPfeZSDA+VUcFIctIyoxoErfQDpHMzS6XZ3JFtZET
I9d/Wm29E+atscnqEchVVsoLaK4cgqfn3msI4lZSIF5zYj4AaHpD8DaRvV7d7iwm
E4z2KvBGWqObvZcSWNsbHzmil53jlMRaqpwRen0yaNefa9B97gLRPP5W9H6Xn/KV
EA85DqX9lGSC/yR8kB3Mi+iwd31LGsxXWHwTZEtyMLGjNxhkFU122iCFGsVex8zn
2bWWQ1ANczA9gyAe8Frap2ydlZw5wiToaF4+82RnndQy0RSWWUXiKSrnZjqXDEOf
NDMXBM3STT2eE6xLgIFyV/1vll4/YXoJKU217Zg4zkZ7AFWpfGXzWtlZ63ku2Ek1
CD8bupgTM9dLcC5vkgaiOSdSVClmgI3wr0Gbg4Swxl/DccpabOHc4/lEm59SuSXj
s4ZUh93dxoYvsNvzyPD0AdIhEp2xSofD6Q4qU4l5kBudIYS4xYXKVzwbbP5spixe
7qdbQnD9s+ufPjFBMRgU0FV25dyKVNCM6JlfwdYGkVDYSclDQQKCAQEA/a43eS/0
GGhASs4uM7P1iLMiDkFfsotJsPQ3klIv6m1XBjKFl4y5M0SbP9NBb0cPsReR4xCx
vyxui4gAVhjc6HBj//zegLjdzg2aGZdc0zrYocaW5CHd/IyQyRv5JwbVWWUC+Y9f
RlNEZB/WFzHbLjXaVDiVwiadURHVb0gr8LBJNp0CZ/poqQf3JwNLshVA79EE7aKv
2R2CKTFzv/GrePMZxoAH7U89qg7Nx9mmJfkC0DgTSem20ADdCDzivhFkYWRmd2k8
5E35d5ZCUz8YsDhaaIEi4MkRrC4wx8TJ/M9l0jWNNtoqqROhSdMQ40Hlc+azURWH
0jeVVw+iQYUBQwKCAQEAx1/4ZU+RzG6PiyImzfD30i972mhJk1KJ6m1K6WkklS6E
r1GttarK/ZLdueOsHQ0+YsAz8HECWJluvlI1GoXwvtrQJ4uB2vAyaSb9mer96O4G
+VjOhXBtLCKrE1Gir2ciUM/49etFe5phplxflmNKIZm/8RFSRtcMfM0NyIXvQKRL
j3rsbG6MofhPQe5dqV/e/KkgK0YTZjz9qlFiAUOQK5RtJ634QHa+27wygQ71KoIr
A/gfKTzo4Hvlt1ZIoVSc/wJI5q3jcTp3jecnE7ZeqZEq39ECSFK4wIp+BTV9dgDF
rqJfBv+T8WXPWlyWYL2PDaVjkcP+hnZyndhrxIjl0QKCAQATGzQCZKI11GRDPhQs
wLkVeqht+m6CvAro0Pv+gUIqneGYDeYR6/9m5RvsSrT69Jcx91x9hZxdkbMhvzSH
R4gb2ZsuhJUdjM7n3g5lLwlGv3/hb1ueAw79HnkpLHovfkxDV2BJ+4ppeYwFHoC5
bn/bkyLGhRyCavYeTQGOSBlHH3/kJR8xRaLsWDU9MvpYh1FEvrFoVIB6Ecbj412c
b09C5MxkLvBp5zX1wPeuva3N3ltf41lQj9G9CNOc8zkAp76vtygghkSXzWekoiSM
sIvjedsVU+SUmZBDL5ds9F1iNRyuIGpqXzQnV9sl/EehVi059X479SstohIVZJAL
70CRAoIBADx/69FaXuTJG/fdaicOGzWFwTCaetEgt8cSWSwRAs/eKNdGks7Qv/5I
hc31v3GlZivzJQ+uS0b1cDyCl6ivwkk3/vgxBjuYrcNo2IXPYDNBn44km660WfyN
nJolKJfPVQNuyJ5fQVtrknw4Ba2N/Z3KmQse9XeXpCtu0eLqeZqFAITOiVWyCizQ
O8TG/1b5R6Il9CQNhK9+jH8VaQb3KJWA2cqzqORu5CWrqyusjUrT6xeuQE50NDqC
fkevACUsluNvHw4Y8C6YzlSaMypxKbKM4ZS/SNEHgRa0L9+GxsZ7H4GJtgpandsf
yNcjx2Idw34hlOBTuDW3Vbp+RN2DqTECggEAYKowYENmxNCiIyVTT8dhunMOoT1h
v9Qn5Rh9QlvcTN+ncnkc41VqO4yV/FbPM+q1cpUiBV4UZL4rQMTOOaqqL2B2p5Li
urWNxTNq/eB7ZfODLqXVfMvRwPKdKxyIjmUZE3bI60imHEDOZ2qWQTYSdE+2zfHJ
IGLuhvU6Exf9rWCIfijaARBgR3ySG7446mZdn9oOUFyqH14GduYwLmecS4EHdwkZ
urb+eIB04u6a48F9wg2Jgz/3BV/S3/8UA3pG1sjFdRHlIIozK4hM7mOe65zO3Tp3
nEho0UzzTj1VU00v7fWNQJBPWlst0Bk6gKInRQZxAYBmvsyiw4+Jt3516A==
MIIJKQIBAAKCAgEAuzBz+aC+e7Lvny2zYlcyAfG6AAtPkxZqJ9JkYkM+0CP87pe0
xOXQh4dz9iJekOwAq7FKpasUEUzkTm6Z0PUoj/TWY/xpoPNXXzz/5dz3u6r/A5Tu
mJ6BmX/7K/x8FsokIeP+lWaN1l+7uBKK8rgfm4AZOXd/plzBkTrnu6lKG/rH9cnr
2leKWDqk2jcG6r15r/07MdStpWgt0OBYoHzvjLJWmJ08VrnF9PFtWhL939xSAIic
FzJ0T9fAdmSSYmg22mKgN1zeWtZndJ/Ejv+5YlWmuFJ8YKvwR0+4XIRLX9qsMy7q
PTh7zsZhgtKzyb7qvVqDh2pmwekGJcwxGcoCLHLqNKf/dD+4vXxgS+f1ObfOcJpD
qBajD/U6BXtrZ/p4cvoYnZA383YR/CRG/nJ8jIvt2FutT4hsNSd0L6c2mfo5mTno
DOEL3mkreZ4Az+GE57jMw1Ia9jwkM1QQoy0a+kTiW8BedNqcRnVVGT9/OS/ggyKF
wxJ/Xh1DfxZXAuyCBRUJUyVl9YCr2y30znguCdaTTViA9UbrjtcE2bZtnqOMAU2s
08F0IiaGLKKMhrxUoXLgngXSX7gomC4aEfcg5hf7ft6FA+bXB9DHwGdv/UyrGr88
nve5um1OT5kmyOujKpka4QZ5/rU+RznBE0UWDcHAyc+Zv+te0DqPUNcAW5ECAwEA
AQKCAgBLJ0Do0Cip8UVTWz3SFb/2F97dda0VGMK2CjpTWTw2xLwf7ric9MesIi3k
fBgLhzUduaiGqxD7gSuIcc8/na4TXfFVY1nlTM2fZxY2a2jq59RK09iXXcwanM9y
8YPAgpfPI4Jq6Sm5D+aGGKvAlzvZaqy17cxKNqNgc43mQimG4kC15cPTfaIFmkXl
doJIbJoWlkzVzNWKuzDp06jBhmeGzXMHAtne1+cqWGPW7hkPb51cqXxBs/gOtkiH
QAmliMG9HCvHDnoXbk1K/XolD3aWjFzLVBKrnVxyxQb33gWFDn5kbkmNGshaVDuC
EqYsMYJ9U4HLNGTdJXlaY4izGe+UyExET5p2KYKC9S34jMvR5k9Hf4pATSUYRhjL
t/EV8EZlWCJGvGRAdtlKNLjRuIAiMTofUZca+sCHDvdcv8+/imOnKCXOETtcOHzw
I7MRdIi2JigcBKuKaua9H77cEuvwG9Bb7aLbqQ3XM5JhoBEBe2jHG39GYDAAjEWz
XWo2ri8rkU0nhixN26x7DXCfMewxZ/zc4czBTU2giM0Yrh4BMpRpHnw14QXRb58y
eTD7GVrC9g1/6HXsAzzBfKyTMZhZhmfjcgSuYMUbzwIvvttJQtw5Ic/LJmR1Eg2F
YZ3mUmwJwDEPyVlV2mXNUYYa64v7O3h+NsjXukWXw080fWdsoQKCAQEA8ZcVn863
wXQVex7RcXs5frdnKEtHx6V6tXXq4tvK71Jbkny3gOmPqwwEF0fk4m2Fo07CmJkX
t5o0tbPxfVbxeWGRGubAstjd5oWgt6nMAgcEkRbAzYM8qLGAGekS4g5+2/SjrQhG
oR2phBv+T9w/Oglf6mVzc8YDNP9B0PB0CTICTYhej16Qhc/jpFmXkjXfslGlUp0F
WVkNE7BZEk/fNgCbmAV1hCcDt7MwoOYBqGBoWb3tRKNhtBIDfJY1LVPjB6Jo3FWl
nolJ1v1In9MhsNudZ6QlYbO8uMadsx3a1Flsu/w69TT+sPjmw+GoSzGuMlH15cFY
qZZ6k75WmwyRGwKCAQEAxlq0SEK86+5zAIvVRQI5pQk0HEGy0dtcUgwBhpy13Bga
sCezorJwS1tEHXfWYMtwmHytMXbySnFQEx5jJLFaQhPfOHybHV94fqq+qcC+NuEt
z2KMoQG+zlupH5LwZv3RzzMSng0AuxNaiPx/tXfXM+5O19wb8VKu7X+hkgOW+psu
wGnofT1zYTCWEbRPZENSL6Mi8BShwu3UIMFhKhVZJZH6MOSU/AoULv49ije39Z58
B06IERBIGpM6FE6L73BHphbUh9Osr/I9vbi7zCzt/utQ1uzMzzxxJjjadYf1K7xa
MYsmtKJ85+dG2/WOw6bRSGk1Dw9KqUBqHQ7bwXq8wwKCAQEAtrCwuotg69qzz8oL
SgyL+uYIDTF4U2Iwu/4ypGDfQkD+XHURc1uruAY7JbvJOuzlbQxHHYxPohjrmSg9
CrJvooGEcFplCBn1G7ibQ6gUTMgvzOPu4rpGaa7oly9ohye9COojx9qFRpsesHdW
xd9gtKuYK7GSL89iZ3ZLuAvNQ5LcqPLhxvsUwQvnMkZJ11gEFF2nbiStgdZUjDoD
8VQTEEw/XSNrrYavSgAoWtP0FvbokkyMmyYN4VTp7BHOnrtb6E8Jiuz9dDiPbRNW
Ev5e8NXyXwiC+DIqGXSglm2SKJiDIFjp4Lm1i/B82U3QrSQhfY37LEYcnQndIdKC
vXcwVwKCAQA0K2UhYFQ6JYQfz6dvOA+bRZlsGSeEJJLajYfVNOBsG/bhAAAyOYZp
e36l1YAQA1IA+UHAMc22IKlz7dkbrH3VxU4/mB5gEl0py5TMJwKggoc+9WeRbVkX
A2qvAEG0hOuq+H7cDQV1LrjwMKESRIvYf8RC6AR9a0bQ9nGzarhJ/4jDWNeqIQB4
voOp8mezMjWqi9jDllmZYF4bo2D/5Y+F3ygTtfstcyUt2vaqpM8AjgeHEHOfMU4V
l0V+U85gUoK1v2l0tArGWAs/HBhgsiyCkLe5X5zaoMYNzIRAx1qHf0mloDi058u8
Xsr3TVWYRgbjabBn3pi/fU6rh93qvHJrAoIBAQCHNkNclB3UMBoZv2Dnd2ZwUZnP
BzN6pq/NjG4GnjSCxPYwYnvB1TakTVLTYOjMS/7TCsFqPFSi93mjDsZ+0ZHBUZOH
076AdxzFD+WxxYZ2+vl3iRhgERY3LgqHOBTt7O1/OYcNraJ6Pk2ppU/PqYaVb389
2dsgqEEbO0np59I2+BP6giIFr3L2xKk2CRdVLKqCQ8FNx2xpi+1kcJlUW+ZxPiwu
JaOA/mNHOz3Kq4DDE+1XjvKq602zm7D1oG67xM/gE5UV/KT7auI9M+zQcTPWZ3T6
cl5A3z4tP9KdWJInadUQyOrVIHCbqxIlZCQjYFo2m9HzFr6fWbgtWC+IyGJQ
-----END rsa private key-----

14
cryptor/rsa_public.pem Normal file
View File

@@ -0,0 +1,14 @@
-----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5IqWfYbW1NlTDWE2plFW
qD6CTquA0Ar/1E66lY8OMtrdpxTmchirmoISWN0BD7r2tV9T/kHs44Sy3raAfkR5
ixYF5FRkb63FdIAtoynsxS6MEE26fgWuDAa1xwNt+t/uivnpfJk25htpBNkKGT8i
i0TPPLn1N15hMHenT6PzWVjGjQxWcp4dUA4gXTDuqaeu7Oy7Ku2yP90/ra+cjTV8
DnBJ9enQhAfu1LivJO3FgmXeuC3uqhhM3t9ZbNy7tvJI5PXDCaS8iesBvCp5carT
SXWLpFEgvIhXQIsXtxezKAP3gRd8r5JPQI9FZlxyUcyf+Htkn2A4qyKhFElnP1z9
j0YLFhL/gQMdtNIY7yhSb29PyuCG7noz25swrfxbA7ZVppM0J19JNhlpqmusBKLB
NqN+KMD8EDli7NLX17S9Dj2pQF9I8fVXPEhkdTb67rr7y3OUANKjh0Opnt3JLoj9
u1X7BfCeUSR/qq3E8RFPeyxvP1tc5QVqKBG6UPBo8nLQqdzJhUd8a1YEfXIUbdHL
s2q5WVFu2MDavIp634+fzfcIFRoBtdxf5aCdxLaLocsA/aLLyL1pK6K0rpdrSUtc
EBMST26qIlU0Ht+SQtaMdE2gyjkMM5RpLh1q/gpow+0j3zu6f5tDsu3qwgjLIx78
YCTGUuCpsVMzFK4tahYSPNkCAwEAAQ==
-----END rsa public key-----

View File

@@ -1,14 +1,14 @@
-----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxZGG6vSSETKwW7bxx7NA
1tXmNh+0AAsOmdwOyd77C/F7bmf3iacdzsdJgumWpBZLpo7Xdke/6NWbzizdbawb
BKkLH5vdZpEYJsbIx9vQYPRrAL1taRHpWwqC86aZHhJ8afo5KARkW8U1BB+MCfJ0
oGOgkpHPbbs7jRNhrUP52BPbdRUw7ZwwJHEseuAvw+nv80UmHdI5mrgKj//8r+cB
1JnErqJi0yTOgYwKqOPFcMoaCoyLRdMV8en1QZ7nY6KWF32K/OBGM8++lRsZGttF
Thx3Zs54QU2GFgthH5rkMtucuZUXDN2k+QqV1XrSC/a+IDZ1zhx0+NUuExktOpOq
kvc4IslIOPAcBJ4MoUWShOy91rW34QSV2ZgD6VMTYMce6xpH4Oh680I4ZtewZiRd
BnQ4EADoxPbiBHfTQJfzUBtaZDXqOFNN+friCITTKice0dByDlCN85bL7et0hyIs
FU1xi0SmFbt3IBFwsJf5B/aorLhZqwCIUFvf7EahOH51PlGr7M2ltZtpgHmFBzB0
zYAoYOnDU1+avzVP5l9fObH4uLl8/fjeAr8pSB0drjaKf0Bj434aKrrftDyeGuMW
pXhFu9rr/eXXUVqU/jBfHR+60m+MBK9h8efkqtVW3EqNVApsapRFo/LNNDAC9lCK
WuFBjh0igdZNjSuatG9O9rMCAwEAAQ==
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuzBz+aC+e7Lvny2zYlcy
AfG6AAtPkxZqJ9JkYkM+0CP87pe0xOXQh4dz9iJekOwAq7FKpasUEUzkTm6Z0PUo
j/TWY/xpoPNXXzz/5dz3u6r/A5TumJ6BmX/7K/x8FsokIeP+lWaN1l+7uBKK8rgf
m4AZOXd/plzBkTrnu6lKG/rH9cnr2leKWDqk2jcG6r15r/07MdStpWgt0OBYoHzv
jLJWmJ08VrnF9PFtWhL939xSAIicFzJ0T9fAdmSSYmg22mKgN1zeWtZndJ/Ejv+5
YlWmuFJ8YKvwR0+4XIRLX9qsMy7qPTh7zsZhgtKzyb7qvVqDh2pmwekGJcwxGcoC
LHLqNKf/dD+4vXxgS+f1ObfOcJpDqBajD/U6BXtrZ/p4cvoYnZA383YR/CRG/nJ8
jIvt2FutT4hsNSd0L6c2mfo5mTnoDOEL3mkreZ4Az+GE57jMw1Ia9jwkM1QQoy0a
+kTiW8BedNqcRnVVGT9/OS/ggyKFwxJ/Xh1DfxZXAuyCBRUJUyVl9YCr2y30zngu
CdaTTViA9UbrjtcE2bZtnqOMAU2s08F0IiaGLKKMhrxUoXLgngXSX7gomC4aEfcg
5hf7ft6FA+bXB9DHwGdv/UyrGr88nve5um1OT5kmyOujKpka4QZ5/rU+RznBE0UW
DcHAyc+Zv+te0DqPUNcAW5ECAwEAAQ==
-----END rsa public key-----

View File

@@ -15,7 +15,6 @@ func TestSinglyLink_InsertAtFirst(t *testing.T) {
link.InsertAtHead(1)
link.InsertAtHead(2)
link.InsertAtHead(3)
link.Print()
expected := []int{3, 2, 1}
values := link.Values()
@@ -32,7 +31,6 @@ func TestSinglyLink_InsertAtTail(t *testing.T) {
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
link.Print()
expected := []int{1, 2, 3}
values := link.Values()
@@ -77,7 +75,6 @@ func TestSinglyLink_DeleteAtHead(t *testing.T) {
link.InsertAtTail(4)
link.DeleteAtHead()
link.Print()
expected := []int{2, 3, 4}
values := link.Values()

View File

@@ -55,12 +55,12 @@ func (q *ArrayQueue[T]) IsFull() bool {
// Front return front value of queue
func (q *ArrayQueue[T]) Front() T {
return q.data[0]
return q.data[q.head]
}
// Back return back value of queue
func (q *ArrayQueue[T]) Back() T {
return q.data[q.size-1]
return q.data[q.tail-1]
}
// EnQueue put element into queue

View File

@@ -100,12 +100,6 @@ func TestBSTree_Delete(t *testing.T) {
acturl1 := bstree.InOrderTraverse()
assert.Equal([]int{2, 5, 6, 7}, acturl1)
//todo
// bstree.DeletetNode(6, comparator)
// bstree.Print()
// acturl2 := bstree.InOrderTraverse()
// assert.Equal([]int{2, 5, 7}, acturl2)
}
func TestBSTree_Depth(t *testing.T) {

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
func AddMinute(t time.Time, minute int64) time.Time {
return t.Add(time.Minute * time.Duration(minute))
func AddMinute(t time.Time, minutes int64) time.Time {
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
func AddHour(t time.Time, hour int64) time.Time {
return t.Add(time.Hour * time.Duration(hour))
func AddHour(t time.Time, hours int64) time.Time {
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
func AddDay(t time.Time, day int64) time.Time {
return t.Add(24 * time.Hour * time.Duration(day))
func AddDay(t time.Time, days int64) time.Time {
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.
// Play: https://go.dev/play/p/MqW2ujnBx10
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.
@@ -213,13 +280,9 @@ func EndOfDay(t time.Time) time.Time {
}
// BeginOfWeek return beginning week, default week begin from Sunday.
// Play: https://go.dev/play/p/ynjoJPz7VNV
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
var beginFromWeekday = time.Sunday
if len(beginFrom) > 0 {
beginFromWeekday = beginFrom[0]
}
y, m, d := t.AddDate(0, 0, int(beginFromWeekday-t.Weekday())).Date()
// Play: https://go.dev/play/p/DCHdcL6gnfV
func BeginOfWeek(t time.Time, beginFrom time.Weekday) time.Time {
y, m, d := t.AddDate(0, 0, int(beginFrom-t.Weekday())).Date()
beginOfWeek := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
if beginOfWeek.After(t) {
return beginOfWeek.AddDate(0, 0, -7)
@@ -228,13 +291,9 @@ func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
}
// EndOfWeek return end week time, default week end with Saturday.
// Play: https://go.dev/play/p/i08qKXD9flf
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time {
var endWithWeekday = time.Saturday
if len(endWith) > 0 {
endWithWeekday = endWith[0]
}
y, m, d := t.AddDate(0, 0, int(endWithWeekday-t.Weekday())).Date()
// Play: https://go.dev/play/p/mGSA162YgX9
func EndOfWeek(t time.Time, endWith time.Weekday) time.Time {
y, m, d := t.AddDate(0, 0, int(endWith-t.Weekday())).Date()
var endWithWeek = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
if endWithWeek.Before(t) {
endWithWeek = endWithWeek.AddDate(0, 0, 7)
@@ -445,7 +504,7 @@ func GenerateDatetimesBetween(start, end time.Time, layout string, interval stri
}
// 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 {
minTime := t1
@@ -459,7 +518,7 @@ func Min(t1 time.Time, times ...time.Time) time.Time {
}
// 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 {
maxTime := t1
@@ -473,7 +532,7 @@ func Max(t1 time.Time, times ...time.Time) time.Time {
}
// 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) {
maxTime = t1
minTime = t1

View File

@@ -7,71 +7,141 @@ import (
)
func ExampleAddDay() {
now := time.Now()
date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
tomorrow := AddDay(now, 1)
diff1 := tomorrow.Sub(now)
after1Day := AddDay(date, 1)
before1Day := AddDay(date, -1)
yesterday := AddDay(now, -1)
diff2 := yesterday.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
// Output:
// 24h0m0s
// -24h0m0s
// 2021-01-02 00:00:00
// 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() {
now := time.Now()
date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Hours := AddHour(now, 2)
diff1 := after2Hours.Sub(now)
after2Hours := AddHour(date, 2)
before2Hours := AddHour(date, -2)
before2Hours := AddHour(now, -2)
diff2 := before2Hours.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
// Output:
// 2h0m0s
// -2h0m0s
// 2021-01-01 02:00:00
// 2020-12-31 22:00:00
}
func ExampleAddMinute() {
now := time.Now()
date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Minutes := AddMinute(now, 2)
diff1 := after2Minutes.Sub(now)
after2Minutes := AddMinute(date, 2)
before2Minutes := AddMinute(date, -2)
before2Minutes := AddMinute(now, -2)
diff2 := before2Minutes.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
// Output:
// 2m0s
// -2m0s
// 2021-01-01 00:02:00
// 2020-12-31 23:58:00
}
func ExampleAddYear() {
now := time.Now()
date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := AddYear(now, 1)
diff1 := after1Year.Sub(now)
after2Years := AddYear(date, 2)
before2Years := AddYear(date, -2)
before1Year := AddYear(now, -1)
diff2 := before1Year.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Years.Format("2006-01-02"))
fmt.Println(before2Years.Format("2006-01-02"))
// Output:
// 8760h0m0s
// -8760h0m0s
// 2023-01-01
// 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() {
@@ -229,23 +299,23 @@ func ExampleEndOfDay() {
func ExampleBeginOfWeek() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := BeginOfWeek(input)
result := BeginOfWeek(input, time.Monday)
fmt.Println(result)
// Output:
// 2023-01-08 00:00:00 +0000 UTC
// 2023-01-02 00:00:00 +0000 UTC
}
func ExampleEndOfWeek() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := EndOfWeek(input)
result := EndOfWeek(input, time.Sunday)
fmt.Println(result)
// Output:
// 2023-01-14 23:59:59.999999999 +0000 UTC
// 2023-01-08 23:59:59.999999999 +0000 UTC
}
func ExampleBeginOfMonth() {

View File

@@ -9,78 +9,427 @@ import (
func TestAddYear(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddDay")
now := time.Now()
after2Years := AddYear(now, 1)
diff1 := after2Years.Sub(now)
assert.Equal(float64(8760), diff1.Hours())
tests := []struct {
inputDate string
years int
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)
diff2 := before2Years.Sub(now)
assert.Equal(float64(-8760), diff2.Hours())
}
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)
for _, tt := range tests {
date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
result := AddYear(date, int64(tt.years))
assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
}
func TestAddDay(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddDay")
now := time.Now()
after2Days := AddDay(now, 2)
diff1 := after2Days.Sub(now)
assert.Equal(float64(48), diff1.Hours())
tests := []struct {
inputDate string
days int
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)
diff2 := before2Days.Sub(now)
assert.Equal(float64(-48), diff2.Hours())
for _, tt := range tests {
date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
result := AddDay(date, int64(tt.days))
assert.Equal(tt.expected, result.Format("2006-01-02 15:04:05"))
}
}
func TestAddHour(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddHour")
now := time.Now()
after2Hours := AddHour(now, 2)
diff1 := after2Hours.Sub(now)
assert.Equal(float64(2), diff1.Hours())
tests := []struct {
inputDate string
hours int
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) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddMinute")
now := time.Now()
after2Minutes := AddMinute(now, 2)
diff1 := after2Minutes.Sub(now)
assert.Equal(float64(2), diff1.Minutes())
tests := []struct {
inputDate string
minutes int
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)
diff2 := before2Minutes.Sub(now)
assert.Equal(float64(-2), diff2.Minutes())
for _, tt := range tests {
date, _ := time.Parse("2006-01-02 15:04:05", tt.inputDate)
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) {
@@ -262,9 +611,9 @@ func TestBeginOfWeek(t *testing.T) {
assert := internal.NewAssert(t, "TestBeginOfWeek")
expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local)
expected := time.Date(2022, 2, 14, 0, 0, 0, 0, time.Local)
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
actual := BeginOfWeek(td)
actual := BeginOfWeek(td, time.Monday)
assert.Equal(expected, actual)
}
@@ -274,9 +623,9 @@ func TestEndOfWeek(t *testing.T) {
assert := internal.NewAssert(t, "TestEndOfWeek")
expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local)
expected := time.Date(2022, 2, 20, 23, 59, 59, 999999999, time.Local)
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
actual := EndOfWeek(td)
actual := EndOfWeek(td, time.Sunday)
assert.Equal(expected, actual)
}
@@ -578,3 +927,19 @@ func TestMaxMin(t *testing.T) {
assert.Equal(oneMinuteAgo, min)
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,8 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],
},
{ text: 'datetime', link: '/en/api/packages/datetime' },
{ text: 'enum', link: '/en/api/packages/enum' },
{ text: 'eventbus', link: '/en/api/packages/eventbus' },
{ text: 'fileutil', link: '/en/api/packages/fileutil' },
{ text: 'formatter', link: '/en/api/packages/formatter' },
{ text: 'function', link: '/en/api/packages/function' },
@@ -127,9 +129,9 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
{ text: 'stream', link: '/en/api/packages/stream' },
{ text: 'struct', link: '/en/api/packages/struct' },
{ text: 'strutil', link: '/en/api/packages/strutil' },
{ text: 'system', link: '/en/api/packages/system' },
{ text: 'tuple', link: '/en/api/packages/tuple' },
{ text: 'validator', link: '/en/api/packages/validator' },
{ text: 'system', link: '/en/api/packages/system' },
{ text: 'xerror', link: '/en/api/packages/xerror' },
],
},

View File

@@ -127,7 +127,8 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],
},
{ text: '日期&时间', link: '/api/packages/datetime' },
{ text: '文件', link: '/api/packages/fileutil' },
{ text: '事件总线', link: '/api/packages/eventbus' },
{ text: '文件处理', link: '/api/packages/fileutil' },
{ text: '格式化工具', link: '/api/packages/formatter' },
{ text: '函数', link: '/api/packages/function' },
{ text: '数学工具', link: '/api/packages/mathutil' },
@@ -140,9 +141,10 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
{ text: '流', link: '/api/packages/stream' },
{ text: '结构体', link: '/api/packages/struct' },
{ text: '字符串', link: '/api/packages/strutil' },
{ text: '系统', link: '/api/packages/system' },
{ text: '枚举', link: '/api/packages/enum' },
{ text: '元组', link: '/api/packages/tuple' },
{ text: '验证器', link: '/api/packages/validator' },
{ text: '系统工具函数', link: '/api/packages/system' },
{ text: '错误处理', link: '/api/packages/xerror' },
],
},

View File

@@ -2,10 +2,9 @@
outline: deep
---
# API概述
<b>lancet柳叶刀是一个功能强大、全面、高效、可复用的go语言工具函数库。包含25个包超过600个工具函数。功能涵盖字符串处理、切片处理、网络、并发、加解密、文件处理、时间/日期、流处理、迭代器等等。</b>
# API 概述
<b>lancet柳叶刀是一个功能强大、全面、高效、可复用的 go 语言工具函数库。包含 25 个包,超过 600 个工具函数。功能涵盖字符串处理、切片处理、网络、并发、加解密、文件处理、时间/日期、流处理、迭代器等等。</b>
<style>
.package-title {
@@ -27,7 +26,7 @@ outline: deep
display: inline-block;
vertical-align: middle;
line-height: 40px;
background: #10b981;
background: #6cadf5;
border: 1px solid;
margin-right: 10px;
margin-bottom: 10px;
@@ -47,6 +46,8 @@ outline: deep
<div class="package-cell">cryptor</div>
<div class="package-cell">datastructure</div>
<div class="package-cell">datetime</div>
<div class="package-cell">enum</div>
<div class="package-cell">eventbus</div>
<div class="package-cell">fileutil</div>
<div class="package-cell">formatter</div>
<div class="package-cell">function</div>
@@ -66,4 +67,4 @@ outline: deep
<div class="package-cell">validator</div>
<div class="package-cell">xerror</div>
</div>
</div>
</div>

View File

@@ -8,7 +8,7 @@ algorithm 算法包实现一些基本算法sortsearchlrucache。
- [https://github.com/duke-git/lancet/blob/main/algorithm/sort.go](https://github.com/duke-git/lancet/blob/main/algorithm/sort.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/search.go](https://github.com/duke-git/lancet/blob/main/algorithm/search.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go)
<div STYLE="page-break-after: always;"></div>

View File

@@ -7,6 +7,7 @@
## 源码:
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go)
<div STYLE="page-break-after: always;"></div>
@@ -35,6 +36,17 @@ import (
- [Take](#Take)
- [Tee](#Tee)
### KeyedLocker
- [NewKeyedLocker](#NewKeyedLocker)
- [KeyedLocker_Do](#Do)
- [NewRWKeyedLocker](#NewRWKeyedLocker)
- [RLock](#RLock)
- [Lock](#Lock)
- [NewTryKeyedLocker](#NewTryKeyedLocker)
- [TryLock](#TryLock)
- [Unlock](#Unlock)
<div STYLE="page-break-after: always;"></div>
## 文档
@@ -452,3 +464,389 @@ func main() {
// 1
}
```
### KeyedLocker
### <span id="NewKeyedLocker">NewKeyedLocker</span>
<p>NewKeyedLocker创建一个新的KeyedLocker并为锁的过期设置指定的 TTL。KeyedLocker 是一个简单的键值锁实现,允许非阻塞的锁获取。</p>
<b>函数签名:</b>
```go
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GzeyC33T5rw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
task := func() {
fmt.Println("Executing task...")
time.Sleep(1 * time.Second)
fmt.Println("Task completed.")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := locker.Do(ctx, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel2()
if err := locker.Do(ctx2, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
// Output:
// Executing task...
// Task completed.
// Task successfully executed.
// Executing task...
// Task completed.
// Task successfully executed.
}
```
### <span id="Do">Do</span>
<p>为指定的键获取锁并执行提供的函数。</p>
<b>函数签名:</b>
```go
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GzeyC33T5rw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
task := func() {
fmt.Println("Executing task...")
time.Sleep(1 * time.Second)
fmt.Println("Task completed.")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := locker.Do(ctx, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel2()
if err := locker.Do(ctx2, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
// Output:
// Executing task...
// Task completed.
// Task successfully executed.
// Executing task...
// Task completed.
// Task successfully executed.
}
```
### <span id="NewRWKeyedLocker">NewRWKeyedLocker</span>
<p>NewRWKeyedLocker创建一个新的RWKeyedLocker并为锁的过期设置指定的 TTL。RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。</p>
<b>函数签名:</b>
```go
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/CkaJWWwZm9)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.Lock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="RLock">RLock</span>
<p>RLock为指定的键获取读锁并执行提供的函数。</p>
<b>函数签名:</b>
```go
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZrCr8sMo77T)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.RLock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="Lock">Lock</span>
<p>Lock为指定的键获取锁并执行提供的函数。</p>
<b>函数签名:</b>
```go
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WgAcXbOPKGk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.Lock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="NewTryKeyedLocker">NewTryKeyedLocker</span>
<p>创建一个TryKeyedLocker实例TryKeyedLocker是KeyedLocker的非阻塞版本。</p>
<b>函数签名:</b>
```go
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```
### <span id="TryLock">TryLock</span>
<p>TryLock尝试获取指定键的锁。如果锁成功获取则返回true否则返回false。</p>
<b>函数签名:</b>
```go
func (l *TryKeyedLocker[K]) TryLock(key K) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```
### <span id="Unlock">Unlock</span>
<p>释放指定键的锁。</p>
<b>函数签名:</b>
```go
func (l *TryKeyedLocker[K]) Unlock(key K)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```

View File

@@ -33,6 +33,9 @@ import (
- [ToJson](#ToJson)
- [ToMap](#ToMap)
- [ToPointer](#ToPointer)
- [ToPointers](#ToPointers)
- [FromPointer](#FromPointer)
- [FromPointers](#FromPointers)
- [ToString](#ToString)
- [StructToMap](#StructToMap)
- [MapToSlice](#MapToSlice)
@@ -47,6 +50,7 @@ import (
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
- [ToBigInt](#ToBigInt)
<div STYLE="page-break-after: always;"></div>
@@ -372,7 +376,7 @@ import (
func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result, err := ToJson(aMap)
result, err := convertor.ToJson(aMap)
if err != nil {
fmt.Printf("%v", err)
@@ -455,6 +459,108 @@ func main() {
}
```
### <span id="ToPointers">ToPointers</span>
<p>将值的切片转换为指针的切片。</p>
<b>函数签名:</b>
```go
func ToPointers[T any](values []T) []*T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZUoXd2i5ZkV)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
strs := []string{"a", "b", "c"}
pointerStrs := convertor.ToPointers(strs)
fmt.Println(*pointerStrs[0])
fmt.Println(*pointerStrs[1])
fmt.Println(*pointerStrs[2])
// Output:
// a
// b
// c
}
```
### <span id="FromPointer">FromPointer</span>
<p>返回指针所指向的值。</p>
<b>函数签名:</b>
```go
func FromPointer[T any](ptr *T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wAp90V7Zu6g)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
str := "abc"
strPtr := &str
result := convertor.FromPointer(strPtr)
fmt.Println(result)
// Output:
// abc
}
```
### <span id="FromPointers">FromPointers</span>
<p>将指针的切片转换为值的切片。</p>
<b>函数签名:</b>
```go
func FromPointers[T any](pointers []*T) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qIPsyYtNy3Q)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
strs := []string{"a", "b", "c"}
strPtr := []*string{&strs[0], &strs[1], &strs[2]}
result := convertor.FromPointers(strPtr)
fmt.Println(result[0])
fmt.Println(result[1])
fmt.Println(result[2])
// Output:
// a
// b
// c
}
```
### <span id="ToString">ToString</span>
<p>将值转换为字符串,对于数字、字符串、[]byte将转换为字符串。 对于其他类型(切片、映射、数组、结构体)将调用 json.Marshal</p>
@@ -1148,4 +1254,34 @@ func main() {
// dHJ1ZQ
// ZXJy
}
```
```
### <span id="ToBigInt">ToBigInt</span>
<p>将整数值转换为bigInt。</p>
<b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/X3itkCxwB_x)</span></b>
```go
func ToBigInt[T any](v T) (*big.Int, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
n := 9876543210
bigInt, _ := convertor.ToBigInt(n)
fmt.Println(bigInt)
// Output:
// 9876543210
}
```

View File

@@ -27,7 +27,9 @@ import (
- [AesEcbDecrypt](#AesEcbDecrypt)
- [AesCbcEncrypt](#AesCbcEncrypt)
- [AesCbcDecrypt](#AesCbcDecrypt)
- [AesCtrCrypt](#AesCtrCrypt)
- [AesCtrCrypt<sup>deprecated</sup>](#AesCtrCrypt)
- [AesCtrEncrypt](#AesCtrEncrypt)
- [AesCtrDecrypt](#AesCtrDecrypt)
- [AesCfbEncrypt](#AesCfbEncrypt)
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
@@ -40,7 +42,7 @@ import (
- [DesEcbDecrypt](#DesEcbDecrypt)
- [DesCbcEncrypt](#DesCbcEncrypt)
- [DesCbcDecrypt](#DesCbcDecrypt)
- [DesCtrCrypt](#DesCtrCrypt)
- [DesCtrCrypt<sup>deprecated</sup>](#DesCtrCrypt)
- [DesCfbEncrypt](#DesCfbEncrypt)
- [DesCfbDecrypt](#DesCfbDecrypt)
- [DesOfbEncrypt](#DesOfbEncrypt)
@@ -73,7 +75,6 @@ import (
- [RsaSign](#RsaSign)
- [RsaVerifySign](#RsaVerifySign)
<div STYLE="page-break-after: always;"></div>
## 文档
@@ -218,6 +219,8 @@ func main() {
<p>使用AES CTR算法模式加密/解密数据,参数`key`的长度是16, 24 or 32。</p>
> ⚠️ 本函数已弃用,使用`AesCtrEncrypt`和`AesCtrDecrypt`代替。
<b>函数签名:</b>
```go
@@ -248,6 +251,74 @@ func main() {
}
```
### <span id="AesCtrEncrypt">AesCtrEncrypt</span>
<p>使用AES CTR算法模式加密数据参数`key`的长度是16, 24 or 32。</p>
<b>函数签名:</b>
```go
func AesCtrEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/x6pjPAvThRz)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesCtrDecrypt">AesCtrDecrypt</span>
<p>使用AES CTR算法模式解密数据参数`key`的长度是16, 24 or 32。</p>
<b>函数签名:</b>
```go
func AesCtrDecrypt(encrypted, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/x6pjPAvThRz)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesCfbEncrypt">AesCfbEncrypt</span>
<p>使用AES CFB算法模式加密数据参数`key`的长度是16, 24 or 32。</p>
@@ -648,10 +719,80 @@ func main() {
}
```
### <span id="DesCtrEncrypt">DesCtrEncrypt</span>
<p>使用DES CTR算法模式加密数据参数`key`的长度是8</p>
<b>函数签名:</b>
```go
func DesCtrEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/S6p_WHCgH1d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefgh"
encrypted := cryptor.DesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.DesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="DesCtrDecrypt">DesCtrDecrypt</span>
<p>使用DES CTR算法模式加密数据参数`key`的长度是8</p>
<b>函数签名:</b>
```go
func DesCtrDecrypt(encrypted, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/S6p_WHCgH1d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefgh"
encrypted := cryptor.DesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.DesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="DesCtrCrypt">DesCtrCrypt</span>
<p>使用DES CTR算法模式加密/解密数据,参数`key`的长度是8</p>
> ⚠️ 本函数已弃用,使用`DesCtrEncrypt`和`DesCtrDecrypt`代替。
<b>函数签名:</b>
```go
@@ -1436,7 +1577,7 @@ func main() {
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
package main
@@ -1473,7 +1614,7 @@ func main() {
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
package main
@@ -1621,7 +1762,7 @@ func main() {
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
package main
@@ -1638,12 +1779,12 @@ func main() {
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
signature, err := cryptor.RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
err = cryptor.RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
@@ -1660,7 +1801,7 @@ func main() {
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
package main
@@ -1677,12 +1818,12 @@ func main() {
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
signature, err := cryptor.RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
err = cryptor.RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}

View File

@@ -1092,7 +1092,7 @@ func main() {
### 4. PriorityQueue
切片实现的优先级队列。
切片实现的优先级队列。
### <span id="NewPriorityQueue">NewPriorityQueue</span>
<p>返回一个具有特定容量的PriorityQueue指针参数 `comarator` 用于比较队列中T类型的值。</p>

View File

@@ -23,9 +23,14 @@ import (
## 目录
- [AddDay](#AddDay)
- [AddWeek](#AddWeek)
- [AddMonth](#AddMonth)
- [AddHour](#AddHour)
- [AddMinute](#AddMinute)
- [AddYear](#AddYear)
- [AddDaySafe](#AddDaySafe)
- [AddMonthSafe](#AddMonthSafe)
- [AddYearSafe](#AddYearSafe)
- [BeginOfMinute](#BeginOfMinute)
- [BeginOfHour](#BeginOfHour)
- [BeginOfDay](#BeginOfDay)
@@ -109,7 +114,7 @@ import (
<b>函数签名:</b>
```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>
@@ -124,20 +129,89 @@ import (
)
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)
diff1 := tomorrow.Sub(now)
after1Day := datetime.AddDay(date, 1)
before1Day := datetime.AddDay(date, -1)
yesterday := datetime.AddDay(now, -1)
diff2 := yesterday.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
// Output:
// 24h0m0s
// -24h0m0s
// 2021-01-02 00:00:00
// 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>
```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>
@@ -163,20 +237,17 @@ import (
)
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)
diff1 := after2Hours.Sub(now)
after2Hours := datetime.AddHour(date, 2)
before2Hours := datetime.AddHour(date, -2)
before2Hours := datetime.AddHour(now, -2)
diff2 := before2Hours.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
// Output:
// 2h0m0s
// -2h0m0s
// 2021-01-01 02:00:00
// 2020-12-31 22:00:00
}
```
@@ -187,7 +258,7 @@ func main() {
<b>函数签名:</b>
```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>
@@ -202,20 +273,17 @@ import (
)
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)
diff1 := after2Minutes.Sub(now)
after2Minutes := datetime.AddMinute(date, 2)
before2Minutes := datetime.AddMinute(date, -2)
before2Minutes := datetime.AddMinute(now, -2)
diff2 := before2Minutes.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
// Output:
// 2m0s
// -2m0s
// 2021-01-01 00:02:00
// 2020-12-31 23:58:00
}
```
@@ -226,7 +294,7 @@ func main() {
<b>函数签名:</b>
```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>
@@ -241,20 +309,137 @@ import (
)
func main() {
now := time.Now()
date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := datetime.AddYear(now, 1)
diff1 := after1Year.Sub(now)
after2Years := AddYear(date, 2)
before2Years := AddYear(date, -2)
before1Year := datetime.AddYear(now, -1)
diff2 := before1Year.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Years.Format("2006-01-02"))
fmt.Println(before2Years.Format("2006-01-02"))
// Output:
// 8760h0m0s
// -8760h0m0s
// 2023-01-01
// 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
}
```
@@ -361,7 +546,7 @@ func main() {
<b>函数签名:</b>
```go
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
func BeginOfWeek(t time.Time, beginFrom time.Weekday) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ynjoJPz7VNV)</span></b>
@@ -377,12 +562,12 @@ import (
func main() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := datetime.BeginOfWeek(input)
result := datetime.BeginOfWeek(input, time.Monday)
fmt.Println(result)
// Output:
// 2023-01-08 00:00:00 +0000 UTC
// 2023-01-09 00:00:00 +0000 UTC
}
```
@@ -542,7 +727,7 @@ func main() {
fmt.Println(result)
// Output:
// 2023-01-08 23:59:59.999999999 +0000 UTC
// 2023-01-02 00:00:00 +0000 UTC
}
```
@@ -553,10 +738,10 @@ func main() {
<b>函数签名:</b>
```go
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
func EndOfWeek(t time.Time, endWith time.Weekday) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/i08qKXD9flf)</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mGSA162YgX9)</span></b>
```go
package main
@@ -569,12 +754,12 @@ import (
func main() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := datetime.EndOfWeek(input)
result := datetime.EndOfWeek(input, time.Sunday)
fmt.Println(result)
// Output:
// 2023-01-14 23:59:59.999999999 +0000 UTC
// 2023-01-08 23:59:59.999999999 +0000 UTC
}
```
@@ -1585,7 +1770,7 @@ func main() {
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
package main
@@ -1615,7 +1800,7 @@ func main() {
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
package main
@@ -1645,7 +1830,7 @@ func main() {
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
package main

850
docs/api/packages/enum.md Normal file
View File

@@ -0,0 +1,850 @@
# Enum
Enum 实现一个简单枚举工具包。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/enum/enum.go](https://github.com/duke-git/lancet/blob/main/enum/enum.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/enum"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [NewItem](#NewItem)
- [NewItemsFromPairs](#NewItemsFromPairs)
- [Value](#Value)
- [Name](#Name)
- [Valid](#Valid)
- [MarshalJSON](#MarshalJSON)
- [NewRegistry](#NewRegistry)
- [Add](#Add)
- [Remove](#Remove)
- [Update](#Update)
- [GetByValue](#GetByValue)
- [GetByName](#GetByName)
- [Items](#Items)
- [Contains](#Contains)
- [Size](#Size)
- [Range](#Range)
- [SortedItems](#SortedItems)
- [Filter](#Filter)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="NewItem">NewItem</span>
<p>创建枚举项。</p>
<b>函数签名:</b>
```go
func NewItem[T comparable](value T, name string) *Item[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8qNsLw01HD5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
fmt.Println(item1.Name(), item1.Value())
fmt.Println(item2.Name(), item2.Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="NewItemsFromPairs">NewItemsFromPairs</span>
<p>从Pair结构体的切片创建枚举项。</p>
<b>函数签名:</b>
```go
func NewItemsFromPairs[T comparable](pairs ...Pair[T]) []*Item[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Value">Value</span>
<p>返回枚举项的值。</p>
<b>函数签名:</b>
```go
func (it *Item[T]) Value() T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Name">Name</span>
<p>返回枚举项的名称。</p>
<b>函数签名:</b>
```go
func (it *Item[T]) Name() string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Valid">Valid</span>
<p>检查枚举项是否有效。如果提供了自定义检查函数,将使用该函数验证值。</p>
<b>函数签名:</b>
```go
func (it *Item[T]) Valid(checker ...func(T) bool) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/pA3lYY2VSm3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item := enum.NewItem(Active, "Active")
fmt.Println(item.Valid())
invalidItem := enum.NewItem(Unknown, "")
fmt.Println(invalidItem.Valid())
// Output:
// true
// false
}
```
### <span id="MarshalJSON">MarshalJSON</span>
<p>枚举项实现json.Marshaler接口。</p>
<b>函数签名:</b>
```go
func (it *Item[T]) MarshalJSON() ([]byte, error)
func (it *Item[T]) UnmarshalJSON(data []byte) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zIZEdAnneB5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item := enum.NewItem(Active, "Active")
data, _ := item.MarshalJSON()
fmt.Println(string(data))
var unmarshaledItem enum.Item[Status]
_ = unmarshaledItem.UnmarshalJSON(data)
fmt.Println(unmarshaledItem.Name(), unmarshaledItem.Value())
// Output:
// {"name":"Active","value":1}
// Active 1
}
```
### <span id="NewRegistry">NewRegistry</span>
<p>Registry 定义了一个通用的枚举注册表结构体。</p>
<b>函数签名:</b>
```go
func NewRegistry[T comparable](items ...*Item[T]) *Registry[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ABEXsYfJKMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found by value:", item.Name())
}
if item, found := registry.GetByName("Inactive"); found {
fmt.Println("Found by name:", item.Value())
}
// Output:
// Found by value: Active
// Found by name: 2
}
```
### <span id="Add">Add</span>
<p>向枚举注册表添加枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Add(items ...*Item[T])
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ABEXsYfJKMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found by value:", item.Name())
}
if item, found := registry.GetByName("Inactive"); found {
fmt.Println("Found by name:", item.Value())
}
// Output:
// Found by value: Active
// Found by name: 2
}
```
### <span id="Remove">Remove</span>
<p>在枚举注册表中删除枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Remove(value T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dSG84wQ3TuC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
registry.Add(item1)
fmt.Println("Size before removal:", registry.Size())
removed := registry.Remove(Active)
fmt.Println("Removed:", removed)
fmt.Println("Size after removal:", registry.Size())
// Output:
// Size before removal: 1
// Removed: true
// Size after removal: 0
}
```
### <span id="Update">Update</span>
<p>在枚举注册表中更新枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Update(value T, newName string) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Ol0moT1J9Xl)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
registry.Add(item1)
updated := registry.Update(Active, "Activated")
fmt.Println("Updated:", updated)
if item, found := registry.GetByValue(Active); found {
fmt.Println("New name:", item.Name())
}
// Output:
// Updated: true
// New name: Activated
}
```
### <span id="GetByValue">GetByValue</span>
<p>在枚举注册表中通过值获取枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) GetByValue(value T) (*Item[T], bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/niJ1U2KlE_m)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found name by value:", item.Name())
}
// Output:
// Found name by value: Active
}
```
### <span id="GetByName">GetByName</span>
<p>在枚举注册表中通过名称获取枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) GetByName(name string) (*Item[T], bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/49ie_gpqH0m)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
if item, found := registry.GetByName("Active"); found {
fmt.Println("Found value by name:", item.Value())
}
// Output:
// Found value by name: 1
}
```
### <span id="Items">Items</span>
<p>返回枚举注册表中的枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Items() []*Item[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/lAJFAradbvQ)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
for _, item := range registry.Items() {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Contains">Contains</span>
<p>检查注册表中是否存在具有给定值的枚举项。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Contains(value T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_T-lPYkZn2j)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
fmt.Println(registry.Contains(Active))
fmt.Println(registry.Contains(Inactive))
// Output:
// true
// false
}
```
### <span id="Size">Size</span>
<p>返回注册表中枚举项的数目。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Size() int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/TeDArWhlQe2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
fmt.Println("Initial size:", registry.Size())
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
fmt.Println("Size after adding items:", registry.Size())
registry.Remove(Active)
fmt.Println("Size after removing an item:", registry.Size())
// Output:
// Initial size: 0
// Size after adding items: 2
// Size after removing an item: 1
}
```
### <span id="Range">Range</span>
<p>遍历注册表中的所有枚举项,并应用给定的函数。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Range(fn func(*Item[T]) bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GPsZbQbefWN)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
registry.Range(func(item *enum.Item[Status]) bool {
fmt.Println(item.Name(), item.Value())
return true // continue iteration
})
// Output:
// Active 1
// Inactive 2
}
```
### <span id="SortedItems">SortedItems</span>
<p>返回按给定比较函数排序的所有枚举项的切片。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) SortedItems(less func(*Item[T], *Item[T]) bool) []*Item[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tN9RE_m_WEI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Inactive, "Inactive")
item2 := enum.NewItem(Active, "Active")
registry.Add(item1, item2)
for _, item := range registry.SortedItems(func(i1, i2 *enum.Item[Status]) bool {
return i1.Value() < i2.Value()
}) {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Filter">Filter</span>
<p>返回满足给定谓词函数的枚举项切片。</p>
<b>函数签名:</b>
```go
func (r *Registry[T]) Filter(predicate func(*Item[T]) bool) []*Item[T]
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uTUpTdcyoCU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
activeItems := registry.Filter(func(item *enum.Item[Status]) bool {
return item.Value() == Active
})
for _, item := range activeItems {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
}
```

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

@@ -35,6 +35,7 @@ import (
- [IsDir](#IsDir)
- [ListFileNames](#ListFileNames)
- [RemoveFile](#RemoveFile)
- [RemoveDir](#RemoveDir)
- [ReadFileToString](#ReadFileToString)
- [ReadFileByLine](#ReadFileByLine)
- [Zip](#Zip)
@@ -390,12 +391,12 @@ func main() {
### <span id="RemoveFile">RemoveFile</span>
<p>删除文件</p>
<p>删除文件,支持传入删除前的回调函数。</p>
<b>函数签名:</b>
```go
func RemoveFile(path string) error
func RemoveFile(path string, onDelete ...func(path string)) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/P2y0XW8a1SH)</span></b>
@@ -416,6 +417,41 @@ func main() {
}
```
### <span id="RemoveDir">RemoveDir</span>
<p>删除目录,支持传入删除前的回调函数。</p>
<b>函数签名:</b>
```go
func RemoveDir(path string, onDelete ...func(path string)) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Oa6KnPek2uy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
var deletedPaths []string
err := fileutil.RemoveDir("./tempdir", func(p string) {
deletedPaths = append(deletedPaths, p)
})
if err != nil {
fmt.Println(err)
}
fmt.Println(deletedPaths)
}
```
### <span id="ReadFileToString">ReadFileToString</span>
<p>读取文件内容并返回字符串</p>
@@ -934,7 +970,7 @@ func main() {
<b>函数签名:</b>
```go
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uNep3Tr8fqF)</span></b>
@@ -958,9 +994,9 @@ func main() {
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
@@ -989,7 +1025,7 @@ import (
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
@@ -1053,7 +1089,7 @@ func main() {
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
@@ -1078,6 +1114,7 @@ func main() {
// 2
}
```
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
<p>返回exe,dll文件版本号(仅Window平台).</p>
@@ -1088,7 +1125,7 @@ func main() {
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
package main
@@ -1107,4 +1144,4 @@ func main() {
// Output:
// 3.9.10.19
}
```
```

View File

@@ -78,7 +78,8 @@ import (
- [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
- [FindValuesBy](#FindValuesBy)
- [ToMarkdownTable](#ToMarkdownTable)
<div STYLE="page-break-after: always;"></div>
@@ -1095,7 +1096,7 @@ func main() {
<b>函数签名:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
@@ -1259,7 +1260,6 @@ func main() {
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>删除给定键的键值对。</p>
@@ -2192,7 +2192,6 @@ func main() {
}
```
### <span id="GetOrSet">GetOrSet</span>
<p>返回给定键的值,如果不存在则设置该值。</p>
@@ -2276,7 +2275,7 @@ func main() {
<b>函数签名:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/99QjSYSBdiM)</span></b>
@@ -2307,4 +2306,108 @@ func main() {
// a
// default
}
```
```
### <span id="FindValuesBy">FindValuesBy</span>
<p>返回一个切片包含满足给定谓词判断函数的map中的值。</p>
<b>函数签名:</b>
```go
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bvNwNBZDm6v)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result := maputil.FindValuesBy(m, func(k int, v string) bool {
return k%2 == 0
})
fmt.Println(result)
// Output:
// [b d]
}
```
### <span id="ToMarkdownTable">ToMarkdownTable</span>
<p>将一个 map 切片数据转换为 Markdown 表格字符串。支持自定义表头显示名称和列的显示顺序。</p>
<b>函数签名:</b>
```go
编辑
func ToMarkdownTable(data []map[string]interface{}, headerMap map[string]string, columnOrder []string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/w_pSLfeyEB5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
// 基本用法:自动从数据中提取列名作为表头
data := []map[string]interface{}{
{"name": "Alice", "age": 25, "salary": 50000},
{"name": "Bob", "age": 30, "salary": 60000},
}
result := maputil.ToMarkdownTable(data, nil, nil)
fmt.Println(result)
// 输出:
// |name|age|salary|
// |---|---|---|
// |Alice|25|50000|
// |Bob|30|60000|
// 自定义表头显示名称
headerMap := map[string]string{
"name": "姓名",
"age": "年龄",
"salary": "薪资",
}
result = maputil.ToMarkdownTable(data, headerMap, nil)
fmt.Println(result)
// 输出:
// |姓名|年龄|薪资|
// |---|---|---|
// |Alice|25|50000|
// |Bob|30|60000|
// 自定义列顺序
columnOrder := []string{"salary", "name"}
result = maputil.ToMarkdownTable(data, nil, columnOrder)
fmt.Println(result)
// 输出:
// |salary|name|
// |---|---|
// |50000|Alice|
// |60000|Bob|
}
```

View File

@@ -466,14 +466,14 @@ func main() {
}
```
### <span id="T运行cRound">T运行cRound</span>
### <span id="TruncRound">TruncRound</span>
<p>截短n位小数不进行四舍五入</p>
<b>函数签名:</b>
```go
func T运行cRound[T constraints.Float | constraints.Integer](x T, n int) T
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
@@ -487,9 +487,9 @@ import (
)
func main() {
result1 := mathutil.T运行cRound(0.124, 2)
result2 := mathutil.T运行cRound(0.125, 2)
result3 := mathutil.T运行cRound(0.125, 3)
result1 := mathutil.TruncRound(0.124, 2)
result2 := mathutil.TruncRound(0.125, 2)
result3 := mathutil.TruncRound(0.125, 3)
fmt.Println(result1)
fmt.Println(result2)
@@ -1178,7 +1178,7 @@ func main() {
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
package main
@@ -1211,7 +1211,7 @@ func main() {
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
package main
@@ -1222,8 +1222,8 @@ import (
)
func main() {
result1 := mathutil.T运行cRound(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)
result1 := mathutil.TruncRound(mathutil.StdDev([]int{1, 2, 3, 4, 5}), 2)
result2 := mathutil.TruncRound(mathutil.StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2)
fmt.Println(result1)
fmt.Println(result2)
@@ -1236,7 +1236,7 @@ func main() {
### <span id="Permutation">Permutation</span>
<p>计算排列数P(n, k)。</p>
<p>计算排列数P(n, k)。</p>
<b>函数签名:</b>
@@ -1244,7 +1244,7 @@ func main() {
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
package main
@@ -1269,7 +1269,7 @@ func main() {
### <span id="Combination">Combination</span>
<p>计算组合数C(n, k)。</p>
<p>计算组合数C(n, k)。</p>
<b>函数签名:</b>
@@ -1277,7 +1277,7 @@ func main() {
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
package main

View File

@@ -48,6 +48,8 @@ import (
- [UploadFile](#UploadFile)
- [IsPingConnected](#IsPingConnected)
- [IsTelnetConnected](#IsTelnetConnected)
- [BuildUrl](#BuildUrl)
- [AddQueryParams](#AddQueryParams)
<div STYLE="page-break-after: always;"></div>
@@ -1031,3 +1033,83 @@ func main() {
// false
}
```
### <span id="BuildUrl">BuildUrl</span>
<p>创建url字符串。</p>
<b>函数签名:</b>
```go
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JLXl1hZK7l4)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/netutil"
)
func main() {
urlStr, err := netutil.BuildUrl(
"https",
"example.com",
"query",
map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
},
)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com/query?a=foo&a=bar&b=baz
// <nil>
}
```
### <span id="AddQueryParams">AddQueryParams</span>
<p>向url添加查询参数。</p>
<b>函数签名:</b>
```go
func AddQueryParams(urlStr string, params map[string][]string) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JLXl1hZK7l4)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/netutil"
)
func main() {
urlStr, err := netutil.BuildUrl(
"https",
"example.com",
"query",
map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
},
)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com/query?a=foo&a=bar&b=baz
// <nil>
}
```

View File

@@ -526,7 +526,7 @@ func main() {
```
### <span id="RandNumberOfLength">RandNumberOfLength</span>
<p>生成指定长度的随机数</p>
<p>生成指定长度的随机数</p>
<b>函数签名:</b>
@@ -534,7 +534,7 @@ func main() {
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
package main

View File

@@ -27,6 +27,7 @@ import (
- [Contain](#Contain)
- [ContainBy](#ContainBy)
- [ContainSubSlice](#ContainSubSlice)
- [ContainAny](#ContainAny)
- [Chunk](#Chunk)
- [Compact](#Compact)
- [Concat](#Concat)
@@ -44,6 +45,7 @@ import (
- [Every](#Every)
- [Equal](#Equal)
- [EqualWith](#EqualWith)
- [EqualUnordered](#EqualUnordered)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
@@ -69,6 +71,7 @@ import (
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [ReverseCopy](#ReverseCopy)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
@@ -77,6 +80,7 @@ import (
- [ReplaceAll](#ReplaceAll)
- [Repeat](#Repeat)
- [Shuffle](#Shuffle)
- [ShuffleCopy](#ShuffleCopy)
- [IsAscending](#IsAscending)
- [IsDescending](#IsDescending)
- [IsSorted](#IsSorted)
@@ -253,6 +257,43 @@ func main() {
}
```
### <span id="ContainAny">ContainAny</span>
<p>判断slice是否包含targets切片中的任意一个元素</p>
<b>函数签名:</b>
```go
func ContainAny[T comparable](slice []T, targets []T) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4xoxhc9XSSw)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result1 := slice.ContainAny([]string{"a", "b", "c"}, []string{"a"})
result2 := slice.ContainAny([]string{"a", "b", "c"}, []string{"d", "e"})
result3 := slice.ContainAny([]string{"a", "b", "c"}, []string{"d", "a"})
result4 := slice.ContainAny([]string{"a", "b", "c"}, []string{})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// false
// true
// false
}
```
### <span id="Chunk">Chunk</span>
<p>按照size参数均分slice</p>
@@ -874,6 +915,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>
<p>返回切片中通过predicate函数真值测试的所有元素</p>
@@ -1728,6 +1800,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>
<p>将切片中的元素依次运行iteratee函数返回运行结果。</p>
@@ -1962,7 +2066,7 @@ func main() {
### <span id="Shuffle">Shuffle</span>
<p>随机打乱切片中的元素顺序</p>
<p>随机打乱切片中的元素顺序</p>
<b>函数签名:</b>
@@ -1989,6 +2093,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>
<p>检查切片元素是否按升序排列。</p>
@@ -2999,7 +3134,7 @@ func main() {
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
import (
@@ -3029,7 +3164,7 @@ func main() {
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
import (

View File

@@ -951,7 +951,7 @@ func main() {
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
import (
@@ -984,7 +984,7 @@ func main() {
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
import (

View File

@@ -31,6 +31,7 @@ import (
- [IsStruct](#IsStruct)
- [Tag](#Tag)
- [Name](#Name)
- [TypeName](#TypeName)
- [Value](#Value)
- [Kind](#Kind)
- [IsEmbedded](#IsEmbedded)
@@ -53,7 +54,7 @@ import (
func New(value any, tagName ...string) *Struct
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/O29l8kk-Z17)</span></b>
```go
package main
@@ -68,7 +69,11 @@ func main() {
}
p1 := &People{Name: "11"}
s := structs.New(p1)
// to do something
fmt.Println(s.ToMap())
// Output:
// map[name:11] <nil>
}
```
@@ -88,7 +93,7 @@ func (s *Struct) ToMap() (map[string]any, error)
func ToMap(v any) (map[string]any, error)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qQbLySBgerZ)</span></b>
```go
package main
@@ -129,7 +134,7 @@ func main() {
func (s *Struct) Fields() []*Field
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/w3Kk_CyVY7D)</span></b>
```go
package main
@@ -161,10 +166,10 @@ func main() {
<b>函数签名:</b>
```go
func (s *Struct) Field(name string) *Field
func (s *Struct) Field(name string) (*Field, bool)
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KocZMSYarza)</span></b>
```go
package main
@@ -180,12 +185,14 @@ func main() {
}
p1 := &People{Name: "11"}
s := structs.New(p1)
f := s.Field("Name")
f, found := s.Field("Name")
fmt.Println(f.Value())
fmt.Println(found)
// Output:
// 11
// true
}
```
@@ -199,7 +206,7 @@ func main() {
func (s *Struct) IsStruct() bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bU2FSdkbK1C)</span></b>
```go
package main
@@ -233,7 +240,7 @@ func main() {
func (f *Field) Tag() *Tag
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/DVrx5HvvUJr)</span></b>
```go
package main
@@ -254,7 +261,7 @@ func main() {
tag := n.Tag()
fmt.Println(tag.Name)
// Output:
// name
}
@@ -270,7 +277,7 @@ func main() {
func (f *Field) Value() any
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qufYEU2o4Oi)</span></b>
```go
package main
@@ -288,10 +295,10 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
fmt.Println(n.Value())
// Output:
// Output:
// 111
}
```
@@ -306,7 +313,7 @@ func main() {
func (f *Field) IsEmbedded() bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wV2PrbYm3Ec)</span></b>
```go
package main
@@ -331,11 +338,11 @@ func main() {
s := structs.New(c1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.IsEmbedded())
fmt.Println(a.IsEmbedded())
// Output:
// Output:
// true
// false
}
@@ -351,7 +358,7 @@ func main() {
func (f *Field) IsExported() bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/csK4AXYaNbJ)</span></b>
```go
package main
@@ -370,11 +377,11 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("age")
fmt.Println(n.IsExported())
fmt.Println(a.IsExported())
// Output:
// Output:
// true
// false
}
@@ -390,7 +397,7 @@ func main() {
func (f *Field) IsZero() bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/RzqpGISf87r)</span></b>
```go
package main
@@ -409,11 +416,11 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.IsZero())
fmt.Println(a.IsZero())
// Output:
// Output:
// true
// false
}
@@ -429,7 +436,7 @@ func main() {
func (f *Field) Name() string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zfIGlqsatee)</span></b>
```go
package main
@@ -448,11 +455,11 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.Name())
fmt.Println(a.Name())
// Output:
// Output:
// Name
// Age
}
@@ -468,7 +475,7 @@ func main() {
func (f *Field) Kind() reflect.Kind
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wg4NlcUNG5o)</span></b>
```go
package main
@@ -487,16 +494,52 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.Kind())
fmt.Println(a.Kind())
// Output:
// Output:
// string
// int
}
```
### <span id="TypeName">TypeName</span>
<p>获取结构体类型名。</p>
<b>函数签名:</b>
```go
func (s *Struct) TypeName() string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/SWLWd0XBaBb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
Age int
}
p := &Parent{Age: 11}
s := structs.New(p)
fmt.Println(s.TypeName())
// Output:
// Parent
}
```
### <span id="IsSlice">IsSlice</span>
<p>判断属性是否是切片</p>
@@ -507,7 +550,7 @@ func main() {
func (f *Field) IsSlice() bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/MKz4CgBIUrU)</span></b>
```go
package main
@@ -526,10 +569,10 @@ func main() {
p1 := &Parent{arr: []int{1, 2, 3}}
s := structs.New(p1)
a, _ := s.Field("arr")
fmt.Println(a.IsSlice())
// Output:
// Output:
// true
}
```
@@ -544,7 +587,7 @@ func main() {
func (f *Field) IsTargetType(targetType reflect.Kind) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Ig75P-agN39)</span></b>
```go
package main
@@ -565,12 +608,12 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("arr")
fmt.Println(n.IsTargetType(reflect.String))
fmt.Println(a.IsTargetType(reflect.Slice))
// Output:
// Output:
// true
// true
}
```
```

View File

@@ -69,6 +69,7 @@ import (
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#ExtractContent)
- [FindAllOccurrences](#FindAllOccurrences)
<div STYLE="page-break-after: always;"></div>
@@ -1708,7 +1709,7 @@ func main() {
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
import (
@@ -1741,7 +1742,7 @@ func main() {
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
import (
@@ -1759,4 +1760,32 @@ func main() {
// Output:
// [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)
- [IsIpV4](#IsIpV4)
- [IsIpV6](#IsIpV6)
- [IsIpPort](#IsIpPort)
- [IsStrongPassword](#IsStrongPassword)
- [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword)
@@ -64,6 +65,8 @@ import (
- [IsAmericanExpress](#IsAmericanExpress)
- [IsUnionPay](#IsUnionPay)
- [IsChinaUnionPay](#IsChinaUnionPay)
- [IsPassport](#IsPassport)
- [IsChineseHMPassport](#IsChineseHMPassport)
<div STYLE="page-break-after: always;"></div>
@@ -823,6 +826,43 @@ func main() {
}
```
### <span id="IsAlphaNumeric">IsAlphaNumeric</span>
<p>验证字符串是字母或数字。</p>
<b>函数签名:</b>
```go
func IsAlphaNumeric(s string) bool
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/RHeESLrLg9c)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsAlphaNumeric("ABC")
result2 := validator.IsAlphaNumeric("123")
result3 := validator.IsAlphaNumeric("abc123")
result4 := validator.IsAlphaNumeric("abc123@#$")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
```
### <span id="IsJSON">IsJSON</span>
<p>验证字符串是否是有效json。</p>
@@ -990,6 +1030,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>
<p>验证字符串是否是强密码:(alpha(lower+upper) + number + special chars(!@#$%^&*()?&gt&lt))。</p>
@@ -1475,6 +1552,8 @@ func IsChinaUnionPay(v string) bool
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/yafpdxLiymu)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
@@ -1492,3 +1571,82 @@ func main() {
// false
}
```
### <span id="IsPassport">IsPassport</span>
<p>判断护照(正则判断)。</p>
<b>函数签名:</b>
```go
func IsPassport(passport, country string) bool
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/dvOiV2BW7Aw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsPassport("P123456789", "CN")
result2 := validator.IsPassport("123456789", "US")
result3 := validator.IsPassport("AB1234567", "RU")
result4 := validator.IsPassport("123456789", "CN")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
```
### <span id="IsChineseHMPassport">IsChineseHMPassport</span>
<p>判断港澳台通行证(正则判断)。</p>
<b>函数签名:</b>
```go
func IsChineseHMPassport(hmPassport string) bool
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/xKG6spQTcY0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsChineseHMPassport("C12345678")
result2 := validator.IsChineseHMPassport("C00000000")
result3 := validator.IsChineseHMPassport("M12345678")
result4 := validator.IsChineseHMPassport("c12345678")
result5 := validator.IsChineseHMPassport("C1234567")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// true
// true
// true
// false
// false
}
```

View File

@@ -509,7 +509,7 @@ func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
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
package main
@@ -529,8 +529,6 @@ func main() {
return errors.New("error in try block")
}).Catch(func(ctx context.Context, err error) {
calledCatch = true
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
// fmt.Println(err.Error())
}).Finally(func(ctx context.Context) {
calledFinally = true
}).Do()

View File

@@ -6,7 +6,6 @@ outline: deep
<b>Lancet (Lancet) is a powerful, comprehensive, efficient and reusable go language tool function library. Contains 25 packages, more than 600 utility functions. Functions cover string processing, slice processing, network, concurrency, encryption and decryption, file processing, time/date, stream processing, iterators, and more.</b>
<style>
.package-title {
color: black;
@@ -27,7 +26,7 @@ outline: deep
display: inline-block;
vertical-align: middle;
line-height: 40px;
background: #059669;
background: #6cadf5;
border: 1px solid;
margin-right: 10px;
margin-bottom: 10px;
@@ -47,6 +46,8 @@ outline: deep
<div class="package-cell">cryptor</div>
<div class="package-cell">datastructure</div>
<div class="package-cell">datetime</div>
<div class="package-cell">enum</div>
<div class="package-cell">eventbus</div>
<div class="package-cell">fileutil</div>
<div class="package-cell">formatter</div>
<div class="package-cell">function</div>
@@ -67,4 +68,3 @@ outline: deep
<div class="package-cell">xerror</div>
</div>
</div>

View File

@@ -8,7 +8,7 @@ Package algorithm implements some basic algorithm. eg. sort, search.
- [https://github.com/duke-git/lancet/blob/main/algorithm/sort.go](https://github.com/duke-git/lancet/blob/main/algorithm/sort.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/search.go](https://github.com/duke-git/lancet/blob/main/algorithm/search.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go)
- [https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go)
<div STYLE="page-break-after: always;"></div>

View File

@@ -1,15 +1,18 @@
# Concurrency
Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
<div STYLE="page-break-after: always;"></div>
## Source:
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go)
<div STYLE="page-break-after: always;"></div>
## Usage:
```go
import (
"github.com/duke-git/lancet/v2/concurrency"
@@ -19,24 +22,39 @@ import (
<div STYLE="page-break-after: always;"></div>
## Index
### Channel
- [NewChannel](#NewChannel)
- [Bridge](#Bridge)
- [FanIn](#FanIn)
- [Generate](#Generate)
- [Or](#Or)
- [OrDone](#OrDone)
- [Repeat](#Repeat)
- [RepeatFn](#RepeatFn)
- [Take](#Take)
- [Tee](#Tee)
- [NewChannel](#NewChannel)
- [Bridge](#Bridge)
- [FanIn](#FanIn)
- [Generate](#Generate)
- [Or](#Or)
- [OrDone](#OrDone)
- [Repeat](#Repeat)
- [RepeatFn](#RepeatFn)
- [Take](#Take)
- [Tee](#Tee)
### KeyedLocker
- [NewKeyedLocker](#NewKeyedLocker)
- [Do](#Do)
- [NewRWKeyedLocker](#NewRWKeyedLocker)
- [RLock](#RLock)
- [Lock](#Lock)
- [NewTryKeyedLocker](#NewTryKeyedLocker)
- [TryLock](#TryLock)
- [Unlock](#Unlock)
<div STYLE="page-break-after: always;"></div>
## Documentation
## Channel
### <span id="NewChannel">NewChannel</span>
<p>Create a Channel pointer instance.</p>
<b>Signature:</b>
@@ -45,6 +63,7 @@ import (
type Channel[T any] struct
func NewChannel[T any]() *Channel[T]
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
```go
@@ -69,6 +88,7 @@ func main() {
```go
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qmWSy1NVF-Y)</span></b>
```go
@@ -121,6 +141,7 @@ func main() {
```go
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2VYFMexEvTm)</span></b>
```go
@@ -160,6 +181,7 @@ func main() {
```go
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
```
<b>Example:</b>
```go
@@ -199,6 +221,7 @@ func main() {
```go
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
```go
@@ -237,6 +260,7 @@ func main() {
```go
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
```
<b>Example:</b>
```go
@@ -279,6 +303,7 @@ func main() {
```go
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
```
<b>Example:</b>
```go
@@ -322,6 +347,7 @@ func main() {
```go
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
```
<b>Example:</b>
```go
@@ -360,6 +386,7 @@ func main() {
```go
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
```
<b>Example:</b>
```go
@@ -406,6 +433,7 @@ func main() {
```go
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
```
<b>Example:</b>
```go
@@ -430,11 +458,397 @@ func main() {
fmt.Println(v)
fmt.Println(<-ch2)
}
// Output:
// 1
// 1
// 1
// 1
}
```
```
### KeyedLocker
### <span id="NewKeyedLocker">NewKeyedLocker</span>
<p>KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.</p>
<b>Signature:</b>
```go
func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GzeyC33T5rw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
task := func() {
fmt.Println("Executing task...")
time.Sleep(1 * time.Second)
fmt.Println("Task completed.")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := locker.Do(ctx, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel2()
if err := locker.Do(ctx2, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
// Output:
// Executing task...
// Task completed.
// Task successfully executed.
// Executing task...
// Task completed.
// Task successfully executed.
}
```
### <span id="Do">Do</span>
<p>Acquires a lock for the specified key and executes the provided function.</p>
<b>Signature:</b>
```go
func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GzeyC33T5rw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewKeyedLocker[string](2 * time.Second)
task := func() {
fmt.Println("Executing task...")
time.Sleep(1 * time.Second)
fmt.Println("Task completed.")
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := locker.Do(ctx, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel2()
if err := locker.Do(ctx2, "mykey", task); err != nil {
log.Fatalf("Error executing task: %v\n", err)
} else {
fmt.Println("Task successfully executed.")
}
// Output:
// Executing task...
// Task completed.
// Task successfully executed.
// Executing task...
// Task completed.
// Task successfully executed.
}
```
### <span id="NewRWKeyedLocker">NewRWKeyedLocker</span>
<p>RWKeyedLocker is a read-write version of KeyedLocker.</p>
<b>Signature:</b>
```go
func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZrCr8sMo77T)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.Lock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="RLock">RLock</span>
<p>Acquires a read lock for the specified key and executes the provided function.</p>
<b>Signature:</b>
```go
func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZrCr8sMo77T)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.RLock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="Lock">Lock</span>
<p>Acquires a write lock for the specified key and executes the provided function.</p>
<b>Signature:</b>
```go
func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WgAcXbOPKGk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := NewRWKeyedLocker[string](2 * time.Second)
// Simulate a key
key := "resource_key"
fn := func() {
fmt.Println("Starting write operation...")
// Simulate write operation, assuming it takes 2 seconds
time.Sleep(200 * time.Millisecond)
fmt.Println("Write operation completed!")
}
// Acquire the write lock and execute the operation
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// Execute the lock operation with a 3-second timeout
err := locker.Lock(ctx, key, fn)
if err != nil {
return
}
//output:
//Starting write operation...
//Write operation completed!
}
```
### <span id="NewTryKeyedLocker">NewTryKeyedLocker</span>
<p>TryKeyedLocker is a non-blocking version of KeyedLocker.</p>
<b>Signature:</b>
```go
func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```
### <span id="TryLock">TryLock</span>
<p>TryLock tries to acquire a lock for the specified key.</p>
<b>Signature:</b>
```go
func (l *TryKeyedLocker[K]) TryLock(key K) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```
### <span id="Unlock">Unlock</span>
<p>Unlock releases the lock for the specified key.</p>
<b>Signature:</b>
```go
func (l *TryKeyedLocker[K]) Unlock(key K)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/VG9qLvyetE2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
locker := concurrency.NewTryKeyedLocker[string]()
key := "resource_key"
if locker.TryLock(key) {
fmt.Println("Lock acquired")
time.Sleep(1 * time.Second)
// Unlock after work is done
locker.Unlock(key)
fmt.Println("Lock released")
} else {
fmt.Println("Lock failed")
}
//output:
//Lock acquired
//Lock released
}
```

View File

@@ -33,6 +33,9 @@ import (
- [ToJson](#ToJson)
- [ToMap](#ToMap)
- [ToPointer](#ToPointer)
- [ToPointers](#ToPointers)
- [FromPointer](#FromPointer)
- [FromPointers](#FromPointers)
- [ToString](#ToString)
- [StructToMap](#StructToMap)
- [MapToSlice](#MapToSlice)
@@ -47,6 +50,7 @@ import (
- [ToUrlBase64](#ToUrlBase64)
- [ToRawStdBase64](#ToRawStdBase64)
- [ToRawUrlBase64](#ToRawUrlBase64)
- [ToBigInt](#ToBigInt)
<div STYLE="page-break-after: always;"></div>
@@ -372,7 +376,7 @@ import (
func main() {
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
result, err := ToJson(aMap)
result, err := convertor.ToJson(aMap)
if err != nil {
fmt.Printf("%v", err)
@@ -455,6 +459,108 @@ func main() {
}
```
### <span id="ToPointers">ToPointers</span>
<p>Convert a slice of values to a slice of pointers.</p>
<b>Signature:</b>
```go
func ToPointers[T any](values []T) []*T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZUoXd2i5ZkV)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
strs := []string{"a", "b", "c"}
pointerStrs := convertor.ToPointers(strs)
fmt.Println(*pointerStrs[0])
fmt.Println(*pointerStrs[1])
fmt.Println(*pointerStrs[2])
// Output:
// a
// b
// c
}
```
### <span id="FromPointer">FromPointer</span>
<p>Returns the value pointed to by the pointer.</p>
<b>Signature:</b>
```go
func FromPointer[T any](ptr *T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wAp90V7Zu6g)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
str := "abc"
strPtr := &str
result := convertor.FromPointer(strPtr)
fmt.Println(result)
// Output:
// abc
}
```
### <span id="FromPointers">FromPointers</span>
<p>Convert a slice of pointers to a slice of values.</p>
<b>Signature:</b>
```go
func FromPointers[T any](pointers []*T) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qIPsyYtNy3Q)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
strs := []string{"a", "b", "c"}
strPtr := []*string{&strs[0], &strs[1], &strs[2]}
result := convertor.FromPointers(strPtr)
fmt.Println(result[0])
fmt.Println(result[1])
fmt.Println(result[2])
// Output:
// a
// b
// c
}
```
### <span id="ToString">ToString</span>
<p>ToString convert value to string, for number, string, []byte, will convert to string. For other type (slice, map, array, struct) will call json.Marshal</p>
@@ -571,7 +677,6 @@ func main() {
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>Encode data to byte slice.</p>
@@ -637,7 +742,6 @@ func main() {
}
```
### <span id="CopyProperties">CopyProperties</span>
<p>Copies each field from the source struct into the destination struct. Use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.</p>
@@ -747,7 +851,7 @@ func main() {
// Output:
// true
// true
// true
}
```
@@ -780,7 +884,7 @@ func main() {
// Output:
// true
// hello
// hello
}
```
@@ -856,8 +960,6 @@ func main() {
```
### <span id="ToUrlBase64">ToUrlBase64</span>
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
@@ -1116,4 +1218,34 @@ func main() {
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```
```
### <span id="ToBigInt">ToBigInt</span>
<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](https://go.dev/play/p/X3itkCxwB_x)</span></b>
```go
func ToBigInt[T any](v T) (*big.Int, error)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
n := 9876543210
bigInt, _ := convertor.ToBigInt(n)
fmt.Println(bigInt)
// Output:
// 9876543210
}
```

View File

@@ -27,7 +27,9 @@ import (
- [AesEcbDecrypt](#AesEcbDecrypt)
- [AesCbcEncrypt](#AesCbcEncrypt)
- [AesCbcDecrypt](#AesCbcDecrypt)
- [AesCtrCrypt](#AesCtrCrypt)
- [AesCtrCrypt<sup>deprecated</sup>](#AesCtrCrypt)
- [AesCtrEncrypt](#AesCtrEncrypt)
- [AesCtrDecrypt](#AesCtrDecrypt)
- [AesCfbEncrypt](#AesCfbEncrypt)
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
@@ -40,7 +42,9 @@ import (
- [DesEcbDecrypt](#DesEcbDecrypt)
- [DesCbcEncrypt](#DesCbcEncrypt)
- [DesCbcDecrypt](#DesCbcDecrypt)
- [DesCtrCrypt](#DesCtrCrypt)
- [DesCtrCrypt<sup>deprecated</sup>](#DesCtrCrypt)
- [DesCfbEncrypt](#DesCfbEncrypt)
- [DesCfbDecrypt](#DesCfbDecrypt)
- [DesCfbEncrypt](#DesCfbEncrypt)
- [DesCfbDecrypt](#DesCfbDecrypt)
- [DesOfbEncrypt](#DesOfbEncrypt)
@@ -217,6 +221,8 @@ func main() {
<p>Encrypt or decrypt data with key use AES CTR algorithm. Length of `key` param should be 16, 24 or 32.</p>
> ⚠️ This function is deprecated. use `AesCtrEncrypt` and `AesCtrDecrypt` instead.
<b>Signature:</b>
```go
@@ -247,6 +253,74 @@ func main() {
}
```
### <span id="AesCtrEncrypt">AesCtrEncrypt</span>
<p>Encrypt data with key use AES CTR algorithm</p>
<b>Signature:</b>
```go
func AesCtrEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/x6pjPAvThRz)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesCtrDecrypt">AesCtrDecrypt</span>
<p>Decrypt data with key use AES CTR algorithm</p>
<b>Signature:</b>
```go
func AesCtrDecrypt(encrypted, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/x6pjPAvThRz)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesCfbEncrypt">AesCfbEncrypt</span>
<p>Encrypt data with key use AES CFB algorithm. Length of `key` param should be 16, 24 or 32.</p>
@@ -651,6 +725,8 @@ func main() {
<p>Encrypt or decrypt data with key use DES CTR algorithm. Length of `key` param should be 8.</p>
> ⚠️ This function is deprecated. use `DesCtrEncrypt` and `DesCtrDecrypt` instead.
<b>Signature:</b>
```go
@@ -681,6 +757,74 @@ func main() {
}
```
### <span id="DesCtrEncrypt">DesCtrCrypt</span>
<p>Encrypt data with key use DES CTR algorithm. Length of `key` param should be 8.</p>
<b>Signature:</b>
```go
func DesCtrEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/S6p_WHCgH1d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefgh"
encrypted := cryptor.DesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.DesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="DesCtrDecrypt">DesCtrDecrypt</span>
<p>Decrypt data with key use DES CTR algorithm. Length of `key` param should be 8.</p>
<b>Signature:</b>
```go
func DesCtrDecrypt(encrypted, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/S6p_WHCgH1d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefgh"
encrypted := cryptor.DesCtrEncrypt([]byte(data), []byte(key))
decrypted := cryptor.DesCtrDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="DesCfbEncrypt">DesCfbEncrypt</span>
<p>Encrypt data with key use DES CFB algorithm. Length of `key` param should be 8.</p>
@@ -1435,7 +1579,7 @@ func main() {
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
package main
@@ -1472,7 +1616,7 @@ func main() {
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
package main
@@ -1620,7 +1764,7 @@ func main() {
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
package main
@@ -1659,7 +1803,7 @@ func main() {
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
package main

View File

@@ -24,9 +24,14 @@ import (
## Index
- [AddDay](#AddDay)
- [AddWeek](#AddWeek)
- [AddMonth](#AddMonth)
- [AddHour](#AddHour)
- [AddMinute](#AddMinute)
- [AddYear](#AddYear)
- [AddDaySafe](#AddDaySafe)
- [AddMonthSafe](#AddMonthSafe)
- [AddYearSafe](#AddYearSafe)
- [BeginOfMinute](#BeginOfMinute)
- [BeginOfHour](#BeginOfHour)
- [BeginOfDay](#BeginOfDay)
@@ -111,7 +116,7 @@ import (
<b>Signature:</b>
```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>
@@ -126,20 +131,89 @@ import (
)
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)
diff1 := tomorrow.Sub(now)
after1Day := datetime.AddDay(date, 1)
before1Day := datetime.AddDay(date, -1)
yesterday := datetime.AddDay(now, -1)
diff2 := yesterday.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after1Day.Format("2006-01-02 15:04:05"))
fmt.Println(before1Day.Format("2006-01-02 15:04:05"))
// Output:
// 24h0m0s
// -24h0m0s
// 2021-01-02 00:00:00
// 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() {
now := time.Now()
date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00")
after2Hours := datetime.AddHour(now, 2)
diff1 := after2Hours.Sub(now)
after2Hours := datetime.AddHour(date, 2)
before2Hours := datetime.AddHour(date, -2)
before2Hours := datetime.AddHour(now, -2)
diff2 := before2Hours.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Hours.Format("2006-01-02 15:04:05"))
fmt.Println(before2Hours.Format("2006-01-02 15:04:05"))
// Output:
// 2h0m0s
// -2h0m0s
// 2021-01-01 02:00:00
// 2020-12-31 22:00:00
}
```
@@ -204,20 +275,17 @@ import (
)
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)
diff1 := after2Minutes.Sub(now)
after2Minutes := datetime.AddMinute(date, 2)
before2Minutes := datetime.AddMinute(date, -2)
before2Minutes := datetime.AddMinute(now, -2)
diff2 := before2Minutes.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Minutes.Format("2006-01-02 15:04:05"))
fmt.Println(before2Minutes.Format("2006-01-02 15:04:05"))
// Output:
// 2m0s
// -2m0s
// 2021-01-01 00:02:00
// 2020-12-31 23:58:00
}
```
@@ -243,20 +311,137 @@ import (
)
func main() {
now := time.Now()
date, _ := time.Parse("2006-01-02", "2021-01-01")
after1Year := datetime.AddYear(now, 1)
diff1 := after1Year.Sub(now)
after2Years := AddYear(date, 2)
before2Years := AddYear(date, -2)
before1Year := datetime.AddYear(now, -1)
diff2 := before1Year.Sub(now)
fmt.Println(diff1)
fmt.Println(diff2)
fmt.Println(after2Years.Format("2006-01-02"))
fmt.Println(before2Years.Format("2006-01-02"))
// Output:
// 8760h0m0s
// -8760h0m0s
// 2023-01-01
// 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
}
```
@@ -366,7 +551,7 @@ func main() {
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ynjoJPz7VNV)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/DCHdcL6gnfV)</span></b>
```go
package main
@@ -379,12 +564,13 @@ import (
func main() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := datetime.BeginOfWeek(input)
result := datetime.BeginOfWeek(input, time.Monday)
fmt.Println(result)
// Output:
// 2023-01-08 00:00:00 +0000 UTC
// 2023-01-02 00:00:00 +0000 UTC
}
```
@@ -558,7 +744,7 @@ func main() {
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/i08qKXD9flf)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mGSA162YgX9)</span></b>
```go
package main
@@ -571,12 +757,12 @@ import (
func main() {
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
result := datetime.EndOfWeek(input)
result := datetime.EndOfWeek(input, time.Sunday)
fmt.Println(result)
// Output:
// 2023-01-14 23:59:59.999999999 +0000 UTC
// 2023-01-08 23:59:59.999999999 +0000 UTC
}
```
@@ -1342,7 +1528,7 @@ import (
func main() {
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(result2)
@@ -1586,7 +1772,7 @@ func main() {
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
package main
@@ -1616,7 +1802,7 @@ func main() {
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
package main
@@ -1646,7 +1832,7 @@ func main() {
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
package main

View File

@@ -0,0 +1,850 @@
# Enum
Package enum provides a simple enum implementation.
<div STYLE="page-break-after: always;"></div>
## Source:
- [https://github.com/duke-git/lancet/blob/main/enum/enum.go](https://github.com/duke-git/lancet/blob/main/enum/enum.go)
<div STYLE="page-break-after: always;"></div>
## Usage:
```go
import (
"github.com/duke-git/lancet/v2/enum"
)
```
<div STYLE="page-break-after: always;"></div>
## Index
- [NewItem](#NewItem)
- [NewItemsFromPairs](#NewItemsFromPairs)
- [Value](#Value)
- [Name](#Name)
- [Valid](#Valid)
- [MarshalJSON](#MarshalJSON)
- [NewRegistry](#NewRegistry)
- [Add](#Add)
- [Remove](#Remove)
- [Update](#Update)
- [GetByValue](#GetByValue)
- [GetByName](#GetByName)
- [Items](#Items)
- [Contains](#Contains)
- [Size](#Size)
- [Range](#Range)
- [SortedItems](#SortedItems)
- [Filter](#Filter)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="NewItem">NewItem</span>
<p>Creates a new enum item.</p>
<b>Signature:</b>
```go
func NewItem[T comparable](value T, name string) *Item[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8qNsLw01HD5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
fmt.Println(item1.Name(), item1.Value())
fmt.Println(item2.Name(), item2.Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="NewItemsFromPairs">NewItemsFromPairs</span>
<p>Creates enum items from a slice of Pair structs.</p>
<b>Signature:</b>
```go
func NewItemsFromPairs[T comparable](pairs ...Pair[T]) []*Item[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Value">Value</span>
<p>Returns the value of the enum item.</p>
<b>Signature:</b>
```go
func (it *Item[T]) Value() T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Name">Name</span>
<p>Returns the name of the enum item.</p>
<b>Signature:</b>
```go
func (it *Item[T]) Name() string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xKnoGa7gnev)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
items := enum.NewItemsFromPairs(
enum.Pair[Status]{Value: Active, Name: "Active"},
enum.Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Valid">Valid</span>
<p>Checks if the enum item is valid. If a custom check function is provided, it will be used to validate the value.</p>
<b>Signature:</b>
```go
func (it *Item[T]) Valid(checker ...func(T) bool) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/pA3lYY2VSm3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item := enum.NewItem(Active, "Active")
fmt.Println(item.Valid())
invalidItem := enum.NewItem(Unknown, "")
fmt.Println(invalidItem.Valid())
// Output:
// true
// false
}
```
### <span id="MarshalJSON">MarshalJSON</span>
<p>Implementation of json.Marshaler interface.</p>
<b>Signature:</b>
```go
func (it *Item[T]) MarshalJSON() ([]byte, error)
func (it *Item[T]) UnmarshalJSON(data []byte) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zIZEdAnneB5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
item := enum.NewItem(Active, "Active")
data, _ := item.MarshalJSON()
fmt.Println(string(data))
var unmarshaledItem enum.Item[Status]
_ = unmarshaledItem.UnmarshalJSON(data)
fmt.Println(unmarshaledItem.Name(), unmarshaledItem.Value())
// Output:
// {"name":"Active","value":1}
// Active 1
}
```
### <span id="NewRegistry">NewRegistry</span>
<p>Creates a new enum registry.</p>
<b>Signature:</b>
```go
func NewRegistry[T comparable](items ...*Item[T]) *Registry[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ABEXsYfJKMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found by value:", item.Name())
}
if item, found := registry.GetByName("Inactive"); found {
fmt.Println("Found by name:", item.Value())
}
// Output:
// Found by value: Active
// Found by name: 2
}
```
### <span id="Add">Add</span>
<p>Adds enum items to the registry.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Add(items ...*Item[T])
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ABEXsYfJKMo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found by value:", item.Name())
}
if item, found := registry.GetByName("Inactive"); found {
fmt.Println("Found by name:", item.Value())
}
// Output:
// Found by value: Active
// Found by name: 2
}
```
### <span id="Remove">Remove</span>
<p>Removes an enum item from the registry by its value.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Remove(value T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dSG84wQ3TuC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
registry.Add(item1)
fmt.Println("Size before removal:", registry.Size())
removed := registry.Remove(Active)
fmt.Println("Removed:", removed)
fmt.Println("Size after removal:", registry.Size())
// Output:
// Size before removal: 1
// Removed: true
// Size after removal: 0
}
```
### <span id="Update">Update</span>
<p>Updates the name of an enum item in the registry by its value.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Update(value T, newName string) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Ol0moT1J9Xl)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
registry.Add(item1)
updated := registry.Update(Active, "Activated")
fmt.Println("Updated:", updated)
if item, found := registry.GetByValue(Active); found {
fmt.Println("New name:", item.Name())
}
// Output:
// Updated: true
// New name: Activated
}
```
### <span id="GetByValue">GetByValue</span>
<p>Retrieves an enum item by its value.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) GetByValue(value T) (*Item[T], bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/niJ1U2KlE_m)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found name by value:", item.Name())
}
// Output:
// Found name by value: Active
}
```
### <span id="GetByName">GetByName</span>
<p>Retrieves an enum item by its name.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) GetByName(name string) (*Item[T], bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/49ie_gpqH0m)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
if item, found := registry.GetByName("Active"); found {
fmt.Println("Found value by name:", item.Value())
}
// Output:
// Found value by name: 1
}
```
### <span id="Items">Items</span>
<p>Returns a slice of all enum items in the registry.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Items() []*Item[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/lAJFAradbvQ)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
for _, item := range registry.Items() {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Contains">Contains</span>
<p>Checks if an enum item with the given value exists in the registry.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Contains(value T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_T-lPYkZn2j)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item := enum.NewItem(Active, "Active")
registry.Add(item)
fmt.Println(registry.Contains(Active))
fmt.Println(registry.Contains(Inactive))
// Output:
// true
// false
}
```
### <span id="Size">Size</span>
<p>Returns the number of enum items in the registry.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Size() int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/TeDArWhlQe2)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
fmt.Println("Initial size:", registry.Size())
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
fmt.Println("Size after adding items:", registry.Size())
registry.Remove(Active)
fmt.Println("Size after removing an item:", registry.Size())
// Output:
// Initial size: 0
// Size after adding items: 2
// Size after removing an item: 1
}
```
### <span id="Range">Range</span>
<p>Iterates over all enum items in the registry and applies the given function.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Range(fn func(*Item[T]) bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GPsZbQbefWN)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
registry.Range(func(item *enum.Item[Status]) bool {
fmt.Println(item.Name(), item.Value())
return true // continue iteration
})
// Output:
// Active 1
// Inactive 2
}
```
### <span id="SortedItems">SortedItems</span>
<p>Returns a slice of all enum items sorted by the given less function.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) SortedItems(less func(*Item[T], *Item[T]) bool) []*Item[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/tN9RE_m_WEI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Inactive, "Inactive")
item2 := enum.NewItem(Active, "Active")
registry.Add(item1, item2)
for _, item := range registry.SortedItems(func(i1, i2 *enum.Item[Status]) bool {
return i1.Value() < i2.Value()
}) {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
```
### <span id="Filter">Filter</span>
<p>Returns a slice of enum items that satisfy the given predicate function.</p>
<b>Signature:</b>
```go
func (r *Registry[T]) Filter(predicate func(*Item[T]) bool) []*Item[T]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uTUpTdcyoCU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/enum"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func main() {
registry := enum.NewRegistry[Status]()
item1 := enum.NewItem(Active, "Active")
item2 := enum.NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
activeItems := registry.Filter(func(item *enum.Item[Status]) bool {
return item.Value() == Active
})
for _, item := range activeItems {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
}
```

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

@@ -35,6 +35,7 @@ import (
- [IsDir](#IsDir)
- [ListFileNames](#ListFileNames)
- [RemoveFile](#RemoveFile)
- [RemoveDir](#RemoveDir)
- [ReadFileToString](#ReadFileToString)
- [ReadFileByLine](#ReadFileByLine)
- [Zip](#Zip)
@@ -395,7 +396,7 @@ func main() {
<b>Signature:</b>
```go
func RemoveFile(path string) error
func RemoveFile(path string, onDelete ...func(path string)) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/P2y0XW8a1SH)</span></b>
@@ -416,6 +417,41 @@ func main() {
}
```
### <span id="RemoveDir">RemoveDir</span>
<p>Delete directory.</p>
<b>Signature:</b>
```go
func RemoveDir(path string, onDelete ...func(path string)) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Oa6KnPek2uy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
var deletedPaths []string
err := fileutil.RemoveDir("./tempdir", func(p string) {
deletedPaths = append(deletedPaths, p)
})
if err != nil {
fmt.Println(err)
}
fmt.Println(deletedPaths)
}
```
### <span id="ReadFileToString">ReadFileToString</span>
<p>Return string of file content.</p>
@@ -934,7 +970,7 @@ func main() {
<b>Signature:</b>
```go
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uNep3Tr8fqF)</span></b>
@@ -987,7 +1023,7 @@ import (
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
@@ -1051,7 +1087,7 @@ func main() {
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
@@ -1087,7 +1123,7 @@ func main() {
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
package main
@@ -1104,8 +1140,8 @@ func main() {
}
fmt.Println(v)
// Output:
// 3.9.10.19
}
```
```

View File

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

View File

@@ -10,7 +10,6 @@ Package maputil includes some functions to manipulate map.
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
<div STYLE="page-break-after: always;"></div>
## Example:
@@ -79,6 +78,8 @@ import (
- [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
- [FindValuesBy](#FindValuesBy)
- [ToMarkdownTable](#ToMarkdownTable)
<div STYLE="page-break-after: always;"></div>
@@ -1110,7 +1111,7 @@ Translate the key and value of the map into two slices that are sorted according
<b>Signature:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
@@ -1274,7 +1275,6 @@ func main() {
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>Deletes the key-value pair for the given key.</p>
@@ -1837,7 +1837,7 @@ func main() {
fmt.Println(om.Elements())
// Output:
// [{a 1} {b 2} {c 3}]
// [{a 1} {b 2} {c 3}]
}
```
@@ -2276,8 +2276,8 @@ func main() {
}
result := maputil.SortByKey(m, func(a, b int) bool {
return a < b
})
return a < b
})
fmt.Println(result)
@@ -2288,15 +2288,15 @@ func main() {
### <span id="GetOrDefault">GetOrDefault</span>
<p>returns the value of the given key or a default value if the key is not present.</p>
<p>Returns the value of the given key or a default value if the key is not present.</p>
<b>Signature:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/99QjSYSBdiM)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/99QjSYSBdiM)</span></b>
```go
package main
@@ -2324,4 +2324,109 @@ func main() {
// a
// default
}
```
```
### <span id="FindValuesBy">FindValuesBy</span>
<p>Returns a slice of values from the map that satisfy the given predicate function.</p>
<b>Signature:</b>
```go
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/bvNwNBZDm6v)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result := maputil.FindValuesBy(m, func(k int, v string) bool {
return k%2 == 0
})
fmt.Println(result)
// Output:
// [b d]
}
```
### <span id="ToMarkdownTable">ToMarkdownTable</span>
<p>Convert a map slice data to a Markdown table string. It supports custom header display names and column display order.</p>
<b>Signature:</b>
```go
编辑
func ToMarkdownTable(data []map[string]interface{}, headerMap map[string]string, columnOrder []string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/w_pSLfeyEB5)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
// basic usage: automatically extract column names from data as table headers
data := []map[string]interface{}{
{"name": "Alice", "age": 25, "salary": 50000},
{"name": "Bob", "age": 30, "salary": 60000},
}
result := maputil.ToMarkdownTable(data, nil, nil)
fmt.Println(result)
// output:
// |name|age|salary|
// |---|---|---|
// |Alice|25|50000|
// |Bob|30|60000|
// custom header name
headerMap := map[string]string{
"name": "n",
"age": "a",
"salary": "s",
}
result = maputil.ToMarkdownTable(data, headerMap, nil)
fmt.Println(result)
// ouput:
// |m|a|s|
// |---|---|---|
// |Alice|25|50000|
// |Bob|30|60000|
// custom column display order
columnOrder := []string{"salary", "name"}
result = maputil.ToMarkdownTable(data, nil, columnOrder)
fmt.Println(result)
// output:
// |salary|name|
// |---|---|
// |50000|Alice|
// |60000|Bob|
}
```

View File

@@ -1177,7 +1177,7 @@ func main() {
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
package main
@@ -1210,7 +1210,7 @@ func main() {
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
package main
@@ -1243,7 +1243,7 @@ func main() {
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
package main
@@ -1276,7 +1276,7 @@ func main() {
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
package main

View File

@@ -48,6 +48,8 @@ import (
- [UploadFile](#UploadFile)
- [IsPingConnected](#IsPingConnected)
- [IsTelnetConnected](#IsTelnetConnected)
- [BuildUrl](#BuildUrl)
- [AddQueryParams](#AddQueryParams)
<div STYLE="page-break-after: always;"></div>
@@ -1031,3 +1033,83 @@ func main() {
// false
}
```
### <span id="BuildUrl">BuildUrl</span>
<p>Builds a URL from the given params.</p>
<b>Signature:</b>
```go
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JLXl1hZK7l4)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/netutil"
)
func main() {
urlStr, err := netutil.BuildUrl(
"https",
"example.com",
"query",
map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
},
)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com/query?a=foo&a=bar&b=baz
// <nil>
}
```
### <span id="AddQueryParams">AddQueryParams</span>
<p>Adds query parameters to the given URL.</p>
<b>Signature:</b>
```go
func AddQueryParams(urlStr string, params map[string][]string) (string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JLXl1hZK7l4)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/netutil"
)
func main() {
urlStr, err := netutil.BuildUrl(
"https",
"example.com",
"query",
map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
},
)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com/query?a=foo&a=bar&b=baz
// <nil>
}
```

View File

@@ -536,7 +536,7 @@ func main() {
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
package main

View File

@@ -27,6 +27,7 @@ import (
- [Contain](#Contain)
- [ContainBy](#ContainBy)
- [ContainSubSlice](#ContainSubSlice)
- [ContainAny](#ContainAny)
- [Chunk](#Chunk)
- [Compact](#Compact)
- [Concat](#Concat)
@@ -43,6 +44,7 @@ import (
- [DropRightWhile](#DropRightWhile)
- [Equal](#Equal)
- [EqualWith](#EqualWith)
- [EqualUnordered](#EqualUnordered)
- [Every](#Every)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
@@ -69,6 +71,7 @@ import (
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [ReverseCopy](#ReverseCopy)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
@@ -77,6 +80,7 @@ import (
- [ReplaceAll](#ReplaceAll)
- [Repeat](#Repeat)
- [Shuffle](#Shuffle)
- [ShuffleCopy](#ShuffleCopy)
- [IsAscending](#IsAscending)
- [IsDescending](#IsDescending)
- [IsSorted](#IsSorted)
@@ -253,6 +257,43 @@ func main() {
}
```
### <span id="ContainAny">ContainAny</span>
<p>Check if the slice contains any element from the targets slice.</p>
<b>Signature:</b>
```go
func ContainAny[T comparable](slice []T, targets []T) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/4xoxhc9XSSw)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result1 := slice.ContainAny([]string{"a", "b", "c"}, []string{"a"})
result2 := slice.ContainAny([]string{"a", "b", "c"}, []string{"d", "e"})
result3 := slice.ContainAny([]string{"a", "b", "c"}, []string{"d", "a"})
result4 := slice.ContainAny([]string{"a", "b", "c"}, []string{})
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// false
// true
// false
}
```
### <span id="Chunk">Chunk</span>
<p>Creates an slice of elements split into groups the length of `size`.</p>
@@ -839,6 +880,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>
<p>Return true if all of the values in the slice pass the predicate function.</p>
@@ -1724,6 +1796,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>
<p>Reduce slice.</p>
@@ -1986,6 +2090,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>
<p>Checks if a slice is ascending order.</p>
@@ -2995,7 +3130,7 @@ func main() {
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
import (
@@ -3025,7 +3160,7 @@ func main() {
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
import (

View File

@@ -950,7 +950,7 @@ func main() {
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
import (
@@ -983,7 +983,7 @@ func main() {
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
import (

View File

@@ -31,6 +31,7 @@ import (
- [IsStruct](#IsStruct)
- [Tag](#Tag)
- [Name](#Name)
- [TypeName](#TypeName)
- [Value](#Value)
- [Kind](#Kind)
- [IsEmbedded](#IsEmbedded)
@@ -53,12 +54,13 @@ import (
func New(value any, tagName ...string) *Struct
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/O29l8kk-Z17)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
@@ -68,7 +70,11 @@ func main() {
}
p1 := &People{Name: "11"}
s := structs.New(p1)
// to do something
fmt.Println(s.ToMap())
// Output:
// map[name:11] <nil>
}
```
@@ -88,7 +94,7 @@ func (s *Struct) ToMap() (map[string]any, error)
func ToMap(v any) (map[string]any, error)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qQbLySBgerZ)</span></b>
```go
package main
@@ -130,7 +136,7 @@ func main() {
func (s *Struct) Fields() []*Field
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/w3Kk_CyVY7D)</span></b>
```go
package main
@@ -162,10 +168,10 @@ func main() {
<b>Signature:</b>
```go
func (s *Struct) Field(name string) *Field
func (s *Struct) Field(name string) (*Field, bool)
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/KocZMSYarza)</span></b>
```go
package main
@@ -181,12 +187,14 @@ func main() {
}
p1 := &People{Name: "11"}
s := structs.New(p1)
f := s.Field("Name")
f, found := s.Field("Name")
fmt.Println(f.Value())
fmt.Println(found)
// Output:
// 11
// true
}
```
@@ -200,7 +208,7 @@ func main() {
func (s *Struct) IsStruct() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/bU2FSdkbK1C)</span></b>
```go
package main
@@ -234,7 +242,7 @@ func main() {
func (f *Field) Tag() *Tag
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/DVrx5HvvUJr)</span></b>
```go
package main
@@ -271,7 +279,7 @@ func main() {
func (f *Field) Value() any
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qufYEU2o4Oi)</span></b>
```go
package main
@@ -307,7 +315,7 @@ func main() {
func (f *Field) IsEmbedded() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wV2PrbYm3Ec)</span></b>
```go
package main
@@ -352,7 +360,7 @@ func main() {
func (f *Field) IsExported() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/csK4AXYaNbJ)</span></b>
```go
package main
@@ -391,7 +399,7 @@ func main() {
func (f *Field) IsZero() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/RzqpGISf87r)</span></b>
```go
package main
@@ -430,7 +438,7 @@ func main() {
func (f *Field) Name() string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zfIGlqsatee)</span></b>
```go
package main
@@ -469,7 +477,7 @@ func main() {
func (f *Field) Kind() reflect.Kind
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wg4NlcUNG5o)</span></b>
```go
package main
@@ -498,6 +506,42 @@ func main() {
}
```
### <span id="TypeName">TypeName</span>
<p>Return struct type name.</p>
<b>Signature:</b>
```go
func (s *Struct) TypeName() string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/SWLWd0XBaBb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
Age int
}
p := &Parent{Age: 11}
s := structs.New(p)
fmt.Println(s.TypeName())
// Output:
// Parent
}
```
### <span id="IsSlice">IsSlice</span>
<p>Check if the field is a slice</p>
@@ -508,7 +552,7 @@ func main() {
func (f *Field) IsSlice() bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/MKz4CgBIUrU)</span></b>
```go
package main
@@ -545,7 +589,7 @@ func main() {
func (f *Field) IsTargetType(targetType reflect.Kind) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Ig75P-agN39)</span></b>
```go
package main
@@ -566,12 +610,12 @@ func main() {
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("arr")
fmt.Println(n.IsTargetType(reflect.String))
fmt.Println(a.IsTargetType(reflect.Slice))
// Output:
// Output:
// true
// true
}
```
```

View File

@@ -68,7 +68,8 @@ import (
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#RegexMatchAllGroups)
- [ExtractContent](#ExtractContent)
- [FindAllOccurrences](#FindAllOccurrences)
<div STYLE="page-break-after: always;"></div>
@@ -1743,7 +1744,7 @@ func main() {
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
import (
@@ -1761,4 +1762,32 @@ func main() {
// Output:
// [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)
- [IsIpV4](#IsIpV4)
- [IsIpV6](#IsIpV6)
- [IsIpPort](#IsIpPort)
- [IsStrongPassword](#IsStrongPassword)
- [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword)
@@ -64,6 +65,8 @@ import (
- [IsAmericanExpress](#IsAmericanExpress)
- [IsUnionPay](#IsUnionPay)
- [IsChinaUnionPay](#IsChinaUnionPay)
- [IsPassport](#IsPassport)
- [IsChineseHMPassport](#IsChineseHMPassport)
<div STYLE="page-break-after: always;"></div>
@@ -825,6 +828,43 @@ func main() {
}
```
### <span id="IsAlphaNumeric">IsAlphaNumeric</span>
<p>Check if the string is alphanumeric.</p>
<b>Signature:</b>
```go
func IsAlphaNumeric(s string) bool
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/RHeESLrLg9c)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsAlphaNumeric("ABC")
result2 := validator.IsAlphaNumeric("123")
result3 := validator.IsAlphaNumeric("abc123")
result4 := validator.IsAlphaNumeric("abc123@#$")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
```
### <span id="IsJSON">IsJSON</span>
<p>Check if the string is valid JSON.</p>
@@ -992,6 +1032,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>
<p>Check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?gt&lt)).</p>
@@ -1494,3 +1571,80 @@ func main() {
// false
}
```
### <span id="IsPassport">IsPassport</span>
<p>Passport validation(using regex).</p>
<b>Signature:</b>
```go
func IsPassport(passport, country string) bool
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/dvOiV2BW7Aw)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsPassport("P123456789", "CN")
result2 := validator.IsPassport("123456789", "US")
result3 := validator.IsPassport("AB1234567", "RU")
result4 := validator.IsPassport("123456789", "CN")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// true
// true
// true
// false
}
```
### <span id="IsChineseHMPassport">IsChineseHMPassport</span>
<p>Mainland travel permit for Hong Kong, Macao validation (using regex). </p>
<b>Signature:</b>
```go
func IsChineseHMPassport(hmPassport string) bool
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/xKG6spQTcY0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
result1 := validator.IsChineseHMPassport("C12345678")
result2 := validator.IsChineseHMPassport("C00000000")
result3 := validator.IsChineseHMPassport("M12345678")
result4 := validator.IsChineseHMPassport("c12345678")
result5 := validator.IsChineseHMPassport("C1234567")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// true
// true
// true
// false
// false
}
```

View File

@@ -507,7 +507,7 @@ func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
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
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
```
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.4. </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 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
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.4。</b>
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。</b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x

526
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

317
enum/enum.go Normal file
View File

@@ -0,0 +1,317 @@
// Copyright 2025 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
// Package enum provides a simple enum implementation.
package enum
import (
"encoding/json"
"fmt"
"reflect"
"sort"
"sync"
)
// Enum defines a common enum interface.
type Enum[T comparable] interface {
Value() T
String() string
Name() string
Valid(checker ...func(T) bool) bool
}
// Item defines a common enum item struct implement Enum interface.
type Item[T comparable] struct {
value T
name string
}
// NewItem creates a new enum item.
// Play: https://go.dev/play/p/8qNsLw01HD5
func NewItem[T comparable](value T, name string) *Item[T] {
return &Item[T]{value: value, name: name}
}
// Pair represents a value-name pair for creating enum items
type Pair[T comparable] struct {
Value T
Name string
}
// NewItemsFromPairs creates enum items from a slice of Pair structs.
// Play: https://go.dev/play/p/xKnoGa7gnev
func NewItemsFromPairs[T comparable](pairs ...Pair[T]) []*Item[T] {
if len(pairs) == 0 {
return []*Item[T]{}
}
items := make([]*Item[T], 0, len(pairs))
for _, pair := range pairs {
items = append(items, &Item[T]{value: pair.Value, name: pair.Name})
}
return items
}
// Value returns the value of the enum item.
// Play: https://go.dev/play/p/xKnoGa7gnev
func (it *Item[T]) Value() T {
return it.value
}
// Name returns the name of the enum item.
// Play: https://go.dev/play/p/xKnoGa7gnev
func (it *Item[T]) Name() string {
return it.name
}
// String returns the string representation of the enum item.
func (it *Item[T]) String() string {
return it.name
}
// Valid checks if the enum item is valid. If a custom check function is provided, it will be used to validate the value.
// Play: https://go.dev/play/p/pA3lYY2VSm3
func (it *Item[T]) Valid(checker ...func(T) bool) bool {
if len(checker) > 0 {
return checker[0](it.value) && it.name != ""
}
var zero T
return it.value != zero && it.name != ""
}
// MarshalJSON implements the json.Marshaler interface.
// Play: https://go.dev/play/p/zIZEdAnneB5
func (it *Item[T]) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"value": it.value,
"name": it.name,
})
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Play: https://go.dev/play/p/zIZEdAnneB5
func (it *Item[T]) UnmarshalJSON(data []byte) error {
type alias struct {
Value any `json:"value"`
Name string `json:"name"`
}
var temp alias
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
var v T
rv := reflect.TypeOf(v)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, ok := temp.Value.(float64)
if !ok {
return fmt.Errorf("invalid type for value, want int family")
}
converted := reflect.ValueOf(int64(val)).Convert(rv)
it.value = converted.Interface().(T)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, ok := temp.Value.(float64)
if !ok {
return fmt.Errorf("invalid type for value, want uint family")
}
converted := reflect.ValueOf(uint64(val)).Convert(rv)
it.value = converted.Interface().(T)
case reflect.Float32, reflect.Float64:
val, ok := temp.Value.(float64)
if !ok {
return fmt.Errorf("invalid type for value, want float family")
}
converted := reflect.ValueOf(val).Convert(rv)
it.value = converted.Interface().(T)
case reflect.String:
val, ok := temp.Value.(string)
if !ok {
return fmt.Errorf("invalid type for value, want string")
}
it.value = any(val).(T)
case reflect.Bool:
val, ok := temp.Value.(bool)
if !ok {
return fmt.Errorf("invalid type for value, want bool")
}
it.value = any(val).(T)
default:
val, ok := temp.Value.(float64)
if ok {
converted := reflect.ValueOf(int64(val)).Convert(rv)
it.value = converted.Interface().(T)
} else {
val2, ok2 := temp.Value.(T)
if !ok2 {
return fmt.Errorf("invalid type for value")
}
it.value = val2
}
}
it.name = temp.Name
return nil
}
// Registry defines a common enum registry struct.
type Registry[T comparable] struct {
mu sync.RWMutex
values map[T]*Item[T]
names map[string]*Item[T]
items []*Item[T]
}
// NewRegistry creates a new enum registry.
// Play: https://go.dev/play/p/ABEXsYfJKMo
func NewRegistry[T comparable](items ...*Item[T]) *Registry[T] {
r := &Registry[T]{
values: make(map[T]*Item[T]),
names: make(map[string]*Item[T]),
items: make([]*Item[T], 0, len(items)),
}
r.Add(items...)
return r
}
// Add adds enum items to the registry.
// Play: https://go.dev/play/p/ABEXsYfJKMo
func (r *Registry[T]) Add(items ...*Item[T]) {
r.mu.Lock()
defer r.mu.Unlock()
for _, item := range items {
if _, exists := r.values[item.value]; exists {
continue
}
if _, exists := r.names[item.name]; exists {
continue
}
r.values[item.value] = item
r.names[item.name] = item
r.items = append(r.items, item)
}
}
// Remove removes an enum item from the registry by its value.
// Play: https://go.dev/play/p/dSG84wQ3TuC
func (r *Registry[T]) Remove(value T) bool {
r.mu.Lock()
defer r.mu.Unlock()
item, ok := r.values[value]
if !ok {
return false
}
delete(r.values, value)
delete(r.names, item.name)
for i, it := range r.items {
if it.value == value {
r.items = append(r.items[:i], r.items[i+1:]...)
break
}
}
return true
}
// Update updates the name of an enum item in the registry by its value.
// Play: https://go.dev/play/p/Ol0moT1J9Xl
func (r *Registry[T]) Update(value T, newName string) bool {
r.mu.Lock()
defer r.mu.Unlock()
item, ok := r.values[value]
if !ok {
return false
}
delete(r.names, item.name)
item.name = newName
r.names[newName] = item
return true
}
// GetByValue retrieves an enum item by its value.
// Play: https://go.dev/play/p/niJ1U2KlE_m
func (r *Registry[T]) GetByValue(value T) (*Item[T], bool) {
r.mu.RLock()
defer r.mu.RUnlock()
item, ok := r.values[value]
return item, ok
}
// GetByName retrieves an enum item by its name.
// Play: https://go.dev/play/p/49ie_gpqH0m
func (r *Registry[T]) GetByName(name string) (*Item[T], bool) {
r.mu.RLock()
defer r.mu.RUnlock()
item, ok := r.names[name]
return item, ok
}
// Items returns a slice of all enum items in the registry.
// Play: https://go.dev/play/p/lAJFAradbvQ
func (r *Registry[T]) Items() []*Item[T] {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]*Item[T], len(r.items))
copy(result, r.items)
return result
}
// Contains checks if an enum item with the given value exists in the registry.
// Play: https://go.dev/play/p/_T-lPYkZn2j
func (r *Registry[T]) Contains(value T) bool {
_, ok := r.GetByValue(value)
return ok
}
// Size returns the number of enum items in the registry.
// Play: https://go.dev/play/p/TeDArWhlQe2
func (r *Registry[T]) Size() int {
r.mu.RLock()
defer r.mu.RUnlock()
return len(r.items)
}
// Range iterates over all enum items in the registry and applies the given function.
// Play: https://go.dev/play/p/GPsZbQbefWN
func (r *Registry[T]) Range(fn func(*Item[T]) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
for _, item := range r.items {
if !fn(item) {
break
}
}
}
// SortedItems returns a slice of all enum items sorted by the given less function.
// Play: https://go.dev/play/p/tN9RE_m_WEI
func (r *Registry[T]) SortedItems(less func(*Item[T], *Item[T]) bool) []*Item[T] {
items := r.Items()
sort.Slice(items, func(i, j int) bool {
return less(items[i], items[j])
})
return items
}
// Filter returns a slice of enum items that satisfy the given predicate function.
// Play: https://go.dev/play/p/uTUpTdcyoCU
func (r *Registry[T]) Filter(predicate func(*Item[T]) bool) []*Item[T] {
r.mu.RLock()
defer r.mu.RUnlock()
var result []*Item[T]
for _, item := range r.items {
if predicate(item) {
result = append(result, item)
}
}
return result
}

208
enum/enum_example_test.go Normal file
View File

@@ -0,0 +1,208 @@
package enum
import "fmt"
func ExampleNewItem() {
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
fmt.Println(item1.Name(), item1.Value())
fmt.Println(item2.Name(), item2.Value())
// Output:
// Active 1
// Inactive 2
}
func ExampleNewItemsFromPairs() {
items := NewItemsFromPairs(
Pair[Status]{Value: Active, Name: "Active"},
Pair[Status]{Value: Inactive, Name: "Inactive"},
)
fmt.Println(items[0].Name(), items[0].Value())
fmt.Println(items[1].Name(), items[1].Value())
// Output:
// Active 1
// Inactive 2
}
func ExampleItem_Valid() {
item := NewItem(Active, "Active")
fmt.Println(item.Valid())
invalidItem := NewItem(Unknown, "")
fmt.Println(invalidItem.Valid())
// Output:
// true
// false
}
func ExampleItem_MarshalJSON() {
item := NewItem(Active, "Active")
data, _ := item.MarshalJSON()
fmt.Println(string(data))
var unmarshaledItem Item[Status]
_ = unmarshaledItem.UnmarshalJSON(data)
fmt.Println(unmarshaledItem.Name(), unmarshaledItem.Value())
// Output:
// {"name":"Active","value":1}
// Active 1
}
func ExampleRegistry_Add() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
if item, found := registry.GetByValue(Active); found {
fmt.Println("Found by value:", item.Name())
}
if item, found := registry.GetByName("Inactive"); found {
fmt.Println("Found by name:", item.Value())
}
// Output:
// Found by value: Active
// Found by name: 2
}
func ExampleRegistry_Remove() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
registry.Add(item1)
fmt.Println("Size before removal:", registry.Size())
removed := registry.Remove(Active)
fmt.Println("Removed:", removed)
fmt.Println("Size after removal:", registry.Size())
// Output:
// Size before removal: 1
// Removed: true
// Size after removal: 0
}
func ExampleRegistry_Update() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
registry.Add(item1)
updated := registry.Update(Active, "Activated")
fmt.Println("Updated:", updated)
if item, found := registry.GetByValue(Active); found {
fmt.Println("New name:", item.Name())
}
// Output:
// Updated: true
// New name: Activated
}
func ExampleRegistry_Items() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
for _, item := range registry.Items() {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
func ExampleRegistry_Contains() {
registry := NewRegistry[Status]()
item := NewItem(Active, "Active")
registry.Add(item)
fmt.Println(registry.Contains(Active))
fmt.Println(registry.Contains(Inactive))
// Output:
// true
// false
}
func ExampleRegistry_Size() {
registry := NewRegistry[Status]()
fmt.Println("Initial size:", registry.Size())
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
fmt.Println("Size after adding items:", registry.Size())
registry.Remove(Active)
fmt.Println("Size after removing an item:", registry.Size())
// Output:
// Initial size: 0
// Size after adding items: 2
// Size after removing an item: 1
}
func ExampleRegistry_Range() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
registry.Range(func(item *Item[Status]) bool {
fmt.Println(item.Name(), item.Value())
return true // continue iteration
})
// Output:
// Active 1
// Inactive 2
}
func ExampleRegistry_SortedItems() {
registry := NewRegistry[Status]()
item1 := NewItem(Inactive, "Inactive")
item2 := NewItem(Active, "Active")
registry.Add(item1, item2)
for _, item := range registry.SortedItems(func(i1, i2 *Item[Status]) bool {
return i1.value < i2.value
}) {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
// Inactive 2
}
func ExampleRegistry_Filter() {
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
activeItems := registry.Filter(func(item *Item[Status]) bool {
return item.Value() == Active
})
for _, item := range activeItems {
fmt.Println(item.Name(), item.Value())
}
// Output:
// Active 1
}

184
enum/enum_test.go Normal file
View File

@@ -0,0 +1,184 @@
// Copyright 2025 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
package enum
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type Status int
const (
Unknown Status = iota
Active
Inactive
)
func TestNewItemsFromPairs(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestNewItemsFromPairs")
items := NewItemsFromPairs(
Pair[Status]{Value: Active, Name: "Active"},
Pair[Status]{Value: Inactive, Name: "Inactive"},
)
assert.Equal(2, len(items))
assert.Equal(Active, items[0].Value())
assert.Equal("Active", items[0].Name())
assert.Equal(Inactive, items[1].Value())
assert.Equal("Inactive", items[1].Name())
}
func TestItem_Valid(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestItem_Valid")
item := NewItem(Active, "Active")
assert.Equal(true, item.Valid())
invalidItem := NewItem(Unknown, "")
assert.Equal(false, invalidItem.Valid())
}
func TestItem_MarshalJSON(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestItem_MarshalJSON")
item := NewItem(Active, "Active")
data, err := item.MarshalJSON()
assert.IsNil(err)
assert.Equal("{\"name\":\"Active\",\"value\":1}", string(data))
var unmarshaledItem Item[Status]
err = unmarshaledItem.UnmarshalJSON(data)
assert.IsNil(err)
assert.Equal(item.Value(), unmarshaledItem.Value())
assert.Equal(item.Name(), unmarshaledItem.Name())
}
func TestRegistry_AddAndGet(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_AddAndGet")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
assert.Equal(2, registry.Size())
item, ok := registry.GetByValue(Active)
assert.Equal(true, ok)
assert.Equal("Active", item.Name())
item, ok = registry.GetByName("Inactive")
assert.Equal(true, ok)
assert.Equal(Inactive, item.Value())
}
func TestRegistry_Remove(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_Remove")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
assert.Equal(2, registry.Size())
removed := registry.Remove(Active)
assert.Equal(true, removed)
assert.Equal(1, registry.Size())
_, ok := registry.GetByValue(Active)
assert.Equal(false, ok)
}
func TestRegistry_Update(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_Update")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
registry.Add(item1)
updated := registry.Update(Active, "Activated")
assert.Equal(true, updated)
item, ok := registry.GetByValue(Active)
assert.Equal(true, ok)
assert.Equal("Activated", item.Name())
}
func TestRegistry_Contains(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_Contains")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
registry.Add(item1)
assert.Equal(true, registry.Contains(Active))
assert.Equal(false, registry.Contains(Inactive))
}
func TestRegistry_Range(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_Range")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
var values []Status
registry.Range(func(item *Item[Status]) bool {
values = append(values, item.Value())
return true
})
assert.Equal(2, len(values))
assert.Equal(Active, values[0])
assert.Equal(Inactive, values[1])
}
func TestRegistry_SortedItems(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_SortedItems")
registry := NewRegistry[Status]()
item1 := NewItem(Inactive, "Inactive")
item2 := NewItem(Active, "Active")
registry.Add(item1, item2)
sortedItems := registry.SortedItems(func(i1, i2 *Item[Status]) bool {
return i1.value < i2.value
})
assert.Equal(2, len(sortedItems))
assert.Equal(Active, sortedItems[0].Value())
assert.Equal(Inactive, sortedItems[1].Value())
}
func TestRegistry_Filter(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRegistry_Filter")
registry := NewRegistry[Status]()
item1 := NewItem(Active, "Active")
item2 := NewItem(Inactive, "Inactive")
registry.Add(item1, item2)
filteredItems := registry.Filter(func(item *Item[Status]) bool {
return item.Value() == Active
})
assert.Equal(1, len(filteredItems))
assert.Equal(Active, filteredItems[0].Value())
}

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(topic string, 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(event.Topic, 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(topic string, 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,237 @@
package eventbus
import (
"fmt"
"sort"
"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(topic string, err error) {
fmt.Println(topic, err)
})
eb.Subscribe("event1", func(eventData int) {
panic("error")
}, false, 0, nil)
eb.Publish(Event[int]{Topic: "event1", Payload: 1})
// Output:
// event1 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()
sort.Strings(events)
for _, event := range events {
fmt.Println(event)
}
// Output:
// event1
// event2
}

222
eventbus/eventbus_test.go Normal file
View File

@@ -0,0 +1,222 @@
package eventbus
import (
"sort"
"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(topic string, err error) {
assert.Equal("event1", topic)
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()
sort.Strings(events)
assert.Equal(2, len(events))
assert.Equal([]string{"event1", "event2"}, events)
}

View File

@@ -25,6 +25,8 @@ import (
"sync"
"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
@@ -170,10 +172,54 @@ func IsDir(path string) bool {
// RemoveFile remove the path file.
// Play: https://go.dev/play/p/P2y0XW8a1SH
func RemoveFile(path string) error {
func RemoveFile(path string, onDelete ...func(path string)) error {
info, err := os.Stat(path)
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("%s is a directory", path)
}
if len(onDelete) > 0 && onDelete[0] != nil {
onDelete[0](path)
}
return os.Remove(path)
}
// RemoveDir remove the path directory.
// Play: https://go.dev/play/p/Oa6KnPek2uy
func RemoveDir(path string, onDelete ...func(path string)) error {
info, err := os.Stat(path)
if err != nil {
return err
}
if !info.IsDir() {
return fmt.Errorf("%s is not a directory", path)
}
var callback func(string)
if len(onDelete) > 0 {
callback = onDelete[0]
}
err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
if err == nil && callback != nil {
callback(p)
}
return nil
})
if err != nil {
return err
}
return os.RemoveAll(path)
}
// CopyFile copy src file to dest file.
// Play: https://go.dev/play/p/Jg9AMJMLrJi
func CopyFile(srcPath string, dstPath string) error {
@@ -422,8 +468,15 @@ func UnZip(zipFile string, destPath string) error {
defer zipReader.Close()
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
path, err := safeFilepathJoin(destPath, f.Name)
path, err := safeFilepathJoin(destPath, decodeName)
if err != nil {
return err
}

View File

@@ -85,14 +85,37 @@ func TestIsDir(t *testing.T) {
func TestRemoveFile(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRemoveFile")
f := "./text.txt"
if !IsExist(f) {
CreateFile(f)
err := RemoveFile(f)
assert.IsNil(err)
CreateFile(f)
err := RemoveFile(f)
assert.IsNil(err)
}
func TestRemoveDir(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRemoveDir")
err := os.MkdirAll("./tempdir/a/b", 0755)
if err != nil {
t.Error(err)
t.FailNow()
}
var deletedPaths []string
err = RemoveDir("./tempdir", func(p string) {
deletedPaths = append(deletedPaths, p)
})
if err != nil {
t.Error(err)
t.FailNow()
}
assert.Equal([]string{"./tempdir", "tempdir/a", "tempdir/a/b"}, deletedPaths)
assert.Equal(false, IsExist("./tempdir"))
}
func TestCopyFile(t *testing.T) {

View File

@@ -26,7 +26,7 @@ type tagVS_FIXEDFILEINFO struct {
}
// 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) {
// 加载系统dll
versionDLL := syscall.NewLazyDLL("version.dll")

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"golang.org/x/exp/constraints"
@@ -666,3 +667,164 @@ func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V {
}
return defaultValue
}
// FindValuesBy returns a slice of values from the map that satisfy the given predicate function.
// Play: https://go.dev/play/p/bvNwNBZDm6v
func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V {
result := make([]V, 0)
for k, v := range m {
if predicate(k, v) {
result = append(result, v)
}
}
return result
}
// ToMarkdownTable converts a slice of maps to a Markdown table.
// Play: ttps://go.dev/play/p/w_pSLfeyEB5
func ToMarkdownTable(data []map[string]interface{}, headerMap map[string]string, columnOrder []string) string {
if len(data) == 0 {
return "| |\n|---|\n"
}
var headers []string
// 如果提供了columnOrder则按指定顺序排列
if len(columnOrder) > 0 {
headers = make([]string, len(columnOrder))
copy(headers, columnOrder)
} else {
// 否则按自然顺序提取headers
seen := make(map[string]bool)
for _, row := range data {
for k := range row {
if !seen[k] {
seen[k] = true
headers = append(headers, k)
}
}
}
}
var builder strings.Builder
// Header row - 使用映射的中文标题
builder.WriteString("| ")
for i, h := range headers {
// 如果有映射则使用中文标题,否则使用原字段名
displayHeader := h
if headerMap != nil && headerMap[h] != "" {
displayHeader = headerMap[h]
}
builder.WriteString(displayHeader)
if i < len(headers)-1 {
builder.WriteString(" | ")
}
}
builder.WriteString(" |\n")
// Separator
builder.WriteString("|")
for i := range headers {
if i > 0 {
builder.WriteString("|")
}
builder.WriteString("---")
}
builder.WriteString("|\n")
// Data rows
for _, row := range data {
builder.WriteString("| ")
for i, h := range headers {
val, exists := row[h]
var cell string
if !exists {
cell = ""
} else {
cell = formatValue(val)
}
builder.WriteString(cell)
if i < len(headers)-1 {
builder.WriteString(" | ")
}
}
builder.WriteString(" |\n")
}
return builder.String()
}
// formatValue formats any value for display in Markdown table
func formatValue(v interface{}) string {
switch val := v.(type) {
case int:
return commaInt64(int64(val))
case int8:
return commaInt64(int64(val))
case int16:
return commaInt64(int64(val))
case int32:
return commaInt64(int64(val))
case int64:
return commaInt64(val)
case uint:
return commaUint64(uint64(val))
case uint8:
return commaUint64(uint64(val))
case uint16:
return commaUint64(uint64(val))
case uint32:
return commaUint64(uint64(val))
case uint64:
return commaUint64(val)
case float32:
return fmt.Sprintf("%.2f", val)
case float64:
return fmt.Sprintf("%.2f", val)
case string:
return val
case bool:
return fmt.Sprintf("%t", val)
case nil:
return ""
default:
return fmt.Sprintf("%v", val)
}
}
// commaInt64 adds comma separators to int64
func commaInt64(n int64) string {
if n == 0 {
return "0"
}
neg := n < 0
if neg {
n = -n
}
s := strconv.FormatInt(n, 10)
return addCommas(s)
}
// commaUint64 adds comma separators to uint64
func commaUint64(n uint64) string {
if n == 0 {
return "0"
}
s := strconv.FormatUint(n, 10)
return addCommas(s)
}
// addCommas inserts commas every 3 digits
func addCommas(s string) string {
var result strings.Builder
for i, c := range s {
if i > 0 && (len(s)-i)%3 == 0 {
result.WriteRune(',')
}
result.WriteRune(c)
}
return result.String()
}

View File

@@ -829,3 +829,25 @@ func ExampleOrderedMap_UnmarshalJSON() {
// fmt.Println(om.Elements())
}
func ExampleFindValuesBy() {
m := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result := FindValuesBy(m, func(k int, v string) bool {
return k%2 == 0
})
// github action will excute this test currently, so sort the result
// to make it deterministic
sort.Strings(result)
fmt.Println(result)
// Output:
// [b d]
}

View File

@@ -1,6 +1,7 @@
package maputil
import (
"fmt"
"math/cmplx"
"sort"
"strconv"
@@ -888,3 +889,84 @@ func TestGetOrDefault(t *testing.T) {
result2 := GetOrDefault(m1, 5, "123")
assert.Equal("123", result2)
}
func TestFindValuesBy(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFindValuesBy")
tests := []struct {
name string
inputMap map[string]string
key string
expected []string
}{
{
name: "Key exists",
inputMap: map[string]string{"a": "1", "b": "2", "c": "3"},
key: "b",
expected: []string{"2"},
},
{
name: "Key does not exist",
inputMap: map[string]string{"a": "1", "b": "2", "c": "3"},
key: "d",
expected: []string{},
},
{
name: "Empty map",
inputMap: map[string]string{},
key: "a",
expected: []string{},
},
}
for _, tt := range tests {
result := FindValuesBy(tt.inputMap, func(key string, value string) bool {
return key == tt.key
})
assert.Equal(tt.expected, result)
}
}
func TestToMarkdownTable(t *testing.T) {
// 测试空数据
emptyResult := ToMarkdownTable([]map[string]interface{}{}, nil, nil)
expectedEmpty := "| |\n|---|\n"
if emptyResult != expectedEmpty {
t.Errorf("Expected empty table, got: %s", emptyResult)
}
// 测试基本数据
data := []map[string]interface{}{
{"name": "Alice", "age": 25, "salary": 50000},
{"name": "Bob", "age": 30, "salary": 60000},
}
result := ToMarkdownTable(data, nil, nil)
fmt.Printf("%s", result)
// 验证结果包含预期的表头和数据行
}
// 测试自定义列顺序
func TestToMarkdownTableWithColumnOrder(t *testing.T) {
data := []map[string]interface{}{
{"name": "Alice", "age": 25, "salary": 50000},
}
columnOrder := []string{"salary", "name", "age"}
result := ToMarkdownTable(data, nil, columnOrder)
fmt.Printf("%s", result)
// 验证列顺序是否正确
}
// 测试自定义表头
func TestToMarkdownTableWithHeaderMap(t *testing.T) {
data := []map[string]interface{}{
{"name": "Alice", "age": 25},
}
headerMap := map[string]string{
"name": "姓名",
"age": "年龄",
}
result := ToMarkdownTable(data, headerMap, nil)
fmt.Printf("%s", result)
// 验证中文表头是否正确显示
}

View File

@@ -145,10 +145,14 @@ func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
// Max return max value of numbers.
// Play: https://go.dev/play/p/cN8DHI0rTkH
func Max[T constraints.Integer | constraints.Float](numbers ...T) T {
max := numbers[0]
func Max[T constraints.Ordered](items ...T) T {
if len(items) < 1 {
panic("mathutil.Max: empty list")
}
for _, v := range numbers {
max := items[0]
for _, v := range items {
if 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.
// Play: https://go.dev/play/p/21BER_mlGUj
func Min[T constraints.Integer | constraints.Float](numbers ...T) T {
min := numbers[0]
func Min[T constraints.Ordered](items ...T) T {
if len(items) < 1 {
panic("mathutil.min: empty list")
}
for _, v := range numbers {
min := items[0]
for _, v := range items {
if min > v {
min = v
}
@@ -230,6 +238,9 @@ func Sum[T constraints.Integer | constraints.Float](numbers ...T) T {
// Average return average value of numbers.
// Play: https://go.dev/play/p/Vv7LBwER-pz
func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 {
if len(numbers) == 0 {
return 0
}
var sum float64
for _, num := range numbers {
sum += float64(num)
@@ -400,7 +411,7 @@ func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
}
// 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 {
n := len(numbers)
if n == 0 {
@@ -418,13 +429,13 @@ func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 {
}
// 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 {
return math.Sqrt(Variance(numbers))
}
// Permutation calculate P(n, k).
// Play: todo
// Play: https://go.dev/play/p/MgobwH_FOxj
func Permutation(n, k uint) uint {
if n < k {
return 0
@@ -437,7 +448,7 @@ func Permutation(n, k uint) uint {
}
// Combination calculate C(n, k).
// Play: todo
// Play: https://go.dev/play/p/ENFQRDQUFi9
func Combination(n, k uint) uint {
if n < k {
return 0

View File

@@ -178,6 +178,7 @@ func TestMax(t *testing.T) {
assert.Equal(0, Max(0, 0))
assert.Equal(3, Max(1, 2, 3))
assert.Equal(1.4, Max(1.2, 1.4, 1.1, 1.4))
assert.Equal("abc", Max("a", "ab", "abc"))
type Integer int
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(1, Min(1, 2, 3))
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) {

View File

@@ -3,7 +3,6 @@ package netutil
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
@@ -27,8 +26,8 @@ func TestHttpGet(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestHttpPost(t *testing.T) {
@@ -49,8 +48,8 @@ func TestHttpPost(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestHttpPostFormData(t *testing.T) {
@@ -69,8 +68,8 @@ func TestHttpPostFormData(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestHttpPut(t *testing.T) {
@@ -92,8 +91,8 @@ func TestHttpPut(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestHttpPatch(t *testing.T) {
@@ -115,8 +114,8 @@ func TestHttpPatch(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestHttpDelete(t *testing.T) {
@@ -127,8 +126,8 @@ func TestHttpDelete(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestConvertMapToQueryString(t *testing.T) {
@@ -229,8 +228,8 @@ func TestHttpClent_Post(t *testing.T) {
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
defer resp.Body.Close()
t.Log("response status:", resp.StatusCode)
}
func TestStructToUrlValues(t *testing.T) {

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net"
@@ -11,6 +12,7 @@ import (
"net/url"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"time"
@@ -305,3 +307,97 @@ func IsTelnetConnected(host string, port string) bool {
return true
}
// BuildUrl builds a URL from the given params.
// Play: https://go.dev/play/p/JLXl1hZK7l4
func BuildUrl(scheme, host, path string, query map[string][]string) (string, error) {
if err := validateScheme(scheme); err != nil {
return "", err
}
if path != "" {
if !hostRegex.MatchString(host) {
return "", fmt.Errorf("invalid host: '%s' is not a valid host", host)
}
}
parsedUrl := &url.URL{
Scheme: scheme,
Host: host,
}
if path == "" {
parsedUrl.Path = "/"
} else if !strings.HasPrefix(path, "/") {
parsedUrl.Path = "/" + path
} else {
parsedUrl.Path = path
}
queryParams := parsedUrl.Query()
for key, values := range query {
for _, value := range values {
queryParams.Add(key, value)
}
}
parsedUrl.RawQuery = queryParams.Encode()
return parsedUrl.String(), nil
}
var supportedSchemes = map[string]bool{
"http": true,
"https": true,
"ftp": true,
"file": true,
"mailto": true,
"ws": true, // WebSocket
"wss": true, // WebSocket Secure
"data": true, // Data URL
}
func validateScheme(scheme string) error {
if _, exists := supportedSchemes[scheme]; !exists {
return fmt.Errorf("invalid scheme: '%s' is not supported", scheme)
}
return nil
}
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]?)(\.[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]?)+$`)
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: https://go.dev/play/p/JLXl1hZK7l4
func AddQueryParams(urlStr string, params map[string][]string) (string, error) {
parsedUrl, err := url.Parse(urlStr)
if err != nil {
return "", err
}
queryParams := parsedUrl.Query()
for k, values := range params {
if k == "" {
return "", errors.New("empty key is not allowed")
}
if !alphaNumericRegex.MatchString(k) {
return "", fmt.Errorf("query parameter key %s must be alphanumeric", k)
}
for _, v := range values {
if !alphaNumericRegex.MatchString(v) {
return "", fmt.Errorf("query parameter value %s must be alphanumeric", v)
}
queryParams.Add(k, v)
}
}
parsedUrl.RawQuery = queryParams.Encode()
return parsedUrl.String(), nil
}

View File

@@ -201,3 +201,40 @@ func ExampleIsTelnetConnected() {
// true
// false
}
func ExampleBuildUrl() {
urlStr, err := BuildUrl(
"https",
"example.com",
"query",
map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
},
)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com/query?a=foo&a=bar&b=baz
// <nil>
}
func ExampleAddQueryParams() {
urlStr := "https://example.com"
params := map[string][]string{
"a": {"foo", "bar"},
"b": {"baz"},
}
urlStr, err := AddQueryParams(urlStr, params)
fmt.Println(urlStr)
fmt.Println(err)
// Output:
// https://example.com?a=foo&a=bar&b=baz
// <nil>
}

View File

@@ -142,3 +142,118 @@ func TestTelnetConnected(t *testing.T) {
result2 := IsTelnetConnected("www.baidu.com", "123")
assert.Equal(false, result2)
}
func TestBuildUrl(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBuildUrl")
tests := []struct {
scheme string
host string
path string
query map[string][]string
want string
wantErr bool
}{
{
scheme: "http",
host: "www.test.com",
path: "/path/subpath",
query: map[string][]string{"a": {"1"}, "b": {"2"}},
want: "http://www.test.com/path/subpath?a=1&b=2",
wantErr: false,
},
{
scheme: "http",
host: "www.test.com",
path: "/simple-path",
query: map[string][]string{"a": {"1"}, "b": {"2"}},
want: "http://www.test.com/simple-path?a=1&b=2",
wantErr: false,
},
{
scheme: "http",
host: "www.test.com",
path: "",
query: map[string][]string{"a": {"foo", "bar"}, "b": {"baz"}},
want: "http://www.test.com/?a=foo&a=bar&b=baz",
wantErr: false,
},
{
scheme: "https",
host: "www.test. com",
path: "/path",
query: nil,
want: "",
wantErr: true,
},
{
scheme: "https",
host: "www.test.com",
path: "/path with spaces",
query: nil,
want: "https://www.test.com/path%20with%20spaces",
wantErr: false,
},
{
scheme: "https",
host: "my.api.edu.cn",
path: "/api",
query: nil,
want: "https://my.api.edu.cn/api",
wantErr: false,
},
}
for _, tt := range tests {
got, err := BuildUrl(tt.scheme, tt.host, tt.path, tt.query)
assert.Equal(tt.want, got)
assert.Equal(tt.wantErr, err != nil)
}
}
func TestAddQueryParams(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestAddQueryParams")
tests := []struct {
url string
query map[string][]string
want string
wantErr bool
}{
{
url: "http://www.test.com",
query: map[string][]string{"a": {"1"}, "b": {"2"}},
want: "http://www.test.com?a=1&b=2",
wantErr: false,
},
{
url: "http://www.test.com",
query: map[string][]string{"a": {"foo", "bar"}, "b": {"baz"}},
want: "http://www.test.com?a=foo&a=bar&b=baz",
wantErr: false,
},
{
url: "http://www.test.com",
query: map[string][]string{},
want: "http://www.test.com",
wantErr: false,
},
{
url: "http://www.test.com",
query: map[string][]string{"a": {"$%"}},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
got, err := AddQueryParams(tt.url, tt.query)
assert.Equal(tt.want, got)
assert.Equal(tt.wantErr, err != nil)
}
}

View File

@@ -126,12 +126,24 @@ func RandFloat(min, max float64, precision int) float64 {
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.
// Play: https://go.dev/play/p/I3yndUQ-rhh
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)
used := make(map[float64]struct{}, length)
for i := 0; i < length; {
@@ -335,7 +347,7 @@ func UUIdV4() (string, error) {
}
// RandNumberOfLength 生成一个长度为len的随机数
// Play: todo
// Play: https://go.dev/play/p/oyZbuV7bu7b
func RandNumberOfLength(len int) int {
m := 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)
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) {

View File

@@ -128,16 +128,19 @@ func Retry(retryFunc RetryFunc, opts ...Option) error {
}
var i uint
var lastErr error
for i < config.retryTimes {
err := retryFunc()
if err != nil {
lastErr = retryFunc()
if lastErr == nil {
return nil
}
if i < config.retryTimes-1 { // Only wait if it's not the last retry
select {
case <-time.After(config.backoffStrategy.CalculateInterval()):
case <-config.context.Done():
return errors.New("retry is cancelled")
}
} else {
return nil
}
i++
}
@@ -146,7 +149,7 @@ func Retry(retryFunc RetryFunc, opts ...Option) error {
lastSlash := strings.LastIndex(funcPath, "/")
funcName := funcPath[lastSlash+1:]
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
return fmt.Errorf("function %s run failed after %d times retry, last error: %w", funcName, i, lastErr)
}
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.

View File

@@ -118,7 +118,7 @@ func ExampleRetryTimes() {
}
// Output:
// function retry.ExampleRetryTimes.func1 run failed after 2 times retry
// function retry.ExampleRetryTimes.func1 run failed after 2 times retry, last error: error occurs
}
func ExampleRetry() {

View File

@@ -15,14 +15,16 @@ func TestRetryFailed(t *testing.T) {
assert := internal.NewAssert(t, "TestRetryFailed")
var number int
customError := errors.New("error occurs")
increaseNumber := func() error {
number++
return errors.New("error occurs")
return customError
}
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
assert.IsNotNil(err)
assert.Equal(errors.Is(err, customError), true)
assert.Equal(DefaultRetryTimes, number)
}

View File

@@ -59,19 +59,42 @@ func ContainSubSlice[T comparable](slice, subSlice []T) bool {
return false
}
elementMap := make(map[T]struct{}, len(slice))
elementCount := make(map[T]int, len(slice))
for _, item := range slice {
elementMap[item] = struct{}{}
elementCount[item]++
}
for _, item := range subSlice {
if _, ok := elementMap[item]; !ok {
if elementCount[item] == 0 {
return false
}
elementCount[item]--
}
return true
}
// ContainAny check if the slice contains any element from the targets slice.
// Play: https://go.dev/play/p/4xoxhc9XSSw
func ContainAny[T comparable](slice []T, targets []T) bool {
if len(targets) == 0 {
return false
}
sliceMap := make(map[T]struct{}, len(slice))
for _, item := range slice {
sliceMap[item] = struct{}{}
}
for _, target := range targets {
if _, exists := sliceMap[target]; exists {
return true
}
}
return false
}
// Chunk creates a slice of elements split into groups the length of size.
// Play: https://go.dev/play/p/b4Pou5j2L_C
func Chunk[T any](slice []T, size int) [][]T {
@@ -81,14 +104,18 @@ func Chunk[T any](slice []T, size int) [][]T {
return result
}
for _, item := range slice {
l := len(result)
if l == 0 || len(result[l-1]) == size {
result = append(result, []T{})
l++
}
currentChunk := []T{}
result[l-1] = append(result[l-1], item)
for _, item := range slice {
if len(currentChunk) == size {
result = append(result, currentChunk)
currentChunk = []T{}
}
currentChunk = append(currentChunk, item)
}
if len(currentChunk) > 0 {
result = append(result, currentChunk)
}
return result
@@ -106,6 +133,7 @@ func Compact[T comparable](slice []T) []T {
result = append(result, v)
}
}
return result[:len(result):len(result)]
}
@@ -133,8 +161,17 @@ func Concat[T any](slices ...[]T) []T {
func Difference[T comparable](slice, comparedSlice []T) []T {
result := []T{}
if len(slice) == 0 {
return result
}
comparedMap := make(map[T]struct{}, len(comparedSlice))
for _, v := range comparedSlice {
comparedMap[v] = struct{}{}
}
for _, v := range slice {
if !Contain(comparedSlice, v) {
if _, found := comparedMap[v]; !found {
result = append(result, v)
}
}
@@ -147,13 +184,17 @@ func Difference[T comparable](slice, comparedSlice []T) []T {
// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy.
// Play: https://go.dev/play/p/DiivgwM5OnC
func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(index int, item T) T) []T {
orginSliceAfterMap := Map(slice, iteratee)
comparedSliceAfterMap := Map(comparedSlice, iteratee)
result := make([]T, 0)
for i, v := range orginSliceAfterMap {
if !Contain(comparedSliceAfterMap, v) {
result = append(result, slice[i])
comparedMap := make(map[T]struct{}, len(comparedSlice))
for _, item := range comparedSlice {
comparedMap[iteratee(0, item)] = struct{}{}
}
for i, item := range slice {
transformedItem := iteratee(i, item)
if _, found := comparedMap[transformedItem]; !found {
result = append(result, item)
}
}
@@ -165,23 +206,32 @@ func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(inde
// The comparator is invoked with two arguments: (arrVal, othVal).
// Play: https://go.dev/play/p/v2U2deugKuV
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(item1, item2 T) bool) []T {
result := make([]T, 0)
getIndex := func(arr []T, item T, comparison func(v1, v2 T) bool) int {
index := -1
for i, v := range arr {
if comparison(item, v) {
index = i
return i
}
}
return -1
}
result := make([]T, 0, len(slice))
comparedMap := make(map[int]T, len(comparedSlice))
for _, v := range comparedSlice {
comparedMap[getIndex(comparedSlice, v, comparator)] = v
}
for _, v := range slice {
found := false
for _, existing := range comparedSlice {
if comparator(v, existing) {
found = true
break
}
}
return index
}
for i, v := range slice {
index := getIndex(comparedSlice, v, comparator)
if index == -1 {
result = append(result, slice[i])
if !found {
result = append(result, v)
}
}
@@ -220,6 +270,28 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
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.
// Play: https://go.dev/play/p/R8U6Sl-j8cD
func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
@@ -401,19 +473,20 @@ func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T,
// Flatten flattens slice with one level.
// Play: https://go.dev/play/p/hYa3cBEevtm
func Flatten(slice any) any {
sv := sliceValue(slice)
var result reflect.Value
if sv.Type().Elem().Kind() == reflect.Interface {
result = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, sv.Len())
} else if sv.Type().Elem().Kind() == reflect.Slice {
result = reflect.MakeSlice(sv.Type().Elem(), 0, sv.Len())
} else {
return result
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice {
panic("Flatten: input must be a slice")
}
elemType := sv.Type().Elem()
if elemType.Kind() == reflect.Slice {
elemType = elemType.Elem()
}
result := reflect.MakeSlice(reflect.SliceOf(elemType), 0, sv.Len())
for i := 0; i < sv.Len(); i++ {
item := reflect.ValueOf(sv.Index(i).Interface())
item := sv.Index(i)
if item.Kind() == reflect.Slice {
for j := 0; j < item.Len(); j++ {
result = reflect.Append(result, item.Index(j))
@@ -455,19 +528,17 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
}
// ForEach iterates over elements of slice and invokes function for each element.
// Play: https://go.dev/play/p/DrPaa4YsHRF
func ForEach[T any](slice []T, iteratee func(index int, item T)) {
for i := 0; i < len(slice); i++ {
iteratee(i, slice[i])
for idx, elem := range slice {
iteratee(idx, elem)
}
}
// ForEachWithBreak iterates over elements of slice and invokes function for each element,
// when iteratee return false, will break the for each loop.
// Play: https://go.dev/play/p/qScs39f3D9W
// when function return false, will break the for each loop.
func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) {
for i := 0; i < len(slice); i++ {
if !iteratee(i, slice[i]) {
for idx, elem := range slice {
if !iteratee(idx, elem) {
break
}
}
@@ -585,7 +656,7 @@ func Repeat[T any](item T, n int) []T {
}
// InterfaceSlice convert param to slice of interface.
// This function is deprecated, use generics feature of go1.18+ for replacement.
// deprecated: use generics feature of go1.18+ for replacement.
// Play: https://go.dev/play/p/FdQXF0Vvqs-
func InterfaceSlice(slice any) []any {
sv := sliceValue(slice)
@@ -602,7 +673,7 @@ func InterfaceSlice(slice any) []any {
}
// StringSlice convert param to slice of string.
// This function is deprecated, use generics feature of go1.18+ for replacement.
// deprecated: use generics feature of go1.18+ for replacement.
// Play: https://go.dev/play/p/W0TZDWCPFcI
func StringSlice(slice any) []string {
v := sliceValue(slice)
@@ -620,7 +691,7 @@ func StringSlice(slice any) []string {
}
// IntSlice convert param to slice of int.
// This function is deprecated, use generics feature of go1.18+ for replacement.
// deprecated: use generics feature of go1.18+ for replacement.
// Play: https://go.dev/play/p/UQDj-on9TGN
func IntSlice(slice any) []int {
sv := sliceValue(slice)
@@ -640,15 +711,23 @@ func IntSlice(slice any) []int {
// DeleteAt delete the element of slice at index.
// Play: https://go.dev/play/p/800B1dPBYyd
func DeleteAt[T any](slice []T, index int) []T {
if index >= len(slice) {
index = len(slice) - 1
result := append([]T(nil), slice...)
if index < 0 || index >= len(slice) {
return result[:len(slice)-1]
}
result := make([]T, len(slice)-1)
copy(result, slice[:index])
copy(result[index:], slice[index+1:])
copy(result[index:], result[index+1:])
return result
// Set the last element to zero value, clean up the memory.
result[len(result)-1] = zeroValue[T]()
return result[:len(result)-1]
}
func zeroValue[T any]() T {
var zero T
return zero
}
// DeleteRange delete the element of slice from start index to end indexexclude).
@@ -677,7 +756,8 @@ func Drop[T any](slice []T, n int) []T {
}
if n <= 0 {
return slice
result := make([]T, 0, size)
return append(result, slice...)
}
result := make([]T, 0, size-n)
@@ -695,7 +775,8 @@ func DropRight[T any](slice []T, n int) []T {
}
if n <= 0 {
return slice
result := make([]T, 0, size)
return append(result, slice...)
}
result := make([]T, 0, size-n)
@@ -741,49 +822,59 @@ func InsertAt[T any](slice []T, index int, value any) []T {
size := len(slice)
if index < 0 || index > size {
return slice
result := make([]T, size)
copy(result, slice)
return result
}
if v, ok := value.(T); ok {
slice = append(slice[:index], append([]T{v}, slice[index:]...)...)
return slice
switch v := value.(type) {
case T:
result := make([]T, size+1)
copy(result, slice[:index])
result[index] = v
copy(result[index+1:], slice[index:])
return result
case []T:
result := make([]T, size+len(v))
copy(result, slice[:index])
copy(result[index:], v)
copy(result[index+len(v):], slice[index:])
return result
default:
result := make([]T, size)
copy(result, slice)
return result
}
if v, ok := value.([]T); ok {
slice = append(slice[:index], append(v, slice[index:]...)...)
return slice
}
return slice
}
// UpdateAt update the slice element at index.
// Play: https://go.dev/play/p/f3mh2KloWVm
func UpdateAt[T any](slice []T, index int, value T) []T {
size := len(slice)
result := make([]T, len(slice))
copy(result, slice)
if index < 0 || index >= size {
return slice
if index >= 0 && index < len(slice) {
result[index] = value
}
slice = append(slice[:index], append([]T{value}, slice[index+1:]...)...)
return slice
return result
}
// Unique remove duplicate elements in slice.
// Play: https://go.dev/play/p/AXw0R3ZTE6a
func Unique[T comparable](slice []T) []T {
result := make([]T, 0, len(slice))
if len(slice) == 0 {
return slice
}
seen := make(map[T]struct{}, len(slice))
result := slice[:0]
for i := range slice {
if _, ok := seen[slice[i]]; ok {
continue
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
seen[slice[i]] = struct{}{}
result = append(result, slice[i])
}
return result
@@ -793,18 +884,19 @@ func Unique[T comparable](slice []T) []T {
// The function maintains the order of the elements.
// Play: https://go.dev/play/p/GY7JE4yikrl
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
result := make([]T, 0, len(slice))
if len(slice) == 0 {
return slice
}
seen := make(map[U]struct{}, len(slice))
result := slice[:0]
for i := range slice {
key := iteratee(slice[i])
if _, ok := seen[key]; ok {
continue
for _, item := range slice {
key := iteratee(item)
if _, exists := seen[key]; !exists {
seen[key] = struct{}{}
result = append(result, item)
}
seen[key] = struct{}{}
result = append(result, slice[i])
}
return result
@@ -814,19 +906,20 @@ func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
// The function maintains the order of the elements.
// Play: https://go.dev/play/p/rwSacr-ZHsR
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T {
result := make([]T, 0, len(slice))
seen := make([]T, 0, len(slice))
if len(slice) == 0 {
return slice
}
result := make([]T, 0, len(slice))
for _, item := range slice {
duplicate := false
for _, seenItem := range seen {
if comparator(item, seenItem) {
duplicate = true
isDuplicate := false
for _, existing := range result {
if comparator(item, existing) {
isDuplicate = true
break
}
}
if !duplicate {
seen = append(seen, item)
if !isDuplicate {
result = append(result, item)
}
}
@@ -952,7 +1045,9 @@ func SymmetricDifference[T comparable](slices ...[]T) []T {
return []T{}
}
if len(slices) == 1 {
return Unique(slices[0])
result := make([]T, len(slices[0]))
copy(result, slices[0])
return Unique(result)
}
result := make([]T, 0)
@@ -973,6 +1068,7 @@ func SymmetricDifference[T comparable](slices ...[]T) []T {
}
// Reverse return slice of element order is reversed to the given slice.
// Reverse modifies the slice in place.
// Play: https://go.dev/play/p/8uI8f1lwNrQ
func Reverse[T any](slice []T) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
@@ -980,18 +1076,45 @@ func Reverse[T any](slice []T) {
}
}
// Shuffle the slice.
// ReverseCopy return a new slice of element where the 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
func Shuffle[T any](slice []T) []T {
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(slice), func(i, j int) {
slice[i], slice[j] = slice[j], slice[i]
swap(slice, i, j)
})
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.
// Play: https://go.dev/play/p/9CtsFjet4SH
func IsAscending[T constraints.Ordered](slice []T) bool {
@@ -1145,11 +1268,12 @@ func SortByField[T any](slice []T, field string, sortType ...string) error {
// Without creates a slice excluding all given items.
// Play: https://go.dev/play/p/bwhEXEypThg
func Without[T comparable](slice []T, items ...T) []T {
result := make([]T, 0, len(slice))
if len(items) == 0 || len(slice) == 0 {
return slice
return append(result, slice...)
}
result := make([]T, 0, len(slice))
for _, v := range slice {
if !Contain(items, v) {
result = append(result, v)
@@ -1370,36 +1494,28 @@ func Random[T any](slice []T) (val T, idx int) {
return slice[idx], idx
}
// RightPadding adds padding to the right end of a slice.
// RightPadding returns a copy of the slice padding the given value to the right end of a slice.
// If paddingLength is zero or less, the function returns a copy of the slice.
// Play: https://go.dev/play/p/0_2rlLEMBXL
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
suffix := []T{}
if paddingLength > 0 {
suffix = repeat([]T{paddingValue}, paddingLength)
}
for i := 0; i < paddingLength; i++ {
slice = append(slice, paddingValue)
}
return slice
padded := concat(slice, suffix)
return padded
}
// LeftPadding adds padding to the left begin of a slice.
// LeftPadding returns a copy of the slice padding the given value to the left begin of a slice.
// If paddingLength is zero or less, the function returns a copy of the slice.
// Play: https://go.dev/play/p/jlQVoelLl2k
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
prefix := []T{}
if paddingLength > 0 {
prefix = repeat([]T{paddingValue}, paddingLength)
}
paddedSlice := make([]T, len(slice)+paddingLength)
i := 0
for ; i < paddingLength; i++ {
paddedSlice[i] = paddingValue
}
for j := 0; j < len(slice); j++ {
paddedSlice[i] = slice[j]
i++
}
return paddedSlice
padded := concat(prefix, slice)
return padded
}
// Frequency counts the frequency of each element in the slice.
@@ -1415,7 +1531,7 @@ func Frequency[T comparable](slice []T) map[T]int {
}
// 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 {
var buf strings.Builder
for i, v := range slice {
@@ -1428,7 +1544,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.
// Play: todo
// Play: https://go.dev/play/p/6QcUpcY4UMW
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T {
var result T

View File

@@ -125,6 +125,8 @@ func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T {
result := make([]T, 0)
var wg sync.WaitGroup
var mu sync.Mutex
workerChan := make(chan struct{}, numThreads)
@@ -137,7 +139,9 @@ func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool,
defer wg.Done()
if predicate(i, v) {
mu.Lock()
result = append(result, v)
mu.Unlock()
}
<-workerChan

Some files were not shown because too many files have changed in this diff Show More