diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..d8f0939e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms +liberapay: Duke_Du +patreon: DukeDu diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 9c657fb2..526728eb 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -3,11 +3,11 @@ on: push: branches: - main - # - v2 + - rc pull_request: branches: - main - # - v2 + - rc jobs: build: runs-on: ubuntu-latest @@ -17,8 +17,10 @@ jobs: fetch-depth: 2 - uses: actions/setup-go@v2 with: - go-version: "1.16" + go-version: "1.20" - name: Run coverage run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic + - name: Run govet + run: go vet -v ./... - name: Upload coverage to Codecov run: bash <(curl -s https://codecov.io/bash) diff --git a/.gitignore b/.gitignore index fce5a7c3..a4d5b55b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,10 @@ fileutil/*.txt fileutil/*.zip fileutil/*.link fileutil/unzip/* -cryptor/*.pem \ No newline at end of file +fileutil/tempdir/* +slice/testdata/* +# cryptor/*.pem +test +docs/node_modules +docs/.vitepress/cache +docs/.vitepress/dist diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md new file mode 100644 index 00000000..f16cfb2a --- /dev/null +++ b/CONTRIBUTION.md @@ -0,0 +1,37 @@ +# Lancet Contribution Guide + +Hi! Thank you for choosing Lancet. + +Lancet is a powerful, efficient, and reusable util function library of go. It makes Go dev easier by taking the hassle out of working with concurrency, net, math, slice, string, etc. + +We are excited that you are interested in contributing to lancet. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. + +## Issue Guidelines + +- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. + +- Before submitting an issue, please check if similar problems have already been issued. + +- Please specify which version of Lancet and Go you are using, and provide OS information. [Go Playground](https://go.dev/play/) is recommended to build a live demo so that your issue can be reproduced clearly. + +## Pull Request Guidelines + +- Fork this repository to your own account. Do not create branches here. + +- Commit info should be formatted as `type(scope): info about commit`. eg. `fix(package): [scrollbar] fix xxx bug`. + + 1. type: type must be one of [chore, docs, feat, fix, refactor, release, test]. + + 2. scope: scope must be one of [package, file, internal]. + + 3. header: header must not be longer than 72 characters. + +- Rebase before creating a PR to keep commit history clear. + +- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass. + +- Make sure PRs are created to `rc` branch instead of other branch. + +- If your PR fixes a bug, please provide a description about the related bug. + +- If the PR is for a new feature, make sure to complete the relevant documentation (/lancet/docs/en/api/packages). diff --git a/CONTRIBUTION.zh-CN.md b/CONTRIBUTION.zh-CN.md new file mode 100644 index 00000000..c2afda26 --- /dev/null +++ b/CONTRIBUTION.zh-CN.md @@ -0,0 +1,37 @@ +# Lancet 贡献指南 + +Hi! 首先感谢你使用 Lancet。 + +lancet(柳叶刀)是一个功能强大、全面、高效、可复用的go语言工具函数库。它消除了处理并发、网络、数学、切片、字符串等的麻烦,使 Go 开发变得更容易。 + +Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代码或提供建议,请阅读以下内容。 + +## Issue 规范 + +- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。 + +- 在提交 issue 之前,请搜索相关内容是否已被提出。 + +- 请说明 Lancet 和 Go 的版本号,并提供操作系统信息。推荐使用 [Go Playground](https://go.dev/play/) 生成在线 demo,这能够更直观地重现问题。 + +## Pull Request 规范 + +- 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。 + +- commit 信息要以 `type(scope): 描述信息` 的形式填写,例如 `fix(package): [scrollbar] fix xxx bug`。 + + 1. type: 必须是 chore, docs, feat, fix, refactor, release, test 其中的一个。 + + 2. scope: 必须是 package, file, internal 其中的一个。 + + 3. header: 描述信息不要超过 72 个字符。 + +- 提交 PR 前请 rebase,确保 commit 记录的整洁。 + +- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。 + +- 确保 PR 是提交到 `rc` 分支,而不是其他分支。 + +- 如果是修复 bug,请在 PR 中给出描述信息。 + +- 如果PR是新功能,确保完成相关文档(/lancet/docs/api/packages)。 \ No newline at end of file diff --git a/README.md b/README.md index b9ed128d..bf6d5df7 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,14 @@
-![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.2.6-green.svg)](https://github.com/duke-git/lancet/releases) -[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) -[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) +![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) +[![Release](https://img.shields.io/badge/release-2.3.7-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) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE) +[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Lancet%20Guru-006BFF)](https://gurubase.io/g/lancet) @@ -19,19 +20,29 @@ Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.

-English | [简体中文](./README_zh-CN.md) +## Website | [简体中文](./README_zh-CN.md) -## Feature +## Features -- 👏 Comprehensive, efficient and reusable. -- 💪 180+ go util functions, support string, slice, datetime, net, crypt... -- 💅 Only depend on the go standard library. -- 🌍 Unit test for every exported function. +- 👏 Comprehensive, efficient and reusable. +- 💪 700+ go util functions, support string, slice, datetime, net, crypt... +- 💅 Only depends on two kinds of libraries: go standard library and golang.org/x. +- 🌍 Unit test for every exported function. ## Installation +### Note: + +1. For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions were rewritten with generics of go1.18. + +```go +go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x +``` + +2. For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.6. + ```go -go get github.com/duke-git/lancet +go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x ``` ## Usage @@ -39,345 +50,2321 @@ go get github.com/duke-git/lancet Lancet organizes the code into package structure, and you need to import the corresponding package name when use it. For example, if you use string-related functions,import the strutil package like below: ```go -import "github.com/duke-git/lancet/strutil" +import "github.com/duke-git/lancet/v2/strutil" ``` ## Example -Here takes the string function ReverseStr (reverse order string) as an example, and the strutil package needs to be imported. +Here takes the string function Reverse (reverse order string) as an example, and the strutil package needs to be imported. ```go package main import ( "fmt" - "github.com/duke-git/lancet/strutil" + "github.com/duke-git/lancet/v2/strutil" ) func main() { s := "hello" - rs := strutil.ReverseStr(s) + rs := strutil.Reverse(s) fmt.Println(rs) //olleh } ``` -## API Documentation -### Convertor package contains some functions for data convertion. +## Documentation + +### Index + +- [Algorithm](#user-content-algorithm) +- [Compare](#user-content-compare) +- [Concurrency](#user-content-concurrency) +- [Condition](#user-content-condition) +- [Convertor](#user-content-convertor) +- [Cryptor](#user-content-cryptor) +- [Datetime](#user-content-datetime) +- [Datastructure](#user-content-datastructure) +- [Fileutil](#user-content-fileutil) +- [Formatter](#user-content-formatter) +- [Function](#user-content-function) +- [Maputil](#user-content-maputil) +- [Mathutil](#user-content-mathutil) +- [Netutil](#user-content-netutil) +- [Pointer](#user-content-pointer) +- [Random](#user-content-random) +- [Retry](#user-content-retry) +- [Slice](#user-content-slice) +- [Stream](#user-content-stream) +- [Structs](#user-content-structs) +- [Strutil](#user-content-strutil) +- [System](#user-content-system) +- [Tuple](#user-content-tuple) +- [Validator](#user-content-validator) +- [Xerror](#user-content-xerror) + +

1. Algorithm package implements some basic algorithm. eg. sort, search.        index

```go -import "github.com/duke-git/lancet/convertor" +import "github.com/duke-git/lancet/v2/algorithm" ``` + #### Function list: -- [ColorHexToRGB](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ColorHexToRGB) -- [ColorRGBToHex](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ColorRGBToHex) -- [ToBool](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToBool) -- [ToBytes](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToBytes) -- [ToChar](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToChar) -- [ToInt](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToInt) -- [ToJson](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToJson) -- [ToString](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#ToString) -- [StructToMap](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#StructToMap) - -### Cryptor package is for data encryption and decryption. + +- **BubbleSort** : sorts slice with bubble sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#BubbleSort)] + [[play](https://go.dev/play/p/GNdv7Jg2Taj)] +- **CountSort** : sorts slice with bubble sort algorithm, don't change original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#CountSort)] + [[play](https://go.dev/play/p/tB-Umgm0DrP)] +- **HeapSort** : sorts slice with heap sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#HeapSort)] + [[play](https://go.dev/play/p/u6Iwa1VZS_f)] +- **InsertionSort** : sorts slice with insertion sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#InsertionSort)] + [[play](https://go.dev/play/p/G5LJiWgJJW6)] +- **MergeSort** : sorts slice with merge sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#MergeSort)] + [[play](https://go.dev/play/p/ydinn9YzUJn)] +- **QuickSort** : sorts slice with quick sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#QuickSort)] + [[play](https://go.dev/play/p/7Y7c1Elk3ax)] +- **SelectionSort** : sorts slice with selection sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#SelectionSort)] + [[play](https://go.dev/play/p/oXovbkekayS)] +- **ShellSort** : sorts slice with shell sort algorithm, will change the original slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#ShellSort)] + [[play](https://go.dev/play/p/3ibkszpJEu3)] +- **BinarySearch** : returns the index of target within a sorted slice, use binary search (recursive call itself). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#BinarySearch)] + [[play](https://go.dev/play/p/t6MeGiUSN47)] +- **BinaryIterativeSearch** : returns the index of target within a sorted slice, use binary search (no recursive). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#BinaryIterativeSearch)] + [[play](https://go.dev/play/p/Anozfr8ZLH3)] +- **LinearSearch** : returns the index of target in slice base on equal function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#LinearSearch)] + [[play](https://go.dev/play/p/IsS7rgn5s3x)] +- **LRUCache** : implements memory cache with lru algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/algorithm.md#LRUCache)] + [[play](https://go.dev/play/p/-EZjgOURufP)] + +

2. Compare package provides a lightweight comparison function on any type.        index

```go -import "github.com/duke-git/lancet/cryptor" +import "github.com/duke-git/lancet/v2/compare" ``` #### Function list: -- [AesEcbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesEcbEncrypt) -- [AesEcbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesEcbDecrypt) -- [AesCbcEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesCbcEncrypt) -- [AesCbcDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesCbcDecrypt) -- [AesCtrCrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesCtrCrypt) -- [AesCfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesCfbEncrypt) -- [AesCfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesCfbDecrypt) -- [AesOfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesOfbEncrypt) -- [AesOfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#AesOfbDecrypt) -- [Base64StdEncode](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Base64StdEncode) -- [Base64StdDecode](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Base64StdDecode) -- [DesEcbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesEcbEncrypt) -- [DesEcbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesEcbDecrypt) -- [DesCbcEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesCbcEncrypt) -- [DesCbcDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesCbcDecrypt) -- [DesCtrCrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesCtrCrypt) -- [DesCfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesCfbEncrypt) -- [DesCfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesCfbDecrypt) -- [DesOfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesOfbEncrypt) -- [DesOfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#DesOfbDecrypt) -- [HmacMd5](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#HmacMd5) -- [HmacSha1](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#HmacSha1) -- [HmacSha256](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#HmacSha256) -- [HmacSha512](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#HmacSha512) -- [Md5String](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Md5String) -- [Md5File](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Md5File) -- [Sha1](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Sha1) -- [Sha256](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Sha256) -- [Sha512](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#Sha512) -- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#GenerateRsaKey) -- [RsaEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#RsaEncrypt) -- [RsaDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor.md#RsaDecrypt) - -### Datetime package supports date and time format and compare. +- **Equal** : Checks if two values are equal or not. (check both type and value) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#Equal)] + [[play](https://go.dev/play/p/wmVxR-to4lz)] +- **EqualValue** : Checks if two values are equal or not. (check value only) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#EqualValue)] + [[play](https://go.dev/play/p/fxnna_LLD9u)] +- **LessThan** : Checks if value `left` less than value `right`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#LessThan)] + [[play](https://go.dev/play/p/cYh7FQQj0ne)] +- **GreaterThan** : Checks if value `left` greater than value `right`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#GreaterThan)] + [[play](https://go.dev/play/p/9-NYDFZmIMp)] +- **LessOrEqual** : Checks if value `left` less than or equal than value `right`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#LessOrEqual)] + [[play](https://go.dev/play/p/e4T_scwoQzp)] +- **GreaterOrEqual** : Checks if value `left` less greater or equal than value `right`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#GreaterOrEqual)] + [[play](https://go.dev/play/p/vx8mP0U8DFk)] +- **InDelta** : Checks if two values are equal or not within a delta. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/compare.md#InDelta)] + +

3. Concurrency package contain some functions to support concurrent programming. eg, goroutine, channel, async.        index

```go -import "github.com/duke-git/lancet/datetime" +import "github.com/duke-git/lancet/v2/concurrency" ``` + #### Function list: -- [AddDay](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#AddDay) -- [AddHour](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#AddHour) -- [AddMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#AddMinute) -- [BeginOfMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfMinute) -- [BeginOfHour](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfHour) -- [BeginOfDay](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfDay) -- [BeginOfWeek](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfWeek) -- [BeginOfMonth](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfMonth) -- [BeginOfYear](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfYear) -- [EndOfMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfMinute) -- [EndOfHour](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfHour) -- [EndOfDay](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfDay) -- [EndOfWeek](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfWeek) -- [EndOfMonth](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfMonth) -- [EndOfYear](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfYear) -- [GetNowDate](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#GetNowDate) -- [GetNowTime](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#GetNowTime) -- [GetNowDateTime](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#GetNowDateTime) -- [GetZeroHourTimestamp](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#GetZeroHourTimestamp) -- [GetNightTimestamp](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#GetNightTimestamp) -- [FormatTimeToStr](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#FormatTimeToStr) -- [FormatStrToTime](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#FormatStrToTime) - -### Fileutil package implements some basic functions for file operations. + +- **NewChannel** : create a Channel pointer instance. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#NewChannel)] + [[play](https://go.dev/play/p/7aB4KyMMp9A)] +- **Bridge** : link multiply channels into one channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/Bridge.md#NewChannel)] + [[play](https://go.dev/play/p/qmWSy1NVF-Y)] +- **FanIn** : merge multiple channels into one channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#FanIn)] + [[play](https://go.dev/play/p/2VYFMexEvTm)] +- **Generate** : creates a channel, then put values into the channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Generate)] + [[play](https://go.dev/play/p/7aB4KyMMp9A)] +- **Or** : read one or more channels into one channel, will close when any readin channel is closed. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Or)] + [[play](https://go.dev/play/p/Wqz9rwioPww)] +- **OrDone** : read a channel into another channel, will close until cancel context. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#OrDone)] + [[play](https://go.dev/play/p/lm_GoS6aDjo)] +- **Repeat** : create channel, put values into the channel repeatedly until cancel the context. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Repeat)] + [[play](https://go.dev/play/p/k5N_ALVmYjE)] +- **RepeatFn** : create a channel, executes fn repeatedly, and put the result into the channel, until close context. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RepeatFn)] + [[play](https://go.dev/play/p/4J1zAWttP85)] +- **Take** : create a channel whose values are taken from another channel with limit number. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Take)] + [[play](https://go.dev/play/p/9Utt-1pDr2J)] +- **Tee** : 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)] +- **NewKeyedLocker** : 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)] +- **Do** :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)] +- **NewRWKeyedLocker** :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)] +- **RLock** : 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)] +- **Lock** : 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)] +- **NewTryKeyedLocker** : 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)] +- **TryLock** : 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)] +- **Unlock** : 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)] + +

4. Condition package contains some functions for conditional judgment. eg. And, Or, TernaryOperator...       index

+ +```go +import "github.com/duke-git/lancet/v2/condition" +``` + +#### Function list: + +- **Bool** : returns the truthy value of anything. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Bool)] + [[play](https://go.dev/play/p/ETzeDJRSvhm)] +- **And** : returns true if both a and b are truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#And)] + [[play](https://go.dev/play/p/W1SSUmt6pvr)] +- **Or** : returns false if neither a nor b is truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Or)] + [[play](https://go.dev/play/p/UlQTxHaeEkq)] +- **Xor** : returns true if a or b but not both is truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Xor)] + [[play](https://go.dev/play/p/gObZrW7ZbG8)] +- **Nor** : returns true if neither a nor b is truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Nor)] + [[play](https://go.dev/play/p/g2j08F_zZky)] +- **Xnor** : returns true if both a and b or neither a nor b are truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Xnor)] + [[play](https://go.dev/play/p/OuDB9g51643)] +- **Nand** : returns false if both a and b are truthy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#Nand)] + [[play](https://go.dev/play/p/vSRMLxLIbq8)] +- **TernaryOperator** : ternary operator. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#TernaryOperator)] + [[play](https://go.dev/play/p/ElllPZY0guT)] + +

5. Convertor package contains some functions for data conversion.        index

```go -import "github.com/duke-git/lancet/fileutil" +import "github.com/duke-git/lancet/v2/convertor" +``` + +#### Function list: + +- **ColorHexToRGB** : convert color hex to color rgb. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ColorHexToRGB)] + [[play](https://go.dev/play/p/o7_ft-JCJBV)] +- **ColorRGBToHex** : convert rgb color to hex color. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ColorRGBToHex)] + [[play](https://go.dev/play/p/nzKS2Ro87J1)] +- **ToBool** : convert string to bool. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToBool)] + [[play](https://go.dev/play/p/ARht2WnGdIN)] +- **ToBytes** : convert value to byte slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToBytes)] + [[play](https://go.dev/play/p/fAMXYFDvOvr)] +- **ToChar** : convert string to char slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToChar)] + [[play](https://go.dev/play/p/JJ1SvbFkVdM)] +- **ToChannel** : convert a collection of elements to a read-only channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToChannel)] + [[play](https://go.dev/play/p/hOx_oYZbAnL)] +- **ToFloat** : convert value to float64, if param is a invalid floatable, will return 0.0 and error. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToFloat)] + [[play](https://go.dev/play/p/4YTmPCibqHJ)] +- **ToInt** : convert value to int64 value, if input is not numerical, return 0 and error. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToInt)] + [[play](https://go.dev/play/p/9_h9vIt-QZ_b)] +- **ToJson** : convert value to a json string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToJson)] + [[play](https://go.dev/play/p/2rLIkMmXWvR)] +- **ToMap** : convert a slice of structs to a map based on iteratee function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToMap)] + [[play](https://go.dev/play/p/tVFy7E-t24l)] +- **ToPointer** : 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)] +- **ToString** : 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)] +- **StructToMap** : convert struct to map, only convert exported struct field. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#StructToMap)] + [[play](https://go.dev/play/p/KYGYJqNUBOI)] +- **MapToSlice** : convert map to slice based on iteratee function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#MapToSlice)] + [[play](https://go.dev/play/p/dmX4Ix5V6Wl)] +- **EncodeByte** : encode data to byte slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#EncodeByte)] + [[play](https://go.dev/play/p/DVmM1G5JfuP)] +- **DecodeByte** : decode byte slice data to target object. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#DecodeByte)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **DeepClone** : creates a deep copy of passed item, can't clone unexported field of struct. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#DeepClone)] + [[play](https://go.dev/play/p/j4DP5dquxnk)] +- **CopyProperties** : copies each field from the source struct into the destination struct. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#CopyProperties)] + [[play](https://go.dev/play/p/oZujoB5Sgg5)] +- **ToInterface** : converts reflect value to its interface type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToInterface)] + [[play](https://go.dev/play/p/syqw0-WG7Xd)] +- **Utf8ToGbk** : converts utf8 encoding data to GBK encoding data + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#Utf8ToGbk)] + [[play](https://go.dev/play/p/9FlIaFLArIL)] +- **GbkToUtf8** : converts GBK encoding data to utf8 encoding data. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)] + [[play](https://go.dev/play/p/OphmHCN_9u8)] +- **ToStdBase64** : 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)] +- **ToUrlBase64** : 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)] +- **ToRawStdBase64** : 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)] +- **ToRawUrlBase64** : 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)] +- **ToBigInt** : 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)] + +

6. Cryptor package is for data encryption and decryption.       index

+ +```go +import "github.com/duke-git/lancet/v2/cryptor" +``` + +#### Function list: + +- **AesEcbEncrypt** : encrypt byte slice data with key use AES ECB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesEcbEncrypt)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **AesEcbDecrypt** : decrypt byte slice data with key use AES ECB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesEcbDecrypt)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **AesCbcEncrypt** : encrypt byte slice data with key use AES CBC algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesCbcEncrypt)] + [[play](https://go.dev/play/p/IOq_g8_lKZD)] +- **AesCbcDecrypt** : decrypt byte slice data with key use AES CBC algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesCbcDecrypt)] + [[play](https://go.dev/play/p/IOq_g8_lKZD)] +- **AesCtrCrypt** : encrypt/ decrypt byte slice data with key use AES CRC algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesCtrCrypt)] + [[play](https://go.dev/play/p/SpaZO0-5Nsp)] +- **AesCfbEncrypt** : encrypt byte slice data with key use AES CFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesCfbEncrypt)] + [[play](https://go.dev/play/p/tfkF10B13kH)] +- **AesCfbDecrypt** : decrypt byte slice data with key use AES CFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesCfbDecrypt)] + [[play](https://go.dev/play/p/tfkF10B13kH)] +- **AesOfbEncrypt** : encrypt byte slice data with key use AES OFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesOfbEncrypt)] + [[play](https://go.dev/play/p/VtHxtkUj-3F)] +- **AesOfbDecrypt** : decrypt byte slice data with key use AES OFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesOfbDecrypt)] + [[play](https://go.dev/play/p/VtHxtkUj-3F)] +- **AesGcmEncrypt** : encrypt byte slice data with key use AES GCM algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmEncrypt)] + [[play](https://go.dev/play/p/rUt0-DmsPCs)] +- **AesGcmDecrypt** : decrypt byte slice data with key use AES GCM algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmDecrypt)] + [[play](https://go.dev/play/p/rUt0-DmsPCs)] +- **Base64StdEncode** : encode string with base64 encoding. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Base64StdEncode)] + [[play](https://go.dev/play/p/VOaUyQUreoK)] +- **Base64StdDecode** : decode string with base64 encoding. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Base64StdDecode)] + [[play](https://go.dev/play/p/RWQylnJVgIe)] +- **DesEcbEncrypt** : encrypt byte slice data with key use DES ECB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesEcbEncrypt)] + [[play](https://go.dev/play/p/8qivmPeZy4P)] +- **DesEcbDecrypt** : decrypt byte slice data with key use DES ECB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesEcbDecrypt)] + [[play](https://go.dev/play/p/8qivmPeZy4P)] +- **DesCbcEncrypt** : encrypt byte slice data with key use DES CBC algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesCbcEncrypt)] + [[play](https://go.dev/play/p/4cC4QvWfe3_1)] +- **DesCbcDecrypt** : decrypt byte slice data with key use DES CBC algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesCbcDecrypt)] + [[play](https://go.dev/play/p/4cC4QvWfe3_1)] +- **DesCtrCrypt** : encrypt/decrypt byte slice data with key use DES CRY algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesCtrCrypt)] + [[play](https://go.dev/play/p/9-T6OjKpcdw)] +- **DesCfbEncrypt** : encrypt byte slice data with key use DES CFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesCfbEncrypt)] + [[play](https://go.dev/play/p/y-eNxcFBlxL)] +- **DesCfbDecrypt** : decrypt byte slice data with key use DES CFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesCfbDecrypt)] + [[play](https://go.dev/play/p/y-eNxcFBlxL)] +- **DesOfbEncrypt** : encrypt byte slice data with key use DES OFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesOfbEncrypt)] + [[play](https://go.dev/play/p/74KmNadjN1J)] +- **DesOfbDecrypt** : decrypt byte slice data with key use DES OFB algorithm. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#DesOfbDecrypt)] + [[play](https://go.dev/play/p/74KmNadjN1J)] +- **HmacMd5** : return the md5 hmac hash of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacMd5)] + [[play](https://go.dev/play/p/uef0q1fz53I)] +- **HmacMd5WithBase64** : return the md5 hmac hash of base64 string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacMd5WithBase64)] +- **HmacSha1** : return the hmac hash of string use sha1. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha1)] + [[play](https://go.dev/play/p/1UI4oQ4WXKM)] +- **HmacSha1WithBase64** : return the hmac hash of string use sha1 with base64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha1WithBase64)] + [[play](https://go.dev/play/p/47JmmGrnF7B)] +- **HmacSha256** : return the hmac hash of string use sha256. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha256)] + [[play](https://go.dev/play/p/HhpwXxFhhC0)] +- **HmacSha256WithBase64** : return the hmac hash of string use sha256 with base64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha256WithBase64)] + [[play](https://go.dev/play/p/EKbkUvPTLwO)] +- **HmacSha512** : return the hmac hash of string use sha512. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha512)] + [[play](https://go.dev/play/p/59Od6m4A0Ud)] +- **HmacSha512WithBase64** : return the hmac hash of string use sha512 with base64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#HmacSha512WithBase64)] + [[play](https://go.dev/play/p/c6dSe3E2ydU)] +- **Md5Byte** : return the md5 string of byte slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Md5Byte)] + [[play](https://go.dev/play/p/suraalH8lyC)] +- **Md5ByteWithBase64** : return the md5 string of byte slice with base64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Md5ByteWithBase64)] + [[play](https://go.dev/play/p/Tcb-Z7LN2ax)] +- **Md5String** : return the md5 value of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Md5String)] + [[play](https://go.dev/play/p/1bLcVetbTOI)] +- **Md5StringWithBase64** : return the md5 value of string with base64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Md5StringWithBase64)] + [[play](https://go.dev/play/p/Lx4gH7Vdr5_y)] +- **Md5File** : return the md5 value of file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Md5File)] +- **Sha1** : return the sha1 value (SHA-1 hash algorithm) of base64 string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha1)] + [[play](https://go.dev/play/p/_m_uoD1deMT)] +- **Sha1WithBase64** : return the sha1 value (SHA-1 hash algorithm) of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha1WithBase64)] + [[play](https://go.dev/play/p/fSyx-Gl2l2-)] +- **Sha256** : return the sha256 value (SHA-256 hash algorithm) of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha256)] + [[play](https://go.dev/play/p/tU9tfBMIAr1)] +- **Sha256WithBase64** : return the sha256 value (SHA256 hash algorithm) of base64 string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha256WithBase64)] + [[play](https://go.dev/play/p/85IXJHIal1k)] +- **Sha512** : return the sha512 value (SHA-512 hash algorithm) of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha512)] + [[play](https://go.dev/play/p/3WsvLYZxsHa)] +- **Sha512WithBase64** : return the sha512 value (SHA-512 hash algorithm) of base64 string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sha512WithBase64)] + [[play](https://go.dev/play/p/q_fY2rA-k5I)] +- **GenerateRsaKey** : create rsa private and public pemo file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateRsaKey)] + [[play](https://go.dev/play/p/zutRHrDqs0X)] +- **RsaEncrypt** : 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/7_zo6mrx-eX)] +- **RsaDecrypt** : 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/7_zo6mrx-eX)] +- **GenerateRsaKeyPair** : 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)] +- **RsaEncryptOAEP** : encrypts the given data with RSA-OAEP. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaEncryptOAEP)] + [[play](https://go.dev/play/p/sSVmkfENKMz)] +- **RsaDecryptOAEP** : 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)] +- **RsaSign** : 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)] +- **RsaVerifySign** : 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)] + +

7. Datetime package supports date and time format and compare.        index

+ +```go +import "github.com/duke-git/lancet/v2/datetime" +``` + +#### Function list: + +- **AddDay** : add or sub day to the time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddDay)] + [[play](https://go.dev/play/p/dIGbs_uTdFa)] +- **AddHour** : add or sub day to the time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddHour)] + [[play](https://go.dev/play/p/rcMjd7OCsi5)] +- **AddMinute** : 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)] +- **AddWeek** : 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)] +- **AddMonth** : 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)] +- **AddYear** : 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)] +- **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. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddDaySafe)] + [[play](https://go.dev/play/p/JTohZFpoDJ3)] +- **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. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddMonthSafe)] + [[play](https://go.dev/play/p/KLw0lo6mbVW)] +- **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. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#AddYearSafe)] + [[play](https://go.dev/play/p/KVGXWZZ54ZH)] +- **BeginOfMinute** : 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)] +- **BeginOfHour** : return the date time at the begin of hour of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfHour)] + [[play](https://go.dev/play/p/GhdGFnDWpYs)] +- **BeginOfDay** : return the date time at the begin of day of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfDay)] + [[play](https://go.dev/play/p/94m_UT6cWs9)] +- **BeginOfWeek** : 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/DCHdcL6gnfV)] +- **BeginOfMonth** : 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)] +- **BeginOfYear** : return the date time at the begin of year of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BeginOfYear)] + [[play](https://go.dev/play/p/i326DSwLnV8)] +- **EndOfMinute** : return the date time at the end of minute of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfMinute)] + [[play](https://go.dev/play/p/yrL5wGzPj4z)] +- **EndOfHour** : return the date time at the end of hour of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfHour)] + [[play](https://go.dev/play/p/6ce3j_6cVqN)] +- **EndOfDay** : return the date time at the end of day of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfDay)] + [[play](https://go.dev/play/p/eMBOvmq5Ih1)] +- **EndOfWeek** : 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/mGSA162YgX9)] +- **EndOfMonth** : 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)] +- **EndOfYear** : return the date time at the end of year of specific date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#EndOfYear)] + [[play](https://go.dev/play/p/G01cKlMCvNm)] +- **GetNowDate** : return format yyyy-mm-dd of current date. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetNowDate)] + [[play](https://go.dev/play/p/PvfkPpcpBBf)] +- **GetNowTime** : return format hh-mm-ss of current time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetNowTime)] + [[play](https://go.dev/play/p/l7BNxCkTmJS)] +- **GetNowDateTime** : return format yyyy-mm-dd hh-mm-ss of current datetime. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetNowDateTime)] + [[play](https://go.dev/play/p/pI4AqngD0al)] +- **GetTodayStartTime** : return the start time of today, format: yyyy-mm-dd 00:00:00. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetTodayStartTime)] + [[play](https://go.dev/play/p/84siyYF7t99)] +- **GetTodayEndTime** : return the end time of today, format: yyyy-mm-dd 23:59:59. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetTodayEndTime)] + [[play](https://go.dev/play/p/jjrLnfoqgn3)] +- **GetZeroHourTimestamp** : return timestamp of zero hour (timestamp of 00:00). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetZeroHourTimestamp)] + [[play](https://go.dev/play/p/QmL2oIaGE3q)] +- **GetNightTimestamp** : return timestamp of zero hour (timestamp of 23:59). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GetNightTimestamp)] + [[play](https://go.dev/play/p/UolysR3MYP1)] +- **FormatTimeToStr** : convert time to string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#FormatTimeToStr)] + [[play](https://go.dev/play/p/_Ia7M8H_OvE)] +- **FormatStrToTime** : convert string to time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#FormatStrToTime)] + [[play](https://go.dev/play/p/1h9FwdU8ql4)] +- **NewUnix** : return unix timestamp of specific time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#NewUnix)] + [[play](https://go.dev/play/p/psoSuh_kLRt)] +- **NewUnixNow** : return unix timestamp of current time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#NewUnixNow)] + [[play](https://go.dev/play/p/U4PPx-9D0oz)] +- **NewFormat** : return unix timestamp of specific time string, t should be "yyyy-mm-dd hh:mm:ss". + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#NewFormat)] + [[play](https://go.dev/play/p/VkW08ZOaXPZ)] +- **NewISO8601** : return unix timestamp of specific iso8601 time string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#NewISO8601)] + [[play](https://go.dev/play/p/mkhOHQkdeA2)] +- **ToUnix** : return unix timestamp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#ToUnix)] + [[play](https://go.dev/play/p/_LUiwAdocjy)] +- **ToFormat** : return the time string 'yyyy-mm-dd hh:mm:ss' of unix time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#ToFormat)] + [[play](https://go.dev/play/p/VkW08ZOaXPZ)] +- **ToFormatForTpl** : return the time string which format is specific tpl. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#ToFormatForTpl)] + [[play](https://go.dev/play/p/nyXxXcQJ8L5)] +- **ToIso8601** : return iso8601 time string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#ToIso8601)] + [[play](https://go.dev/play/p/mkhOHQkdeA2)] +- **IsLeapYear** : check if param `year` is leap year or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#IsLeapYear)] + [[play](https://go.dev/play/p/xS1eS2ejGew)] +- **BetweenSeconds** : returns the number of seconds between two times. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#BetweenSeconds)] + [[play](https://go.dev/play/p/n3YDRyfyXJu)] +- **DayOfYear** : returns which day of the year the parameter date `t` is. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#DayOfYear)] + [[play](https://go.dev/play/p/0hjqhTwFNlH)] +- **IsWeekend** : checks if passed time is weekend or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#IsWeekend)] + [[play](https://go.dev/play/p/cupRM5aZOIY)] +- **NowDateOrTime** : returns current datetime with specific format and timezone. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#NowDateOrTime)] + [[play](https://go.dev/play/p/EZ-begEjtT0)] +- **Timestamp** : returns current second timestamp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#Timestamp)] +- **TimestampMilli** : returns current mill second timestamp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampMilli)] + [[play](https://go.dev/play/p/4gvEusOTu1T)] +- **TimestampMicro** : returns current micro second timestamp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampMicro)] + [[play](https://go.dev/play/p/2maANglKHQE)] +- **TimestampNano** : returns current nano second timestamp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)] + [[play](https://go.dev/play/p/A9Oq_COrcCF)] +- **TrackFuncTime** : tracks function execution time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TrackFuncTime)] + [[play](https://go.dev/play/p/QBSEdfXHPTp)] +- **DaysBetween** : returns the number of days between two times. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#DaysBetween)] + [[play](https://go.dev/play/p/qD6qGb3TbOy)] +- **GenerateDatetimesBetween** : 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)] +- **Min** : 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)] +- **Max** : 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-)] +- **MaxMin** : 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)] + +

8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph.        index

+ +```go +import list "github.com/duke-git/lancet/v2/datastructure/list" +import copyonwritelist "github.com/duke-git/lancet/v2/datastructure/copyonwritelist" +import link "github.com/duke-git/lancet/v2/datastructure/link" +import stack "github.com/duke-git/lancet/v2/datastructure/stack" +import queue "github.com/duke-git/lancet/v2/datastructure/queue" +import set "github.com/duke-git/lancet/v2/datastructure/set" +import tree "github.com/duke-git/lancet/v2/datastructure/tree" +import heap "github.com/duke-git/lancet/v2/datastructure/heap" +import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +import optional "github.com/duke-git/lancet/v2/datastructure/optional" +``` + +#### Structure list: + +- **List** : a linear table, implemented with slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/list.md)] +- **CopyOnWriteList** : a thread-safe list implementation that uses go slicing as its base. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/copyonwritelist.md)] +- **Link** : link list structure, contains singly link and doubly link. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/link.md)] +- **Stack** : stack structure(fifo), contains array stack and link stack. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/stack.md)] +- **Queue** : queue structure(filo), contains array queue, circular queue, link queue and priority queue. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/queue.md)] +- **Set** : a data container, like slice, but element of set is not duplicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/set.md)] +- **Tree** : binary search tree structure. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/tree.md)] +- **Heap** : a binary max heap. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)] +- **Hashmap** : hash map structure. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)] +- **Optional** : Optional container. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)] + +

9. EventBus is an event bus used for handling events within an application.        Index

+ +```go +import "github.com/duke-git/lancet/v2/eventbus" +``` + +#### 函数列表: + +- **NewEventBus** : 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)] +- **Subscribe** : 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-)] +- **Unsubscribe** : 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)] +- **Publish** : 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)] +- **ClearListeners** : 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)] +- **ClearListenersByTopic** : 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)] +- **GetListenersCount** : 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)] +- **GetAllListenersCount** : 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)] +- **GetEvents** : 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)] +- **SetErrorHandler** : 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)] + +

9. Fileutil package implements some basic functions for file operations.        index

+ +```go +import "github.com/duke-git/lancet/v2/fileutil" ``` #### Function list: -- [ClearFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#ClearFile) -- [CreateFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#CreateFile) -- [CopyFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#CopyFile) -- [FileMode](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#FileMode) -- [MiMeType](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#MiMeType) -- [IsExist](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#IsExist) -- [IsLink](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#IsLink) -- [IsDir](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#IsDir) -- [ListFileNames](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#ListFileNames) -- [RemoveFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#RemoveFile) -- [ReadFileToString](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#ReadFileToString) -- [ReadFileByLine](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#ReadFileByLine) -- [Zip](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#Zip) -- [UnZip](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#UnZip) - -### Formatter contains some functions for data formatting. +- **ClearFile** : write empty string to target file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ClearFile)] + [[play](https://go.dev/play/p/NRZ0ZT-G94H)] +- **CreateFile** : create file in path. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateFile)] + [[play](https://go.dev/play/p/lDt8PEsTNKI)] +- **CreateDir** : create directory in absolute path. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateDir)] + [[play](https://go.dev/play/p/qUuCe1OGQnM)] +- **CopyFile** : copy src file to dest file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyFile)] + [[play](https://go.dev/play/p/Jg9AMJMLrJi)] +- **CopyDir** : copy src directory to dest directory. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyDir)] + [[play](https://go.dev/play/p/YAyFTA_UuPb)] +- **FileMode** : return file's mode and permission. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileMode)] + [[play](https://go.dev/play/p/2l2hI42fA3p)] +- **MiMeType** : return file mime type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#MiMeType)] + [[play](https://go.dev/play/p/bd5sevSUZNu)] +- **IsExist** : checks if a file or directory exists. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#IsExist)] + [[play](https://go.dev/play/p/nKKXt8ZQbmh)] +- **IsLink** : checks if a file is symbol link or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#IsLink)] + [[play](https://go.dev/play/p/TL-b-Kzvf44)] +- **IsDir** : checks if the path is directory or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#IsDir)] + [[play](https://go.dev/play/p/WkVwEKqtOWk)] +- **ListFileNames** : return all file names in the path. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ListFileNames)] + [[play](https://go.dev/play/p/Tjd7Y07rejl)] +- **RemoveFile** : 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)] +- **RemoveDir** : 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)] +- **ReadFileToString** : 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)] +- **ReadFileByLine** : read file line by line, return string slice of file content. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFileByLine)] + [[play](https://go.dev/play/p/svJP_7ZrBrD)] +- **Zip** : create a zip file of fpath, fpath could be a file or a directory. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#Zip)] + [[play](https://go.dev/play/p/j-3sWBp8ik_P)] +- **ZipAppendEntry** : append a single file or directory by fpath to an existing zip file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ZipAppendEntry)] +- **UnZip** : unzip the zip file and save it to dest path. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#UnZip)] + [[play](https://go.dev/play/p/g0w34kS7B8m)] +- **CurrentPath** : return current absolute path. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CurrentPath)] + [[play](https://go.dev/play/p/s74a9iBGcSw)] +- **IsZipFile** : checks if file is zip file or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#IsZipFile)] + [[play](https://go.dev/play/p/9M0g2j_uF_e)] +- **FileSize** : return file size in bytes. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileSize)] + [[play](https://go.dev/play/p/H9Z05uD-Jjc)] +- **MTime** : return file modified time(unix timestamp). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#MTime)] + [[play](https://go.dev/play/p/s_Tl7lZoAaY)] +- **Sha** : return file sha value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#Sha)] + [[play](https://go.dev/play/p/VfEEcO2MJYf)] +- **ReadCsvFile** : read file content into slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadCsvFile)] + [[play](https://go.dev/play/p/OExTkhGEd3_u)] +- **WriteCsvFile** : write content to target csv file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)] +- **WriteMapsToCsv** : write slice of map to csv file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)] + [[play](https://go.dev/play/p/umAIomZFV1c)] +- **WriteBytesToFile** : write bytes to target file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)] + [[play](https://go.dev/play/p/s7QlDxMj3P8)] +- **WriteStringToFile** : write string to target file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteStringToFile)] + [[play](https://go.dev/play/p/GhLS6d8lH_g)] +- **ReadFile** : read file or url. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)] +- **ChunkRead** : reads a block from the file at the specified offset and returns all lines within the block. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ChunkRead)] + [[play](https://go.dev/play/p/r0hPmKWhsgf)] +- **ParallelChunkRead** : 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)] +- **GetExeOrDllVersion** : 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)] + +

10. Formatter contains some functions for data formatting.        index

```go -import "github.com/duke-git/lancet/formatter" +import "github.com/duke-git/lancet/v2/formatter" ``` + #### Function list: -- [Comma](https://github.com/duke-git/lancet/blob/main/docs/formatter.md#Comma) -### Function package can control the flow of function execution and support part of functional programming +- **Comma** : add comma to a number value by every 3 numbers from right, ahead by a prefix symbol char. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#Comma)] + [[play](https://go.dev/play/p/eRD5k2vzUVX)] +- **Pretty** : pretty print data to JSON string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#Pretty)] + [[play](https://go.dev/play/p/YsciGj3FH2x)] +- **PrettyToWriter** : pretty encode data to writer. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#PrettyToWriter)] + [[play](https://go.dev/play/p/LPLZ3lDi5ma)] +- **DecimalBytes** : returns a human readable byte size under decimal standard (base 1000). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#DecimalBytes)] + [[play](https://go.dev/play/p/FPXs1suwRcs)] +- **BinaryBytes** : returns a human-readable byte size under binary standard (base 1024). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#BinaryBytes)] + [[play](https://go.dev/play/p/G9oHHMCAZxP)] +- **ParseDecimalBytes** : return the human readable bytes size string into the amount it represents(base 1000). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#ParseDecimalBytes)] + [[play](https://go.dev/play/p/Am98ybWjvjj)] +- **ParseBinaryBytes** : return the human readable bytes size string into the amount it represents(base 1024). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#ParseBinaryBytes)] + [[play](https://go.dev/play/p/69v1tTT62x8)] + +

11. Function package can control the flow of function execution and support part of functional programming.       index

```go -import "github.com/duke-git/lancet/function" +import "github.com/duke-git/lancet/v2/function" ``` #### Function list: -- [After](https://github.com/duke-git/lancet/blob/main/docs/function.md#After) -- [Before](https://github.com/duke-git/lancet/blob/main/docs/function.md#Before) -- [Curry](https://github.com/duke-git/lancet/blob/main/docs/function.md#Curry) -- [Compose](https://github.com/duke-git/lancet/blob/main/docs/function.md#Compose) -- [Debounced](https://github.com/duke-git/lancet/blob/main/docs/function.md#Debounced) -- [Delay](https://github.com/duke-git/lancet/blob/main/docs/function.md#Delay) -- [Watcher](https://github.com/duke-git/lancet/blob/main/docs/function.md#Watcher) +- **After** : return a function that invokes passed function once the returned function is called more than n times. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#After)] + [[play](https://go.dev/play/p/eRD5k2vzUVX)] +- **Before** : return a function that invokes passed function once the returned function is called less than n times + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Before)] + [[play](https://go.dev/play/p/0HqUDIFZ3IL)] +- **CurryFn** : make a curry function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#CurryFn)] + [[play](https://go.dev/play/p/5HopfDwANKX)] +- **Compose** : compose the functions from right to left. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Compose)] + [[play](https://go.dev/play/p/KKfugD4PKYF)] +- **Delay** : call the function after delayed time. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Delay)] + [[play](https://go.dev/play/p/Ivtc2ZE-Tye)] +- **Debounceddeprecated** : creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounced)] + [[play](https://go.dev/play/p/absuEGB_GN7)] +- **Debounce** : creates a debounced version of the provided function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounce)] + [[play](https://go.dev/play/p/-dGFrYn_1Zi)] +- **Throttle** : creates a throttled version of the provided function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Throttle)] + [[play](https://go.dev/play/p/HpoMov-tJSN)] +- **Schedule** : invoke function every duration time, util close the returned bool channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Schedule)] + [[play](https://go.dev/play/p/hbON-Xeyn5N)] +- **Pipeline** : takes a list of functions and returns a function whose param will be passed into the functions one by one. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)] + [[play](https://go.dev/play/p/mPdUVvj6HD6)] +- **AcceptIf** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)] + [[play](https://go.dev/play/p/XlXHHtzCf7d)] +- **And** : returns a composed predicate that represents the logical AND of a list of predicates. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#And)] + [[play](https://go.dev/play/p/dTBHJMQ0zD2)] +- **Or** : returns a composed predicate that represents the logical OR of a list of predicates. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Or)] + [[play](https://go.dev/play/p/LitCIsDFNDA)] +- **Negate** : returns a predicate that represents the logical negation of this predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Negate)] + [[play](https://go.dev/play/p/jbI8BtgFnVE)] +- **Nor** : returns a composed predicate that represents the logical NOR of a list of predicates. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nor)] + [[play](https://go.dev/play/p/2KdCoBEOq84)] +- **Nand** : returns a composed predicate that represents the logical Nand of a list of predicates. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nand)] + [[play](https://go.dev/play/p/Rb-FdNGpgSO)] +- **Xnor** : returns a composed predicate that represents the logical XNOR of a list of predicates. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Xnor)] + [[play](https://go.dev/play/p/FJxko8SFbqc)] +- **Watcher** : Watcher is used for record code execution time. can start/stop/reset the watch timer. get the elapsed time of function execution. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)] + [[play](https://go.dev/play/p/l2yrOpCLd1I)] -### Mathutil package implements some functions for math calculation. +

12. Maputil package includes some functions to manipulate map.       index

```go -import "github.com/duke-git/lancet/mathutil" +import "github.com/duke-git/lancet/v2/maputil" ``` #### Function list: -- [Exponent](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Exponent) -- [Fibonacci](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Fibonacci) -- [Factorial](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Factorial) -- [Percent](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Percent) -- [RoundToFloat](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#RoundToFloat) -- [RoundToString](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#RoundToString) -- [TruncRound](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#TruncRound) +- **MapTo** : quick map any value to struct or any base type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapTo)] + [[play](https://go.dev/play/p/4K7KBEPgS5M)] +- **ForEach** : executes iteratee function for every key and value pair in map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ForEach)] + [[play](https://go.dev/play/p/OaThj6iNVXK)] +- **Filter** : iterates over map, return a new map contains all key and value pairs pass the predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Filter)] + [[play](https://go.dev/play/p/fSvF3wxuNG7)] +- **FilterByKeys** : iterates over map, return a new map whose keys are all given keys + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#FilterByKeys)] + [[play](https://go.dev/play/p/7ov6BJHbVqh)] +- **FilterByValues** : iterates over map, return a new map whose values are all given values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#FilterByValues)] + [[play](https://go.dev/play/p/P3-9MdcXegR)] +- **OmitBy** : the opposite of Filter, removes all the map elements for which the predicate function returns true. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitBy)] + [[play](https://go.dev/play/p/YJM4Hj5hNwm)] +- **OmitByKeys** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByKeys)] + [[play](https://go.dev/play/p/jXGrWDBfSRp)] +- **OmitByValues** : the opposite of FilterByValues. remove all elements whose value are in the give slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByValues)] + [[play](https://go.dev/play/p/XB7Y10uw20_U)] +- **Intersect** : iterates over maps, return a new map of key and value pairs in all given maps. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Intersect)] + [[play](https://go.dev/play/p/Zld0oj3sjcC)] +- **Keys** : returns a slice of the map's keys. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Keys)] + [[play](https://go.dev/play/p/xNB5bTb97Wd)] +- **KeysBy** : creates a slice whose element is the result of function mapper invoked by every map's key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#KeysBy)] + [[play](https://go.dev/play/p/hI371iB8Up8)] +- **Merge** : merge maps, next key will overwrite previous key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Merge)] + [[play](https://go.dev/play/p/H95LENF1uB-)] +- **Minus** : creates a map of whose key in mapA but not in mapB. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Minus)] + [[play](https://go.dev/play/p/3u5U9K7YZ9m)] +- **Values** : returns a slice of the map's values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Values)] + [[play](https://go.dev/play/p/CBKdUc5FTW6)] +- **ValuesBy** : creates a slice whose element is the result of function mapper invoked by every map's value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ValuesBy)] + [[play](https://go.dev/play/p/sg9-oRidh8f)] +- **MapKeys** : transforms a map to other type map by manipulating it's keys. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapKeys)] + [[play](https://go.dev/play/p/8scDxWeBDKd)] +- **MapValues** : transforms a map to other type map by manipulating it's values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapValues)] + [[play](https://go.dev/play/p/g92aY3fc7Iw)] +- **Entries** : transforms a map into array of key/value pairs. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Entries)] + [[play](https://go.dev/play/p/Ltb11LNcElY)] +- **FromEntries** : creates a map based on a slice of key/value pairs. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#FromEntries)] + [[play](https://go.dev/play/p/fTdu4sCNjQO)] +- **Transform** : transform a map to another type map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#Transform)] + [[play](https://go.dev/play/p/P6ovfToM3zj)] +- **IsDisjoint** : check two map are disjoint if they have no keys in common. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#IsDisjoint)] + [[play](https://go.dev/play/p/N9qgYg_Ho6f)] +- **HasKey** : checks if map has key or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)] + [[play](https://go.dev/play/p/isZZHOsDhFc)] +- **GetOrSet** : returns value of the given key or set the given value value if not present. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrSet)] + [[play](https://go.dev/play/p/IVQwO1OkEJC)] +- **MapToStruct** : converts map to struct. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapToStruct)] + [[play](https://go.dev/play/p/7wYyVfX38Dp)] +- **ToSortedSlicesDefault** : converts a map to two slices sorted by key: one for the keys and another for the values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)] + [[play](https://go.dev/play/p/43gEM2po-qy)] +- **ToSortedSlicesWithComparator** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)] + [[play](https://go.dev/play/p/0nlPo6YLdt3)] +- **NewOrderedMap** : creates a new OrderedMap. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewOrderedMap)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Set** : sets the given key-value pair for ordered map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Set)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Get** : returns the value for the given key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Get)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Delete** : deletes the key-value pair for the given key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Delete)] + [[play](ttps://go.dev/play/p/5bIi4yaZ3K-)] +- **OrderedMap_Clear** : clears the ordered map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Clear)] + [[play](https://go.dev/play/p/8LwoJyEfuFr)] +- **OrderedMap_Front** : returns the first key-value pair. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Front)] + [[play](https://go.dev/play/p/ty57XSimpoe)] +- **OrderedMap_Back** : returns the last key-value pair. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Back)] + [[play](https://go.dev/play/p/rQMjp1yQmpa)] +- **OrderedMap_Range** : calls the given function for each key-value pair. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Range)] + [[play](https://go.dev/play/p/U-KpORhc7LZ)] +- **OrderedMap_Keys** : returns the keys in order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Keys)] + [[play](https://go.dev/play/p/Vv_y9ExKclA)] +- **OrderedMap_Values** : returns the values in order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Values)] + [[play](https://go.dev/play/p/TWj5n1-PUfx)] +- **OrderedMap_Elements** : returns the key-value pairs in order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Elements)] + [[play](https://go.dev/play/p/4BHG4kKz6bB)] +- **OrderedMap_Len** : returns the number of key-value pairs. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Len)] + [[play](https://go.dev/play/p/cLe6z2VX5N-)] +- **OrderedMap_Contains** : returns true if the given key exists. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Contains)] + [[play](https://go.dev/play/p/QuwqqnzwDNX)] +- **OrderedMap_Iter** : returns a channel that yields key-value pairs in order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Iter)] + [[play](https://go.dev/play/p/tlq2tdvicPt)] +- **OrderedMap_ReverseIter** : returns a channel that yields key-value pairs in reverse order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_ReverseIter)] + [[play](https://go.dev/play/p/8Q0ssg6hZzO)] +- **OrderedMap_SortByKey** : sorts the map by key given less function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_SortByKey)] + [[play](https://go.dev/play/p/N7hjD_alZPq)] +- **OrderedMap_MarshalJSON** : implements the json.Marshaler interface. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_MarshalJSON)] + [[play](https://go.dev/play/p/C-wAwydIAC7)] +- **OrderedMap_UnmarshalJSON** : implements the json.Unmarshaler interface. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_UnmarshalJSON)] + [[play](https://go.dev/play/p/t_pkwerIRVx)] +- **NewConcurrentMap** : creates a ConcurrentMap with specific shard count. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_Set** : set the value for a key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Set)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_Get** : get the value stored in the map for a key, or nil if no. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Get)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_GetOrSet** : returns the existing value for the key if present. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_GetOrSet)] + [[play](https://go.dev/play/p/aDcDApOK01a)] +- **ConcurrentMap_Delete** : delete the value for a key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Delete)] + [[play](https://go.dev/play/p/uTIJZYhpVMS)] +- **ConcurrentMap_GetAndDelete** :returns the existing value for the key if present and then delete the value for the key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_GetAndDelete)] + [[play](https://go.dev/play/p/ZyxeIXSZUiM)] +- **ConcurrentMap_Has** : checks if map has the value for a key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Has)] + [[play](https://go.dev/play/p/C8L4ul9TVwf)] +- **ConcurrentMap_Range** : calls iterator sequentially for each key and value present in each of the shards in the map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Range)] + [[play](https://go.dev/play/p/iqcy7P8P0Pr)] +- **SortByKey** : sorts the map by its keys and returns a new map with sorted keys. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#SortByKey)] + [[play](https://go.dev/play/p/PVdmBSnm6P_W)] +- **GetOrDefault** : 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)] +- **FindValuesBy** : 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)] -### Netutil package contains functions to get net information and send http request. +

13. Mathutil package implements some functions for math calculation.        index

```go -import "github.com/duke-git/lancet/netutil" +import "github.com/duke-git/lancet/v2/mathutil" ``` #### Function list: -- [ConvertMapToQueryString](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#ConvertMapToQueryString) -- [GetInternalIp](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetInternalIp) -- [GetIps](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetIps) -- [GetMacAddrs](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetMacAddrs) -- [GetPublicIpInfo](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetPublicIpInfo) -- [IsPublicIP](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#IsPublicIP) -- [HttpGet](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpGet) -- [HttpDelete](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpDelete) -- [HttpPost](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPost) -- [HttpPut](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPut) -- [HttpPatch](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPatch) -- [ParseHttpResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#ParseHttpResponse) - -### Random package implements some basic functions to generate random int and string. + +- **Average** :return average value of numbers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Average)] + [[play](https://go.dev/play/p/Vv7LBwER-pz)] +- **Exponent** : calculate x^n for int64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Exponent)] + [[play](https://go.dev/play/p/uF3HGNPk8wr)] +- **Fibonacci** :calculate fibonacci number before n for int. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Fibonacci)] + [[play](https://go.dev/play/p/IscseUNMuUc)] +- **Factorial** : calculate x! for uint. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Factorial)] + [[play](https://go.dev/play/p/tt6LdOK67Nx)] +- **Max** : return maximum value of numbers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Max)] + [[play](https://go.dev/play/p/cN8DHI0rTkH)] +- **MaxBy** : return the maximum value of a slice using the given comparator function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#MaxBy)] + [[play](https://go.dev/play/p/pbe2MT-7DV2)] +- **Min** : return minimum value of numbers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Min)] + [[play](https://go.dev/play/p/21BER_mlGUj)] +- **MinBy** : return the minimum value of a slice using the given comparator function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#MinBy)] + [[play](https://go.dev/play/p/N9qgYg_Ho6f)] +- **Percent** : calculate the percentage of value to total. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Percent)] + [[play](https://go.dev/play/p/s0NdFCtwuyd)] +- **RoundToFloat** : round up to n decimal places for float64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#RoundToFloat)] + [[play](https://go.dev/play/p/ghyb528JRJL)] +- **RoundToString** : round up to n decimal places for float64, return string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#RoundToString)] + [[play](https://go.dev/play/p/kZwpBRAcllO)] +- **TruncRound** : round off n decimal places for int64. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#TruncRound)] + [[play](https://go.dev/play/p/aumarSHIGzP)] +- **CeilToFloat** : round float up n decimal places. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToFloat)] + [[play](https://go.dev/play/p/8hOeSADZPCo)] +- **CeilToString** : round float up n decimal places, then conver to string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToString)] + [[play](https://go.dev/play/p/wy5bYEyUKKG)] +- **FloorToFloat** : round float down n decimal places. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToFloat)] + [[play](https://go.dev/play/p/vbCBrQHZEED)] +- **FloorToString** : round float down n decimal places, then conver to string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToString)] + [[play](https://go.dev/play/p/Qk9KPd2IdDb)] +- **Range** : Creates a slice of numbers from start with specified count, element step is 1. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)] + [[play](https://go.dev/play/p/9ke2opxa8ZP)] +- **RangeWithStep** : Creates a slice of numbers from start to end with specified step. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)] + [[play](https://go.dev/play/p/akLWz0EqOSM)] +- **AngleToRadian** : converts angle value to radian value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#AngleToRadian)] + [[play](https://go.dev/play/p/CIvlICqrHql)] +- **RadianToAngle** : converts radian value to angle value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#RadianToAngle)] + [[play](https://go.dev/play/p/dQtmOTUOMgi)] +- **PointDistance** : get two points distance. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#PointDistance)] + [[play](https://go.dev/play/p/RrG4JIaziM8)] +- **IsPrime** : checks if number is prime number. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#IsPrime)] + [[play](https://go.dev/play/p/Rdd8UTHZJ7u)] +- **GCD** : return greatest common divisor (GCD) of integers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#GCD)] + [[play](https://go.dev/play/p/CiEceLSoAKB)] +- **LCM** : return Least Common Multiple (LCM) of integers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#LCM)] + [[play](https://go.dev/play/p/EjcZxfY7G_g)] +- **Cos** : return the cosine of the radian argument. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Cos)] + [[play](https://go.dev/play/p/Sm89LoIfvFq)] +- **Sin** : return the sine of the radian argument. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sin)] + [[play](https://go.dev/play/p/TWMQlMywDsP)] +- **Log** : returns the logarithm of base n. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Log)] + [[play](https://go.dev/play/p/_d4bi8oyhat)] +- **Sum** : return sum of passed numbers. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)] + [[play](https://go.dev/play/p/1To2ImAMJA7)] +- **Abs** : returns the absolute value of param number. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)] + [[play](https://go.dev/play/p/fsyBh1Os-1d)] +- **Div** : 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)] +- **Variance** : 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)] +- **StdDev** : 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)] +- **Permutation** : 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)] +- **Combination** : 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)] + +

14. Netutil package contains functions to get net information and send http request.        index

```go -import "github.com/duke-git/lancet/random" +import "github.com/duke-git/lancet/v2/netutil" ``` #### Function list: -- [RandBytes](https://github.com/duke-git/lancet/blob/main/docs/random.md#RandBytes) -- [RandInt](https://github.com/duke-git/lancet/blob/main/docs/random.md#RandInt) -- [RandString](https://github.com/duke-git/lancet/blob/main/docs/random.md#RandString) -- [UUIdV4](https://github.com/duke-git/lancet/blob/main/docs/random.md#UUIdV4) -### Retry package is for executing a function repeatedly until it was successful or canceled by the context. +- **ConvertMapToQueryString** : convert map to sorted url query string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#ConvertMapToQueryString)] + [[play](https://go.dev/play/p/jnNt_qoSnRi)] +- **EncodeUrl** : encode url(?a=1&b=[2] -> ?a=1&b=%5B2%5D). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#EncodeUrl)] + [[play](https://go.dev/play/p/bsZ6BRC4uKI)] +- **GetInternalIp** : return internal ipv4. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#GetInternalIp)] + [[play](https://go.dev/play/p/5mbu-gFp7ei)] +- **GetIps** : return all ipv4 of current system. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#GetIps)] + [[play](https://go.dev/play/p/NUFfcEmukx1)] +- **GetMacAddrs** : return mac address of current system. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#GetMacAddrs)] + [[play](https://go.dev/play/p/Rq9UUBS_Xp1)] +- **GetPublicIpInfo** : return [public ip information](http://ip-api.com/json/). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#GetPublicIpInfo)] + [[play](https://go.dev/play/p/YDxIfozsRHR)] +- **GetRequestPublicIp** : return the http request public ip. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#GetRequestPublicIp)] + [[play](https://go.dev/play/p/kxU-YDc_eBo)] +- **IsPublicIP** : verify a ip is public or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#IsPublicIP)] + [[play](https://go.dev/play/p/nmktSQpJZnn)] +- **IsInternalIP** : verify an ip is intranet or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#IsInternalIP)] + [[play](https://go.dev/play/p/sYGhXbgO4Cb)] +- **HttpRequest** : a composed http request used for HttpClient send request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpRequest)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **HttpClient** : a http client tool, used for sending http request + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpClient)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **SendRequest** : send http request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#SendRequest)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **DecodeResponse** : decode http response into target object. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#DecodeResponse)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **StructToUrlValues** : convert struct to url values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#StructToUrlValues)] + [[play](https://go.dev/play/p/pFqMkM40w9z)] +- **HttpGetdeprecated** : send http get request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpGet)] +- **HttpDeletedeprecated** : send http delete request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpDelete)] +- **HttpPostdeprecated** : send http post request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpPost)] +- **HttpPutdeprecated** : send http put request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpPut)] +- **HttpPatchdeprecated** : send http patch request. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#HttpPatch)] +- **ParseHttpResponse** : decode http response into target object. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#ParseHttpResponse)] +- **DownloadFile** : download the file exist in url to a local file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#DownloadFile)] +- **UploadFile** : upload the file to a server. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#UploadFile)] +- **IsPingConnected** : checks if can ping the specified host or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#IsPingConnected)] + [[play](https://go.dev/play/p/q8OzTijsA87)] +- **IsTelnetConnected** : 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)] +- **BuildUrl** : 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)] +- **AddQueryParams** : 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)] + +

15. Pointer package contains some util functions to operate go pointer.        index

```go -import "github.com/duke-git/lancet/retry" +import "github.com/duke-git/lancet/v2/pointer" ``` #### Function list: -- [Context](https://github.com/duke-git/lancet/blob/main/docs/retry.md#Context) -- [Retry](https://github.com/duke-git/lancet/blob/main/docs/retry.md#Retry) -- [RetryFunc](https://github.com/duke-git/lancet/blob/main/docs/retry.md#RetryFunc) -- [RetryDuration](https://github.com/duke-git/lancet/blob/main/docs/retry.md#RetryDuration) -- [RetryTimes](https://github.com/duke-git/lancet/blob/main/docs/retry.md#RetryTimes) -### Slice contains some functions to manipulate slice. +- **ExtractPointer** : return the underlying value by the given interface type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#ExtractPointer)] + [[play](https://go.dev/play/p/D7HFjeWU2ZP)] +- **Of** : return a pointer to the value `v`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Of)] + [[play](https://go.dev/play/p/HFd70x4DrMj)] +- **Unwrap** : return the value from the pointer. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)] + [[play](https://go.dev/play/p/cgeu3g7cjWb)] +- **UnwrapOr** : UnwrapOr returns the value from the pointer or fallback if the pointer is nil. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)] + [[play](https://go.dev/play/p/mmNaLC38W8C)] +- **UnwarpOrDefault** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOrDefault)] + [[play](https://go.dev/play/p/ZnGIHf8_o4E)] + +

16. Random package implements some basic functions to generate random int and string.        index

```go -import "github.com/duke-git/lancet/slice" +import "github.com/duke-git/lancet/v2/random" ``` #### Function list: -- [Contain](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Contain) -- [ContainSubSlice](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainSubSlice) -- [Chunk](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Chunk) -- [Compact](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Compact) -- [Concat](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Concat) -- [Count](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Count) -- [Difference](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Difference) -- [DifferenceBy](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DifferenceBy) -- [DeleteByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DeleteByIndex) -- [Drop](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Drop) -- [Every](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Every) -- [Filter](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Filter) -- [Find](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Find) -- [FindLast](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FindLast) -- [FlattenDeep](#FlattenDeep) -- [ForEach](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ForEach) -- [GroupBy](https://github.com/duke-git/lancet/blob/main/docs/slice.md#GroupBy) -- [IntSlice](https://github.com/duke-git/lancet/blob/main/docs/slice.md#IntSlice) -- [InterfaceSlice](https://github.com/duke-git/lancet/blob/main/docs/slice.md#InterfaceSlice) -- [Intersection](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Intersection) -- [InsertByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice.md#InsertByIndex) -- [Map](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Map) -- [ReverseSlice](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ReverseSlice) -- [Reduce](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Reduce) -- [Shuffle](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Shuffle) -- [SortByField](https://github.com/duke-git/lancet/blob/main/docs/slice.md#SortByField) -- [Some](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Some) -- [StringSlice](https://github.com/duke-git/lancet/blob/main/docs/slice.md#StringSlice) -- [Unique](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Unique) -- [Union](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Union) -- [UpdateByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice.md#UpdateByIndex) -- [Without](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Without) - -### Strutil package contains some functions to manipulate string. + +- **RandBytes** : generate random byte slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBytes)] + [[play](https://go.dev/play/p/EkiLESeXf8d)] +- **RandInt** : generate random int number between min and max. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandInt)] + [[play](https://go.dev/play/p/pXyyAAI5YxD)] +- **RandString** : generate random string of specific length. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandString)] + [[play](https://go.dev/play/p/W2xvRUXA7Mi)] +- **RandUpper** : generate a random upper case string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUpper)] + [[play](https://go.dev/play/p/29QfOh0DVuh)] +- **RandLower** : generate a random lower case string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandLower)] + [[play](https://go.dev/play/p/XJtZ471cmtI)] +- **RandNumeral** : generate a random numeral string of specific length. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandNumeral)] + [[play](https://go.dev/play/p/g4JWVpHsJcf)] +- **RandNumeralOrLetter** : generate a random numeral or letter string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandNumeralOrLetter)] + [[play](https://go.dev/play/p/19CEQvpx2jD)] +- **UUIdV4** : generate a random UUID of version 4 according to RFC 4122. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)] + [[play](https://go.dev/play/p/_Z9SFmr28ft)] +- **RandUniqueIntSlice** : generate a slice of random int that do not repeat. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)] + [[play](https://go.dev/play/p/uBkRSOz73Ec)] +- **RandSymbolChar** : generate a random symbol char of specified length. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSymbolChar)] + [[play](https://go.dev/play/p/Im6ZJxAykOm)] +- **RandFloat** : generate a random float64 number between [min, max) with specific precision. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)] + [[play](https://go.dev/play/p/zbD_tuobJtr)] +- **RandFloats** : generate a slice of random float64 numbers that do not repeat. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)] + [[play](https://go.dev/play/p/I3yndUQ-rhh)] +- **RandStringSlice** : generate a slice of random string of length strLen based on charset. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandStringSlice)] + [[play](https://go.dev/play/p/2_-PiDv3tGn)] +- **RandBool** : generate a random boolean value (true or false). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBool)] + [[play](https://go.dev/play/p/to6BLc26wBv)] +- **RandBoolSlice** : generate a random boolean slice of specified length. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBoolSlice)] + [[play](https://go.dev/play/p/o-VSjPjnILI)] +- **RandIntSlice** : generate a slice of random int. Number range in [min, max) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandIntSlice)] + [[play](https://go.dev/play/p/GATTQ5xTEG8)] +- **RandFromGivenSlice** : generate a random element from given slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFromGivenSlice)] + [[play](https://go.dev/play/p/UrkWueF6yYo)] +- **RandSliceFromGivenSlice** : 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)] +- **RandNumberOfLength** : 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)] + +

17. Retry package is for executing a function repeatedly until it was successful or canceled by the context.        index

```go -import "github.com/duke-git/lancet/strutil" +import "github.com/duke-git/lancet/v2/retry" ``` #### Function list: -- [After](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#After) -- [AfterLast](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#AfterLast) -- [Before](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Before) -- [BeforeLast](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#BeforeLast) -- [CamelCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#CamelCase) -- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Capitalize) -- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#IsString) -- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#KebabCase) -- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#LowerFirst) -- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperFirst) -- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd) -- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadStart) -- [ReverseStr](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#ReverseStr) -- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SnakeCase) -- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Wrap) -- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Unwrap) - -### System package contain some functions about os, runtime, shell command. +- **Context** : set retry context config option. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#Context)] + [[play](https://go.dev/play/p/xnAOOXv9GkS)] +- **Retry** : executes the retryFunc repeatedly until it was successful or canceled by the context. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#Retry)] + [[play](https://go.dev/play/p/nk2XRmagfVF)] +- **RetryFunc** : function that retry executes. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryFunc)] + [[play](https://go.dev/play/p/nk2XRmagfVF)] +- **RetryDuration** : set duration of retry + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryDuration)] + [[play](https://go.dev/play/p/nk2XRmagfVF)] +- **RetryTimes** : set times of retry. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryTimes)] + [[play](https://go.dev/play/p/ssfVeU2SwLO)] +- **BackoffStrategy** : An interface that defines a method for calculating backoff intervals. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)] +- **RetryWithCustomBackoff** : set abitary custom backoff strategy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)] + [[play](https://go.dev/play/p/jIm_o2vb5Y4)] +- **RetryWithLinearBackoff** : set linear strategy backoff. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)] + [[play](https://go.dev/play/p/PDet2ZQZwcB)] +- **RetryWithExponentialWithJitterBackoff** : 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)] + +

18. Slice contains some functions to manipulate slice.        index

```go -import "github.com/duke-git/lancet/system" +import "github.com/duke-git/lancet/v2/slice" ``` #### Function list: -- [IsWindows](https://github.com/duke-git/lancet/blob/main/docs/system.md#IsWindows) -- [IsLinux](https://github.com/duke-git/lancet/blob/main/docs/system.md#IsLinux) -- [IsMac](https://github.com/duke-git/lancet/blob/main/docs/system.md#IsMac) -- [GetOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system.md#GetOsEnv) -- [SetOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system.md#SetOsEnv) -- [RemoveOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system.md#RemoveOsEnv) -- [CompareOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system.md#CompareOsEnv) -- [ExecCommand](https://github.com/duke-git/lancet/blob/main/docs/system.md#ExecCommand) -### Validator package contains some functions for data validation. +- **AppendIfAbsent** : if the item is absent,append it to the slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#AppendIfAbsent)] + [[play](https://go.dev/play/p/GNdv7Jg2Taj)] +- **Contain** : check if the value is in the slice or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Contain)] + [[play](https://go.dev/play/p/_454yEHcNjf)] +- **ContainBy** : returns true if predicate function return true. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ContainBy)] + [[play](https://go.dev/play/p/49tkHfX4GNc)] +- **ContainSubSlice** : 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)] +- **Chunk** : 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)] +- **Compact** : creates an slice with all falsy values removed. The values false, nil, 0, and "" are falsy. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Compact)] + [[play](https://go.dev/play/p/pO5AnxEr3TK)] +- **Concat** : creates a new slice concatenating slice with any additional slices. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Concat)] + [[play](https://go.dev/play/p/gPt-q7zr5mk)] +- **Count** : returns the number of occurrences of the given item in the slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Count)] + [[play](https://go.dev/play/p/Mj4oiEnQvRJ)] +- **CountBy** : iterates over elements of slice with predicate function, returns the number of all matched elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#CountBy)] + [[play](https://go.dev/play/p/tHOccTMDZCC)] +- **Difference** : creates an slice of whose element in slice but not in compared slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Difference)] + [[play](https://go.dev/play/p/VXvadzLzhDa)] +- **DifferenceBy** : accepts iteratee which is invoked for each element of slice and values to generate the criterion by which they're compared. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceBy)] + [[play](https://go.dev/play/p/DiivgwM5OnC)] +- **DifferenceWith** : accepts comparator which is invoked to compare elements of slice to values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceWith)] + [[play](https://go.dev/play/p/v2U2deugKuV)] +- **DeleteAt** : delete the element of slice at index. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)] + [[play](https://go.dev/play/p/800B1dPBYyd)] +- **DeleteRange** : delete the element of slice from start index to end index(exclude). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteRange)] + [[play](https://go.dev/play/p/945HwiNrnle)] +- **Drop** : drop n elements from the start of a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)] + [[play](https://go.dev/play/p/jnPO2yQsT8H)] +- **DropRight** : drop n elements from the end of a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DropRight)] + [[play](https://go.dev/play/p/8bcXvywZezG)] +- **DropWhile** : drop n elements from the start of a slice while predicate function returns true. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DropWhile)] + [[play](https://go.dev/play/p/4rt252UV_qs)] +- **DropRightWhile** : drop n elements from the end of a slice while predicate function returns true. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DropRightWhile)] + [[play](https://go.dev/play/p/6wyK3zMY56e)] +- **Equal** : checks if two slices are equal: the same length and all elements' order and value are equal. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Equal)] + [[play](https://go.dev/play/p/WcRQJ37ifPa)] +- **EqualWith** : 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)] +- **EqualUnordered** : 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)] +- **Every** : 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)] +- **Filter** : iterates over elements of slice, returning an slice of all elements pass the predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Filter)] + [[play](https://go.dev/play/p/SdPna-7qK4T)] +- **FilterMap** : returns a slice which apply both filtering and mapping to the given slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FilterMap)] + [[play](https://go.dev/play/p/J94SZ_9MiIe)] +- **Finddeprecated** : iterates over elements of slice, returning the first one that passes a truth test on predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Find)] + [[play](https://go.dev/play/p/CBKeBoHVLgq)] +- **FindBy** : iterates over elements of slice, returning the first one that passes a truth test on predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FindBy)] + [[play](https://go.dev/play/p/n1lysBYl-GB)] +- **FindLastdeprecated** : return the last item that passes a truth test on predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FindLast)] + [[play](https://go.dev/play/p/FFDPV_j7URd)] +- **FindLastBy** : iterates over elements of slice, returning the last one that passes a truth test on predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FindLastBy)] + [[play](https://go.dev/play/p/8iqomzyCl_s)] +- **Flatten** : flattens slice one level. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Flatten)] + [[play](https://go.dev/play/p/hYa3cBEevtm)] +- **FlattenDeep** : flattens slice recursive to one level. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FlattenDeep)] + [[play](https://go.dev/play/p/yjYNHPyCFaF)] +- **FlatMap** : manipulates a slice and transforms and flattens it to a slice of another type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#FlatMap)] + [[play](https://go.dev/play/p/_QARWlWs1N_F)] +- **ForEach** : iterates over elements of slice and invokes function for each element. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEach)] + [[play](https://go.dev/play/p/DrPaa4YsHRF)] +- **ForEachWithBreak** : iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachWithBreak)] + [[play](https://go.dev/play/p/qScs39f3D9W)] +- **ForEachConcurrent** : applies the iteratee function to each item in the slice concurrently. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachConcurrent)] + [[play](https://go.dev/play/p/kT4XW7DKVoV)] +- **GroupBy** : iterate over elements of the slice, each element will be group by criteria, returns two slices. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupBy)] + [[play](https://go.dev/play/p/QVkPxzPR0iA)] +- **GroupWith** : return a map composed of keys generated from the results of running each element of slice thru iteratee. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupWith)] + [[play](https://go.dev/play/p/ApCvMNTLO8a)] +- **IntSlicedeprecated** : convert param to int slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IntSlice)] + [[play](https://go.dev/play/p/FdQXF0Vvqs-)] +- **InterfaceSlicedeprecated** : convert param to interface slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#InterfaceSlice)] + [[play](https://go.dev/play/p/FdQXF0Vvqs-)] +- **Intersection** : creates a slice of unique elements that included by all slices. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Intersection)] + [[play](https://go.dev/play/p/anJXfB5wq_t)] +- **InsertAt** : insert the value or other slice into slice at index. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#InsertAt)] + [[play](https://go.dev/play/p/hMLNxPEGJVE)] +- **IndexOf** : returns the index at which the first occurrence of an item is found in a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IndexOf)] + [[play](https://go.dev/play/p/MRN1f0FpABb)] +- **LastIndexOf** : returns the index at which the last occurrence of the item is found in a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LastIndexOf)] + [[play](https://go.dev/play/p/DokM4cf1IKH)] +- **Map** : creates an slice of values by running each element of slice thru iteratee function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Map)] + [[play](https://go.dev/play/p/biaTefqPquw)] +- **MapConcurrent** : applies the iteratee function to each item in the slice by concrrent. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#MapConcurrent)] + [[play](https://go.dev/play/p/H1ehfPkPen0)] +- **Merge** : merge all given slices into one slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Merge)] + [[play](https://go.dev/play/p/lbjFp784r9N)] +- **Reverse** : return slice of element order is reversed to the given slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Reverse)] + [[play](https://go.dev/play/p/8uI8f1lwNrQ)] +- **Reducedeprecated** : creates an slice of values by running each element of slice thru iteratee function.(Deprecated: use ReduceBy) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Reduce)] + [[play](https://go.dev/play/p/_RfXJJWIsIm)] +- **ReduceBy** : produces a value from slice by accumulating the result of each element as passed through the reducer function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceBy)] + [[play](https://go.dev/play/p/YKDpLi7gtee)] +- **ReduceRight** : ReduceRight is like ReduceBy, but it iterates over elements of slice from right to left. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceRight)] + [[play](https://go.dev/play/p/qT9dZC03A1K)] +- **ReduceConcurrent** : reduces the slice to a single value by applying the reducer function to each item in the slice concurrently. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceConcurrent)] + [[play](https://go.dev/play/p/Tjwe6OtaG07)] +- **Replace** : returns a copy of the slice with the first n non-overlapping instances of old replaced by new. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Replace)] + [[play](https://go.dev/play/p/P5mZp7IhOFo)] +- **ReplaceAll** : returns a copy of the slice with all non-overlapping instances of old replaced by new. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReplaceAll)] + [[play](https://go.dev/play/p/CzqXMsuYUrx)] +- **Repeat** : creates a slice with length n whose elements are passed item. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Repeat)] + [[play](https://go.dev/play/p/1CbOmtgILUU)] +- **Shuffle** : 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)] +- **ShuffleCopy** : 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)] +- **IsAscending** : 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)] +- **IsDescending** : Checks if a slice is descending order. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsDescending)] + [[play](https://go.dev/play/p/U_LljFXma14)] +- **IsSorted** : Checks if a slice is sorted (ascending or descending). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsSorted)] + [[play](https://go.dev/play/p/nCE8wPLwSA-)] +- **IsSortedByKey** : Checks if a slice is sorted by iteratee function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#IsSortedByKey)] + [[play](https://go.dev/play/p/tUoGB7DOHI4)] +- **Sort** : sorts a slice of any ordered type(number or string). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Sort)] + [[play](https://go.dev/play/p/V9AVjzf_4Fk)] +- **SortBy** : sorts the slice in ascending order as determined by the less function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SortBy)] + [[play](https://go.dev/play/p/DAhLQSZEumm)] +- **SortByFielddeprecated** : return sorted slice by specific field. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SortByField)] + [[play](https://go.dev/play/p/fU1prOBP9p1)] +- **Some** : return true if any of the values in the list pass the predicate function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Some)] + [[play](https://go.dev/play/p/4pO9Xf9NDGS)] +- **StringSlicedeprecated** : convert param to slice of string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#StringSlice)] + [[play](https://go.dev/play/p/W0TZDWCPFcI)] +- **SymmetricDifference** : the symmetric difference of two slice, also known as the disjunctive union. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#h42nJX5xMln)] + [[play](https://go.dev/play/p/1CbOmtgILUU)] +- **ToSlice** : returns a slices of a variable parameter transformation. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ToSlice)] + [[play](https://go.dev/play/p/YzbzVq5kscN)] +- **ToSlicePointer** : returns a pointer to the slices of a variable parameter transformation. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ToSlicePointer)] + [[play](https://go.dev/play/p/gx4tr6_VXSF)] +- **Unique** : remove duplicate elements in slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)] + [[play](https://go.dev/play/p/AXw0R3ZTE6a)] +- **UniqueBy** : remove duplicate elements from the input slice based on the values returned by the iteratee function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)] + [[play](https://go.dev/play/p/GY7JE4yikrl)] +- **UniqueByComparator** : remove duplicate elements from the input slice using the provided comparator function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByComparator)] + [[play](https://go.dev/play/p/rwSacr-ZHsR)] +- **UniqueByField** : remove duplicate elements in struct slice by struct field. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByField)] + [[play](https://go.dev/play/p/6cifcZSPIGu)] +- **UniqueByConcurrent** : remove duplicate elements from the slice by concurrent. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByConcurrent)] + [[play](https://go.dev/play/p/wXZ7LcYRMGL)] +- **Union** : creates a slice of unique elements, in order, from all given slices. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)] + [[play](https://go.dev/play/p/hfXV1iRIZOf)] +- **UnionBy** : accepts iteratee which is invoked for each element of each slice, then union slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UnionBy)] + [[play](https://go.dev/play/p/HGKHfxKQsFi)] +- **UpdateAt** : update the slice element at index. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UpdateAt)] + [[play](https://go.dev/play/p/f3mh2KloWVm)] +- **Without** : creates a slice excluding all given items. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Without)] + [[play](https://go.dev/play/p/bwhEXEypThg)] +- **KeyBy** : converts a slice to a map based on a callback function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#KeyBy)] + [[play](https://go.dev/play/p/uXod2LWD1Kg)] +- **Join** : join the slice item with specify separator. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Join)] + [[play](https://go.dev/play/p/huKzqwNDD7V)] +- **Partition** : 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)] +- **Random** : 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)] +- **SetToDefaultIf** : set elements to their default value if they match the given predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SetToDefaultIf)] + [[play](https://go.dev/play/p/9AXGlPRC0-A)] +- **Break** : breaks a list into two parts at the point where the predicate for the first time is true. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Break)] +- **RightPadding** : adds padding to the right end of a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#RightPadding)] + [[play](https://go.dev/play/p/0_2rlLEMBXL)] +- **LeftPadding** : adds padding to the left begin of a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)] + [[play](https://go.dev/play/p/jlQVoelLl2k)] +- **Frequency** : 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)] +- **JoinFunc** : 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)] +- **ConcatBy** : 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)] + +

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.        index

```go -import "github.com/duke-git/lancet/validator" +import "github.com/duke-git/lancet/v2/stream" ``` + #### Function list: -- [ContainChinese](https://github.com/duke-git/lancet/blob/main/docs/validator.md#ContainChinese) -- [ContainLetter](https://github.com/duke-git/lancet/blob/main/docs/validator.md#ContainLetter) -- [ContainLower](https://github.com/duke-git/lancet/blob/main/docs/validator.md#ContainLower) -- [ContainUpper](https://github.com/duke-git/lancet/blob/main/docs/validator.md#ContainUpper) -- [IsAlpha](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsAlpha) -- [IsAllUpper](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsAllUpper) -- [IsAllLower](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsAllLower) -- [IsBase64](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsBase64) -- [IsChineseMobile](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsChineseMobile) -- [IsChineseIdNum](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsChineseIdNum) -- [IsChinesePhone](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsChinesePhone) -- [IsCreditCard](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsCreditCard) -- [IsDns](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsDns) -- [IsEmail](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsEmail) -- [IsEmptyString](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsEmptyString) -- [IsFloatStr](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsFloatStr) -- [IsNumberStr](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsNumberStr) -- [IsJSON](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsJSON) -- [IsRegexMatch](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsRegexMatch) -- [IsIntStr](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsIntStr) -- [IsIp](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsIp) -- [IsIpV4](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsIpV4) -- [IsIpV6](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsIpV6) -- [IsStrongPassword](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsStrongPassword) -- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsUrl) -- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsWeakPassword) - +- **Of** : creates a stream whose elements are the specified values. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Of)] + [[play](https://go.dev/play/p/jI6_iZZuVFE)] +- **FromSlice** : creates a stream from slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromSlice)] + [[play](https://go.dev/play/p/wywTO0XZtI4)] +- **FromChannel** : creates a stream from channel. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromChannel)] + [[play](https://go.dev/play/p/9TZYugGMhXZ)] +- **FromRange** : creates a number stream from start to end. both start and end are included. [start, end] + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromRange)] + [[play](https://go.dev/play/p/9Ex1-zcg-B-)] +- **Generate** : creates a stream where each element is generated by the provided generator function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Generate)] + [[play](https://go.dev/play/p/rkOWL1yA3j9)] +- **Concat** : creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Concat)] + [[play](https://go.dev/play/p/HM4OlYk_OUC)] +- **Distinct** : creates returns a stream that removes the duplicated items. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Distinct)] + [[play](https://go.dev/play/p/eGkOSrm64cB)] +- **Filter** : returns a stream consisting of the elements of this stream that match the given predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Filter)] + [[play](https://go.dev/play/p/MFlSANo-buc)] +- **FilterConcurrent** : Applies the provided filter function `predicate` to each element of the input slice concurrently. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FilterConcurrent)] + [[play](https://go.dev/play/p/t_pkwerIRVx)] +- **Map** : returns a stream consisting of the elements of this stream that apply the given function to elements of stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Map)] + [[play](https://go.dev/play/p/OtNQUImdYko)] +- **Peek** : returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Peek)] + [[play](https://go.dev/play/p/u1VNzHs6cb2)] +- **Skip** : returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Skip)] + [[play](https://go.dev/play/p/fNdHbqjahum)] +- **Limit** : returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Limit)] + [[play](https://go.dev/play/p/qsO4aniDcGf)] +- **Reverse** : 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)] +- **ReverseCopy** : 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-)] +- **Range** : 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)] +- **Sorted** : returns a stream consisting of the elements of this stream, sorted according to the provided less function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Sorted)] + [[play](https://go.dev/play/p/XXtng5uonFj)] +- **ForEach** : performs an action for each element of this stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#ForEach)] + [[play](https://go.dev/play/p/Dsm0fPqcidk)] +- **Reduce** : performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Reduce)] + [[play](https://go.dev/play/p/6uzZjq_DJLU)] +- **FindFirst** : returns the first element of this stream and true, or zero value and false if the stream is empty. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FindFirst)] + [[play](https://go.dev/play/p/9xEf0-6C1e3)] +- **FindLast** : returns the last element of this stream and true, or zero value and false if the stream is empty. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FindLast)] + [[play](https://go.dev/play/p/WZD2rDAW-2h)] +- **Max** : returns the maximum element of this stream according to the provided less function. less function: a > b + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Max)] + [[play](https://go.dev/play/p/fm-1KOPtGzn)] +- **Min** : returns the minimum element of this stream according to the provided less function. less function: a < b + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Min)] + [[play](https://go.dev/play/p/vZfIDgGNRe_0)] +- **AllMatch** : returns whether all elements of this stream match the provided predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#AllMatch)] + [[play](https://go.dev/play/p/V5TBpVRs-Cx)] +- **AnyMatch** : returns whether any elements of this stream match the provided predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#AnyMatch)] + [[play](https://go.dev/play/p/PTCnWn4OxSn)] +- **NoneMatch** : returns whether no elements of this stream match the provided predicate. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#NoneMatch)] + [[play](https://go.dev/play/p/iWS64pL1oo3)] +- **Count** : returns the count of elements in the stream. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Count)] + [[play](https://go.dev/play/p/r3koY6y_Xo-)] +- **ToSlice** : 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)] +- **IndexOf** : 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)] +- **LastIndexOf** : 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)] + +

20. Structs package provides several high level functions to manipulate struct, tag, and field.        index

+ +```go +import "github.com/duke-git/lancet/v2/structs" +``` + +#### Function list: + +- **New** : creates a `Struct` instance. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#New)] +- **ToMap** : converts a valid struct to a map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#ToMap)] +- **Fields** : 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)] +- **IsStruct** : check if the struct is valid. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsStruct)] +- **Tag** : 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)] +- **Name** : get the field name. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Name)] +- **Value** : get the `Field` underlying value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Value)] +- **Kind** : get the field's kind. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Kind)] +- **IsEmbedded** : check if the field is an embedded field. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsEmbedded)] +- **IsExported** : check if the field is exported. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsExported)] +- **IsZero** : check if the field is zero value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsZero)] +- **IsSlice** : check if the field is a slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsSlice)] +- **IsTargetType** : check if the field is target type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsTargetType)] + +

21. Strutil package contains some functions to manipulate string.        index

+ +```go +import "github.com/duke-git/lancet/v2/strutil" +``` + +#### Function list: + +- **After** : returns the substring after the first occurrence of a specific string in the source string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#After)] + [[play](https://go.dev/play/p/RbCOQqCDA7m)] +- **AfterLast** : returns the substring after the last occurrence of a specific string in the source string. [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#AfterLast)] + [[play](https://go.dev/play/p/1TegARrb8Yn)] +- **Before** : returns the substring before the first occurrence of a specific string in the source string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Before)] + [[play](https://go.dev/play/p/JAWTZDS4F5w)] +- **BeforeLast** : returns the substring before the last occurrence of a specific string in the source string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#BeforeLast)] + [[play](https://go.dev/play/p/pJfXXAoG_Te)] +- **CamelCase** : coverts source string to its camelCase string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#CamelCase)] + [[play](https://go.dev/play/p/9eXP3tn2tUy)] +- **Capitalize** : converts the first character of source string to upper case and the remaining to lower case. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Capitalize)] + [[play](https://go.dev/play/p/2OAjgbmAqHZ)] +- **ContainsAll** : return true if target string contains all the substrings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#ContainsAll)] + [[play](https://go.dev/play/p/KECtK2Os4zq)] +- **ContainsAny** : return true if target string contains any one of the substrings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#ContainsAny)] + [[play](https://go.dev/play/p/dZGSSMB3LXE)] +- **IsString** : checks if the parameter value data type is string or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#IsString)] + [[play](https://go.dev/play/p/IOgq7oF9ERm)] +- **KebabCase** : coverts string to kebab-case string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#KebabCase)] + [[play](https://go.dev/play/p/dcZM9Oahw-Y)] +- **UpperKebabCase** : coverts string to upper KEBAB-CASE string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#UpperKebabCase)] + [[play](https://go.dev/play/p/zDyKNneyQXk)] +- **LowerFirst** : converts the first character of string to lower case. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#LowerFirst)] + [[play](https://go.dev/play/p/CbzAyZmtJwL)] +- **UpperFirst** : converts the first character of string to upper case. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#UpperFirst)] + [[play](https://go.dev/play/p/sBbBxRbs8MM)] +- **Pad** : pads string on the left and right side if it's shorter than size. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Pad)] + [[play](https://go.dev/play/p/NzImQq-VF8q)] +- **PadEnd** : pads string with given characters on the right side if it's shorter than limit size. Padding characters are truncated if they exceed size. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#PadEnd)] + [[play](https://go.dev/play/p/9xP8rN0vz--)] +- **PadStart** : pads string with given characters on the left side if it's shorter than limit size. Padding characters are truncated if they exceed size. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#PadStart)] + [[play](https://go.dev/play/p/xpTfzArDfvT)] +- **Reverse** : returns string whose char order is reversed to the given string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Reverse)] + [[play](https://go.dev/play/p/adfwalJiecD)] +- **SnakeCase** : coverts string to snake_case string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SnakeCase)] + [[play](https://go.dev/play/p/tgzQG11qBuN)] +- **UpperSnakeCase** : coverts string to upper SNAKE_CASE string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#UpperSnakeCase)] + [[play](https://go.dev/play/p/4COPHpnLx38)] +- **SplitEx** : split a given string which can control the result slice contains empty string or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SplitEx)] + [[play](https://go.dev/play/p/Us-ySSbWh-3)] +- **Substring** : returns a substring of the specific length starting at the specific offset position. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Substring)] + [[play](https://go.dev/play/p/q3sM6ehnPDp)] +- **Wrap** : wrap a string with given string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Wrap)] + [[play](https://go.dev/play/p/KoZOlZDDt9y)] +- **Unwrap** : unwrap a given string from anther string. will change source string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Unwrap)] + [[play](https://go.dev/play/p/Ec2q4BzCpG-)] +- **SplitWords** : splits a string into words, word only contains alphabetic characters. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SplitWords)] + [[play](https://go.dev/play/p/KLiX4WiysMM)] +- **WordCount** : return the number of meaningful word of a string, word only contains alphabetic characters. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#WordCount)] + [[play](https://go.dev/play/p/bj7_odx3vRf)] +- **RemoveNonPrintable** : remove non-printable characters from a string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveNonPrintable)] + [[play](https://go.dev/play/p/og47F5x_jTZ)] +- **StringToBytes** : converts a string to byte slice without a memory allocation. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#StringToBytes)] + [[play](https://go.dev/play/p/7OyFBrf9AxA)] +- **BytesToString** : converts a byte slice to string without a memory allocation. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#BytesToString)] + [[play](https://go.dev/play/p/6c68HRvJecH)] +- **IsBlank** : checks if a string is whitespace or empty. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#IsBlank)] + [[play](https://go.dev/play/p/6zXRH_c0Qd3)] +- **IsNotBlank** : checks if a string is not whitespace or not empty. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#IsNotBlank)] + [[play](https://go.dev/play/p/e_oJW0RAquA)] +- **HasPrefixAny** : checks if a string starts with any of an array of specified strings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HasPrefixAny)] + [[play](https://go.dev/play/p/8UUTl2C5slo)] +- **HasSuffixAny** : checks if a string ends with any of an array of specified strings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HasSuffixAny)] + [[play](https://go.dev/play/p/sKWpCQdOVkx)] +- **IndexOffset** : returns the index of the first instance of substr in string after offsetting the string by `idxFrom`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#IndexOffset)] + [[play](https://go.dev/play/p/qZo4lV2fomB)] +- **ReplaceWithMap** : returns a copy of `str`, which is replaced by a map in unordered way, case-sensitively. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#ReplaceWithMap)] + [[play](https://go.dev/play/p/h3t7CNj2Vvu)] +- **Trim** : strips whitespace (or other characters) from the beginning and end of a string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Trim)] + [[play](https://go.dev/play/p/Y0ilP0NRV3j)] +- **SplitAndTrim** : splits string `str` by a string `delimiter` to a slice, and calls Trim to every element of slice. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SplitAndTrim)] + [[play](https://go.dev/play/p/ZNL6o4SkYQ7)] +- **HideString** : Hide some chars in source string with param `replaceChar`. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HideString)] + [[play](https://go.dev/play/p/pzbaIVCTreZ)] +- **RemoveWhiteSpace** : remove whitespace characters from a string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveWhiteSpace)] + [[play](https://go.dev/play/p/HzLC9vsTwkf)] +- **SubInBetween** : return substring between the start and end position(excluded) of source string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SubInBetween)] + [[play](https://go.dev/play/p/EDbaRvjeNsv)] +- **HammingDistance** : calculates the Hamming distance between two strings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HammingDistance)] + [[play](https://go.dev/play/p/glNdQEA9HUi)] +- **Concat** : concatenates strings. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Concat)] + [[play](https://go.dev/play/p/gD52SZHr4Kp)] +- **Ellipsis** : truncates a string to a specified length and appends an ellipsis. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Ellipsis)] + [[play](https://go.dev/play/p/i1vbdQiQVRR)] +- **Shuffle** : shuffle the order of characters of given string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Shuffle)] + [[play](https://go.dev/play/p/iStFwBwyGY7)] +- **Rotate** : rotates the string by the specified number of characters. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Rotate)] + [[play](https://go.dev/play/p/Kf03iOeT5bd)] +- **TemplateReplace** : replaces the placeholders in the template string with the corresponding values in the data map. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#TemplateReplace)] + [[play](https://go.dev/play/p/cXSuFvyZqv9)] +- **RegexMatchAllGroups** : 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-)] +- **ExtractContent** : 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)] +- **FindAllOccurrences** : 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)] + +

22. System package contain some functions about os, runtime, shell command.        index

+ +```go +import "github.com/duke-git/lancet/v2/system" +``` + +#### Function list: + +- **IsWindows** : check if current os is windows. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#IsWindows)] + [[play](https://go.dev/play/p/XzJULbzmf9m)] +- **IsLinux** : check if current os is linux. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#IsLinux)] + [[play](https://go.dev/play/p/zIflQgZNuxD)] +- **IsMac** : check if current os is macos. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#IsMac)] + [[play](https://go.dev/play/p/Mg4Hjtyq7Zc)] +- **GetOsEnv** : get the value of the environment variable named by the key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetOsEnv)] + [[play](https://go.dev/play/p/D88OYVCyjO-)] +- **SetOsEnv** : set the value of the environment variable named by the key. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#SetOsEnv)] + [[play](https://go.dev/play/p/D88OYVCyjO-)] +- **RemoveOsEnv** : remove a single environment variable. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#RemoveOsEnv)] + [[play](https://go.dev/play/p/fqyq4b3xUFQ)] +- **CompareOsEnv** : get env named by the key and compare it with passed env. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#CompareOsEnv)] + [[play](https://go.dev/play/p/BciHrKYOHbp)] +- **ExecCommand** : execute command, return the stdout and stderr string of command, and error if error occurs. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#ExecCommand)] + [[play](https://go.dev/play/p/n-2fLyZef-4)] +- **GetOsBits** : return current os bits (32 or 64). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetOsBits)] + [[play](https://go.dev/play/p/ml-_XH3gJbW)] +- **StartProcess** : start a new process with the specified name and arguments. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StartProcess)] + [[play](https://go.dev/play/p/5GVol6ryS_X)] +- **StopProcess** : stop a process by pid. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StopProcess)] + [[play](https://go.dev/play/p/jJZhRYGGcmD)] +- **KillProcess** : kill a new process with the specified name and arguments. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#KillProcess)] + [[play](https://go.dev/play/p/XKmvV-ExBWa)] +- **GetProcessInfo** : retrieves detailed process information by pid. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)] + [[play](https://go.dev/play/p/NQDVywEYYx7)] + +

23. Tuple package implements tuple data type and some operations on it.        index

+ +```go +import "github.com/duke-git/lancet/v2/tuple" +``` + +#### Function list: + +- **Tuple2** : represents a 2 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple2)] + [[play](https://go.dev/play/p/3sHVqBQpLYN)] +- **Tuple2_Unbox** : returns values in Tuple2. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple2_Unbox)] + [[play](https://go.dev/play/p/0fD1qfCVwjm)] +- **Zip2** : create a slice of Tuple2, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip2)] + [[play](https://go.dev/play/p/4ncWJJ77Xio)] +- **Unzip2** : create a group of slice from a slice of Tuple2. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip2)] + [[play](https://go.dev/play/p/KBecr60feXb)] +- **Tuple3** : represents a 3 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple3)] + [[play](https://go.dev/play/p/FtH2sdCLlCf)] +- **Tuple3_Unbox** : returns values in Tuple3. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple3_Unbox)] + [[play](https://go.dev/play/p/YojLy-id1BS)] +- **Zip3** : create a slice of Tuple3, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip3)] + [[play](https://go.dev/play/p/97NgmsTILfu)] +- **Unzip3** : create a group of slice from a slice of Tuple3. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip3)] + [[play](https://go.dev/play/p/bba4cpAa7KO)] +- **Tuple4** : represents a 4 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple4)] + [[play](https://go.dev/play/p/D2EqDz096tk)] +- **Tuple4_Unbox** : returns values in Tuple4. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple4_Unbox)] + [[play](https://go.dev/play/p/ACj9YuACGgW)] +- **Zip4** : create a slice of Tuple4, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip4)] + [[play](https://go.dev/play/p/PEmTYVK5hL4)] +- **Unzip4** : create a group of slice from a slice of Tuple4. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip4)] + [[play](https://go.dev/play/p/rb8z4gyYSRN)] +- **Tuple5** : represents a 5 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple5)] + [[play](https://go.dev/play/p/2WndmVxPg-r)] +- **Tuple5_Unbox** : returns values in Tuple4. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple5_Unbox)] + [[play](https://go.dev/play/p/GyIyZHjCvoS)] +- **Zip5** : create a slice of Tuple5, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip5)] + [[play](https://go.dev/play/p/fCAAJLMfBIP)] +- **Unzip5** : create a group of slice from a slice of Tuple5. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip5)] + [[play](https://go.dev/play/p/gyl6vKfhqPb)] +- **Tuple6** : represents a 6 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple6)] + [[play](https://go.dev/play/p/VjqcCwEJZbs)] +- **Tuple6_Unbox** : returns values in Tuple6. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple6_Unbox)] + [[play](https://go.dev/play/p/FjIHV7lpxmW)] +- **Zip6** : create a slice of Tuple6, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip6)] + [[play](https://go.dev/play/p/oWPrnUYuFHo)] +- **Unzip6** : create a group of slice from a slice of Tuple6. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip6)] + [[play](https://go.dev/play/p/l41XFqCyh5E)] +- **Tuple7** : represents a 7 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple7)] + [[play](https://go.dev/play/p/dzAgv_Ezub9)] +- **Tuple7_Unbox** : returns values in Tuple7. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple7_Unbox)] + [[play](https://go.dev/play/p/R9I8qeDk0zs)] +- **Zip7** : create a slice of Tuple7, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip7)] + [[play](https://go.dev/play/p/WUJuo897Egf)] +- **Unzip7** : create a group of slice from a slice of Tuple7. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip7)] + [[play](https://go.dev/play/p/hws_P1Fr2j3)] +- **Tuple8** : represents a 8 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple8)] + [[play](https://go.dev/play/p/YA9S0rz3dRz)] +- **Tuple8_Unbox** : returns values in Tuple8. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple8_Unbox)] + [[play](https://go.dev/play/p/PRxLBBb4SMl)] +- **Zip8** : create a slice of Tuple8, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip8)] + [[play](https://go.dev/play/p/8V9jWkuJfaQ)] +- **Unzip8** : create a group of slice from a slice of Tuple8. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip8)] + [[play](https://go.dev/play/p/1SndOwGsZB4)] +- **Tuple9** : represents a 9 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple9)] + [[play](https://go.dev/play/p/yS2NGGtZpQr)] +- **Tuple9_Unbox** : returns values in Tuple9. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple9_Unbox)] + [[play](https://go.dev/play/p/oFJFGTAuOa8)] +- **Zip9** : create a slice of Tuple9, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip9)] + [[play](https://go.dev/play/p/cgsL15QYnfz)] +- **Unzip9** : create a group of slice from a slice of Tuple9. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip9)] + [[play](https://go.dev/play/p/91-BU_KURSA)] +- **Tuple10** : represents a 10 elements tuple. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple10)] + [[play](https://go.dev/play/p/799qqZg0hUv)] +- **Tuple10_Unbox** : returns values in Tuple10. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple10_Unbox)] + [[play](https://go.dev/play/p/qfyx3x_X0Cu)] +- **Zip10** : create a slice of Tuple10, whose elements are correspond to the given slice elements. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Zip10)] + [[play](https://go.dev/play/p/YSR-2cXnrY4)] +- **Unzip10** : create a group of slice from a slice of Tuple10. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip10)] + [[play](https://go.dev/play/p/-taQB6Wfre_z)] + +

24. Validator package contains some functions for data validation.        index

+ +```go +import "github.com/duke-git/lancet/v2/validator" +``` + +#### Function list: + +- **ContainChinese** : check if the string contain mandarin chinese. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#ContainChinese)] + [[play](https://go.dev/play/p/7DpU0uElYeM)] +- **ContainLetter** : check if the string contain at least one letter. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#ContainLetter)] + [[play](https://go.dev/play/p/lqFD04Yyewp)] +- **ContainLower** : check if the string contain at least one lower case letter a-z. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#ContainLower)] + [[play](https://go.dev/play/p/Srqi1ItvnAA)] +- **ContainUpper** : check if the string contain at least one upper case letter A-Z. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#ContainUpper)] + [[play](https://go.dev/play/p/CmWeBEk27-z)] +- **IsAlpha** : checks if the string contains only letters (a-zA-Z). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAlpha)] + [[play](https://go.dev/play/p/7Q5sGOz2izQ)] +- **IsAllUpper** : check if the string is all upper case letters A-Z. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAllUpper)] + [[play](https://go.dev/play/p/ZHctgeK1n4Z)] +- **IsAllLower** : check if the string is all lower case letters a-z. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAllLower)] + [[play](https://go.dev/play/p/GjqCnOfV6cM)] +- **IsBase64** : check if the string is base64 string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsBase64)] + [[play](https://go.dev/play/p/sWHEySAt6hl)] +- **IsChineseMobile** : check if the string is chinese mobile number. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChineseMobile)] + [[play](https://go.dev/play/p/GPYUlGTOqe3)] +- **IsChineseIdNum** : check if the string is chinese id card. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChineseIdNum)] + [[play](https://go.dev/play/p/d8EWhl2UGDF)] +- **IsChinesePhone** : check if the string is chinese phone number.(xxx-xxxxxxxx or xxxx-xxxxxxx.) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChinesePhone)] + [[play](https://go.dev/play/p/RUD_-7YZJ3I)] +- **IsCreditCard** : check if the string is credit card. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsCreditCard)] + [[play](https://go.dev/play/p/sNwwL6B0-v4)] +- **IsDns** : check if the string is dns. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsDns)] + [[play](https://go.dev/play/p/jlYApVLLGTZ)] +- **IsEmail** : check if the string is a email address. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsEmail)] + [[play](https://go.dev/play/p/Os9VaFlT33G)] +- **IsEmptyString** : check if the string is empty. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsEmptyString)] + [[play](https://go.dev/play/p/dpzgUjFnBCX)] +- **IsFloat** : check if the value is float(float32, float34) or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsFloat)] + [[play](https://go.dev/play/p/vsyG-sxr99_Z)] +- **IsFloatStr** : check if the string can convert to a float. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsFloatStr)] + [[play](https://go.dev/play/p/LOYwS_Oyl7U)] +- **IsNumber** : check if the value is number(integer, float) or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsNumber)] + [[play](https://go.dev/play/p/mdJHOAvtsvF)] +- **IsNumberStr** : 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)] +- **IsAlphaNumeric** : 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)] +- **IsJSON** : 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)] +- **IsRegexMatch** : check if the string match the regexp. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsRegexMatch)] + [[play](https://go.dev/play/p/z_XeZo_litG)] +- **IsInt** : check if the string can convert to a number. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsInt)] + [[play](https://go.dev/play/p/eFoIHbgzl-z)] +- **IsIntStr** : check if the string can convert to a integer. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIntStr)] + [[play](https://go.dev/play/p/jQRtFv-a0Rk)] +- **IsIp** : check if the string is ip. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIp)] + [[play](https://go.dev/play/p/FgcplDvmxoD)] +- **IsIpV4** : check if the string is ipv4. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsIpV4)] + [[play](https://go.dev/play/p/zBGT99EjaIu)] +- **IsIpV6** : 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)] +- **IsIpPort** : 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)] +- **IsStrongPassword** : 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)] +- **IsUrl** : check if the string is url. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsUrl)] + [[play](https://go.dev/play/p/pbJGa7F98Ka)] +- **IsWeakPassword** : check if the string is weak password. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsWeakPassword)] + [[play](https://go.dev/play/p/wqakscZH5gH)] +- **IsZeroValue** : check if value is a zero value. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsZeroValue)] + [[play](https://go.dev/play/p/UMrwaDCi_t4)] +- **IsGBK** : check if data encoding is gbk. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsGBK)] + [[play](https://go.dev/play/p/E2nt3unlmzP)] +- **IsASCII** : checks if string is all ASCII char. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsASCII)] + [[play](https://go.dev/play/p/hfQNPLX0jNa)] +- **IsPrintable** : checks if string is all printable chars. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsPrintable)] + [[play](https://go.dev/play/p/Pe1FE2gdtTP)] +- **IsBin** : check if a give string is a valid binary value or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsBin)] + [[play](https://go.dev/play/p/ogPeg2XJH4P)] +- **IsHex** : check if a give string is a valid hexadecimal value or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsHex)] + [[play](https://go.dev/play/p/M2qpHbEwmm7)] +- **IsBase64URL** : check if a give string is a valid URL-safe Base64 encoded string. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsBase64URL)] + [[play](https://go.dev/play/p/vhl4mr8GZ6S)] +- **IsJWT** : check if a give string is a valid JSON Web Token (JWT). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJWT)] + [[play](https://go.dev/play/p/R6Op7heJbKI)] +- **IsVisa** : check if a give string is a valid visa card number or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsVisa)] + [[play](https://go.dev/play/p/SdS2keOyJsl)] +- **IsMasterCard** : check if a give string is a valid master card number or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsMasterCard)] + [[play](https://go.dev/play/p/CwWBFRrG27b)] +- **IsAmericanExpress** : check if a give string is a valid american expression card number or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAmericanExpress)] + [[play](https://go.dev/play/p/HIDFpcOdpkd)] +- **IsUnionPay** : check if a give string is a valid union pay number or not. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsUnionPay)] + [[play](https://go.dev/play/p/CUHPEwEITDf)] +- **IsChinaUnionPay** : 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)] + +

25. Xerror package implements helpers for errors.        index

+ +```go +import "github.com/duke-git/lancet/v2/xerror" +``` + +#### Function list: + +- **New** : creates a new XError pointer instance with message. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#New)] + [[play](https://go.dev/play/p/w4oWZts7q7f)] +- **Wrap** : creates a new XError pointer instance based on error object, and add message. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#Wrap)] + [[play](https://go.dev/play/p/5385qT2dCi4)] +- **Unwrap** : returns unwrapped XError from err by errors.As. If no XError, returns nil. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#Unwrap)] + [[play](https://go.dev/play/p/LKMLep723tu)] +- **XError_Wrap** : creates a new XError and copy message and id to new one. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Wrap)] + [[play](https://go.dev/play/p/RpjJ5u5sc97)] +- **XError_Unwrap** : Compatible with github.com/pkg/errors. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Unwrap)] + [[play](https://go.dev/play/p/VUXJ8BST4c6)] +- **XError_With** : adds key and value related to the XError object. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_With)] + [[play](https://go.dev/play/p/ow8UISXX_Dp)] +- **XError_Id** : sets XError object id to check equality in XError.Is. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Id)] + [[play](https://go.dev/play/p/X6HBlsy58U9)] +- **XError_Is** : checks if target error is XError and Error.id of two errors are matched. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Is)] + [[play](https://go.dev/play/p/X6HBlsy58U9)] +- **XError_Values** : returns map of key and value that is set by XError.With function. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Values)] + [[play](https://go.dev/play/p/ow8UISXX_Dp)] +- **XError_StackTrace** : returns stack trace which is compatible with pkg/errors. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_StackTrace)] + [[play](https://go.dev/play/p/6FAvSQpa7pc)] +- **XError_Info** : returns information of xerror, which can be printed. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Info)] + [[play](https://go.dev/play/p/1ZX0ME1F-Jb)] +- **XError_Error** : implements standard error interface. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/xerror.md#XError_Error)] + [[play](https://go.dev/play/p/w4oWZts7q7f)] +- **TryUnwrap** : 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)] +- **TryCatch** : 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 -I really appreciate any code commits which make lancet lib powerful. Please follow the rules below to create your pull request. +#### [Contribution Guide](./CONTRIBUTION.md) + +## Contributors + +Thank you to all the people who contributed to lancet! + + + + + +## Star History -1. Fork the repository. -2. Create your feature branch. -3. Commit your changes. -4. Push to the branch -5. Create new pull request. \ No newline at end of file +[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date) diff --git a/README_zh-CN.md b/README_zh-CN.md index d3b05a5a..2c7bb846 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -3,13 +3,14 @@
-![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.2.6-green.svg)](https://github.com/duke-git/lancet/releases) -[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) -[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) +![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) +[![Release](https://img.shields.io/badge/release-2.3.7-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) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE) +[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Lancet%20Guru-006BFF)](https://gurubase.io/g/lancet) @@ -18,367 +19,2361 @@ lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。

-简体中文 | [English](./README.md) - +## 官网 | [English](./README.md) ## 特性 -- 👏 全面、高效、可复用 -- 💪 180+常用go工具函数,支持string、slice、datetime、net、crypt... -- 💅 只依赖go标准库 -- 🌍 所有导出函数单元测试覆盖率100% +- 👏 全面、高效、可复用。 +- 💪 700+常用 go 工具函数,支持 string、slice、datetime、net、crypt... +- 💅 只依赖 go 标准库和 golang.org/x。 +- 🌍 所有导出函数单元测试覆盖率 100%。 ## 安装 +### Note: + +1. 使用 go1.18 及以上版本的用户,建议安装 v2.x.x。 因为 v2.x.x 应用 go1.18 的泛型重写了大部分函数。 + +```go +go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x +``` + +2. 使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。 + ```go -go get github.com/duke-git/lancet +go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本 ``` ## 用法 -lancet是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入strutil包: +lancet 是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入 strutil 包: ```go -import "github.com/duke-git/lancet/strutil" +import "github.com/duke-git/lancet/v2/strutil" ``` -## 例子 +## 示例 -此处以字符串工具函数ReverseStr(逆序字符串)为例,需要导入strutil包: +此处以字符串工具函数 Reverse(逆序字符串)为例,需要导入 strutil 包: ```go package main import ( "fmt" - "github.com/duke-git/lancet/strutil" + "github.com/duke-git/lancet/v2/strutil" ) func main() { s := "hello" - rs := strutil.ReverseStr(s) + rs := strutil.Reverse(s) fmt.Println(rs) //olleh } ``` -## API文档 -### convertor转换器包支持一些常见的数据类型转换。 +## 文档 + +### 目录 + +- [Algorithm](#user-content-algorithm) +- [Compare](#user-content-compare) +- [Concurrency](#user-content-concurrency) +- [Condition](#user-content-condition) +- [Convertor](#user-content-convertor) +- [Cryptor](#user-content-cryptor) +- [Datetime](#user-content-datetime) +- [Datastructure](#user-content-datastructure) +- [Fileutil](#user-content-fileutil) +- [Formatter](#user-content-formatter) +- [Function](#user-content-function) +- [Maputil](#user-content-maputil) +- [Mathutil](#user-content-mathutil) +- [Netutil](#user-content-netutil) +- [Pointer](#user-content-pointer) +- [Random](#user-content-random) +- [Retry](#user-content-retry) +- [Slice](#user-content-slice) +- [Stream](#user-content-stream) +- [Structs](#user-content-structs) +- [Strutil](#user-content-strutil) +- [System](#user-content-system) +- [Tuple](#user-content-tuple) +- [Validator](#user-content-validator) +- [Xerror](#user-content-xerror) + +

1. algorithm 包实现一些基本查找和排序算法。        回到目录

```go -import "github.com/duke-git/lancet/convertor" +import "github.com/duke-git/lancet/v2/algorithm" ``` + #### 函数列表: -- [ColorHexToRGB](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ColorHexToRGB) -- [ColorRGBToHex](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ColorRGBToHex) -- [ToBool](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToBool) -- [ToBytes](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToBytes) -- [ToChar](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToChar) -- [ToInt](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToInt) -- [ToJson](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToJson) -- [ToString](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#ToString) -- [StructToMap](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#StructToMap) - -### cryptor加密包支持数据加密和解密,获取md5,hash值。支持base64, md5, hmac, aes, des, rsa。 + +- **BubbleSort** : 使用冒泡排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#BubbleSort)] + [[play](https://go.dev/play/p/GNdv7Jg2Taj)] +- **CountSort** : 使用计数排序算法对切片进行排序。不改变原数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#CountSort)] + [[play](https://go.dev/play/p/tB-Umgm0DrP)] +- **HeapSort** : 使用堆排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#HeapSort)] + [[play](https://go.dev/play/p/u6Iwa1VZS_f)] +- **InsertionSort** : 使用插入排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#InsertionSort)] + [[play](https://go.dev/play/p/G5LJiWgJJW6)] +- **MergeSort** : 使用合并排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#MergeSort)] + [[play](https://go.dev/play/p/ydinn9YzUJn)] +- **QuickSort** : 使用快速排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#QuickSort)] + [[play](https://go.dev/play/p/7Y7c1Elk3ax)] +- **SelectionSort** : 使用选择排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#SelectionSort)] + [[play](https://go.dev/play/p/oXovbkekayS)] +- **ShellSort** : 使用希尔排序算法对切片进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#ShellSort)] + [[play](https://go.dev/play/p/3ibkszpJEu3)] +- **BinarySearch** : 返回排序切片中目标值的索引,使用二分搜索(递归调用)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#BinarySearch)] + [[play](https://go.dev/play/p/t6MeGiUSN47)] +- **BinaryIterativeSearch** :返回排序切片中目标值的索引,使用二分搜索(非递归)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#BinaryIterativeSearch)] + [[play](https://go.dev/play/p/Anozfr8ZLH3)] +- **LinearSearch** : 基于传入的相等函数返回切片中目标值的索引。(线性查找) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#LinearSearch)] + [[play](https://go.dev/play/p/IsS7rgn5s3x)] +- **LRUCache** : 应用 lru 算法实现内存缓存. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/algorithm.md#LRUCache)] + [[play](https://go.dev/play/p/-EZjgOURufP)] + +

2. compare 包提供几个轻量级的类型比较函数。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/compare" +``` + +#### 函数列表: + +- **Equal** : 检查两个值是否相等(检查类型和值)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#Equal)] + [[play](https://go.dev/play/p/wmVxR-to4lz)] +- **EqualValue** : 检查两个值是否相等(只检查值)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#EqualValue)] + [[play](https://go.dev/play/p/fxnna_LLD9u)] +- **LessThan** : 验证参数`left`的值是否小于参数`right`的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#LessThan)] + [[play](https://go.dev/play/p/cYh7FQQj0ne)] +- **GreaterThan** : 验证参数`left`的值是否大于参数`right`的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#GreaterThan)] + [[play](https://go.dev/play/p/9-NYDFZmIMp)] +- **LessOrEqual** : 验证参数`left`的值是否小于或等于参数`right`的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#LessOrEqual)] + [[play](https://go.dev/play/p/e4T_scwoQzp)] +- **GreaterOrEqual** : 验证参数`left`的值是否大于或等于参数`right`的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#GreaterOrEqual)] + [[play](https://go.dev/play/p/vx8mP0U8DFk)] +- **InDelta** : 检查增量内两个值是否相等。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/compare.md#InDelta)] + +

3. concurrency 包含一些支持并发编程的功能。例如:goroutine, channel, async 等。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/concurrency" +``` + +#### 函数列表: + +- **NewChannel** : 返回一个 Channel 指针实例。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewChannel)] + [[play](https://go.dev/play/p/7aB4KyMMp9A)] +- **Bridge** : 将多个 channel 链接到一个 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/Bridge.md#NewChannel)] + [[play](https://go.dev/play/p/qmWSy1NVF-Y)] +- **FanIn** : 将多个 channel 合并为一个 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#FanIn)] + [[play](https://go.dev/play/p/2VYFMexEvTm)] +- **Generate** : 根据传入的值,生成 channel。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Generate)] + [[play](https://go.dev/play/p/7aB4KyMMp9A)] +- **Or** : 将一个或多个 channel 读取到一个 channel 中,当任何读取 channel 关闭时将结束读取。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Or)] + [[play](https://go.dev/play/p/Wqz9rwioPww)] +- **OrDone** : 将一个 channel 读入另一个 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#OrDone)] + [[play](https://go.dev/play/p/lm_GoS6aDjo)] +- **Repeat** : 返回一个 channel,将参数`values`重复放入 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Repeat)] + [[play](https://go.dev/play/p/k5N_ALVmYjE)] +- **RepeatFn** : 返回一个 channel,重复执行函数 fn,并将结果放入返回的 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#RepeatFn)] + [[play](https://go.dev/play/p/4J1zAWttP85)] +- **Take** : 返回一个 channel,其值从另一个 channel 获取,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Take)] + [[play](https://go.dev/play/p/9Utt-1pDr2J)] +- **Tee** : 将一个 channel 分成两个 channel,直到取消上下文。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Tee)] + [[play](https://go.dev/play/p/3TQPKnCirrP)] +- **NewKeyedLocker** : 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)] +- **Do** :为指定的键获取锁并执行提供的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Do)] + [[play](https://go.dev/play/p/GzeyC33T5rw)] +- **NewRWKeyedLocker** :RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#NewRWKeyedLocker)] + [[play](https://go.dev/play/p/ZrCr8sMo77T)] +- **RLock** : 为指定的键获取读锁并执行提供的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#RLock)] + [[play](https://go.dev/play/p/ZrCr8sMo77T)] +- **Lock** : 为指定的键获取锁并执行提供的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Lock)] + [[play](https://go.dev/play/p/WgAcXbOPKGk)] +- **NewTryKeyedLocker** : 创建一个 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)] +- **TryLock** : 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)] +- **Unlock** : 释放指定键的锁。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/concurrency.md#Unlock)] + [[play](https://go.dev/play/p/VG9qLvyetE2)] + +

4. condition 包含一些用于条件判断的函数。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/condition" +``` + +#### 函数列表: + +- **Bool** : 返回传入参数的 bool 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Bool)] + [[play](https://go.dev/play/p/ETzeDJRSvhm)] +- **And** : 逻辑且操作,当切仅当 a 和 b 都为 true 时返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#And)] + [[play](https://go.dev/play/p/W1SSUmt6pvr)] +- **Or** : 逻辑或操作,当切仅当 a 和 b 都为 false 时返回 false。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Or)] + [[play](https://go.dev/play/p/UlQTxHaeEkq)] +- **Xor** : 逻辑异或操作,a 和 b 相同返回 false,a 和 b 不相同返回 true + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Xor)] + [[play](https://go.dev/play/p/gObZrW7ZbG8)] +- **Nor** : 异或的取反操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Nor)] + [[play](https://go.dev/play/p/g2j08F_zZky)] +- **Xnor** : 如果 a 和 b 都是真的或 a 和 b 均是假的,则返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Xnor)] + [[play](https://go.dev/play/p/OuDB9g51643)] +- **Nand** : 如果 a 和 b 都为真,返回 false,否则返回 true + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#Nand)] + [[play](https://go.dev/play/p/vSRMLxLIbq8)] +- **TernaryOperator** : 三元运算符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/condition.md#TernaryOperator)] + [[play](https://go.dev/play/p/ElllPZY0guT)] + +

5. convertor 转换器包支持一些常见的数据类型转换。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/convertor" +``` + +#### 函数列表: + +- **ColorHexToRGB** : 颜色值十六进制转 rgb。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ColorHexToRGB)] + [[play](https://go.dev/play/p/o7_ft-JCJBV)] +- **ColorRGBToHex** : 颜色值 rgb 转十六进制。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ColorRGBToHex)] + [[play](https://go.dev/play/p/nzKS2Ro87J1)] +- **ToBool** : 字符串转布尔类型,使用 strconv.ParseBool。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToBool)] + [[play](https://go.dev/play/p/ARht2WnGdIN)] +- **ToBytes** : interface 转字节切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToBytes)] + [[play](https://go.dev/play/p/fAMXYFDvOvr)] +- **ToChar** : 字符串转字符切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToChar)] + [[play](https://go.dev/play/p/JJ1SvbFkVdM)] +- **ToChannel** : 将切片转为只读 channel。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToChannel)] + [[play](https://go.dev/play/p/hOx_oYZbAnL)] +- **ToFloat** : 将 interface 转成 float64 类型,如果参数无法转换,会返回 0.0 和 error。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToFloat)] + [[play](https://go.dev/play/p/4YTmPCibqHJ)] +- **ToInt** : 将 interface 转成 int64 类型,如果参数无法转换,会返回 0 和 error。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToInt)] + [[play](https://go.dev/play/p/9_h9vIt-QZ_b)] +- **ToJson** : 将 interface 转成 json 字符串,如果参数无法转换,会返回""和 error。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToJson)] + [[play](https://go.dev/play/p/2rLIkMmXWvR)] +- **ToMap** : 将切片转为 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToMap)] + [[play](https://go.dev/play/p/tVFy7E-t24l)] +- **ToPointer** : 返回传入值的指针。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToPointer)] + [[play](https://go.dev/play/p/ASf_etHNlw1)] +- **ToString** : 将值转换为字符串,对于数字、字符串、[]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)] +- **StructToMap** : 将 struct 转成 map,只会转换 struct 中可导出的字段。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#StructToMap)] + [[play](https://go.dev/play/p/KYGYJqNUBOI)] +- **MapToSlice** : map 中 key 和 value 执行函数 iteratee 后,转为切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#MapToSlice)] + [[play](https://go.dev/play/p/dmX4Ix5V6Wl)] +- **EncodeByte** : 将传入的 data 编码成字节切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#EncodeByte)] + [[play](https://go.dev/play/p/DVmM1G5JfuP)] +- **DecodeByte** : 解码字节切片到目标对象,目标对象需要传入一个指针实例。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#DecodeByte)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **DeepClone** : 创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#DeepClone)] + [[play](https://go.dev/play/p/j4DP5dquxnk)] +- **CopyProperties** : 拷贝不同结构体之间的同名字段。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#CopyProperties)] + [[play](https://go.dev/play/p/oZujoB5Sgg5)] +- **ToInterface** : 将反射值转换成对应的 interface 类型。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToInterface)] + [[play](https://go.dev/play/p/syqw0-WG7Xd)] +- **Utf8ToGbk** : utf8 编码转 GBK 编码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#Utf8ToGbk)] + [[play](https://go.dev/play/p/9FlIaFLArIL)] +- **GbkToUtf8** : 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)] +- **ToStdBase64** : 将值转换为 StdBase64 编码的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)] + [[play](https://go.dev/play/p/_fLJqJD3NMo)] +- **ToUrlBase64** : 将值转换为 url Base64 编码的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)] + [[play](https://go.dev/play/p/C_d0GlvEeUR)] +- **ToRawStdBase64** : 将值转换为 RawStdBase64 编码的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)] + [[play](https://go.dev/play/p/wSAr3sfkDcv)] +- **ToRawUrlBase64** : 将值转换为 RawUrlBase64 编码的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)] + [[play](https://go.dev/play/p/HwdDPFcza1O)] +- **ToBigInt** : 将整数转为\*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)] + +

6. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/cryptor" +``` + +#### 函数列表: + +- **AesEcbEncrypt** : 使用 AES ECB 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesEcbEncrypt)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **AesEcbDecrypt** : 使用 AES ECB 算法模解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesEcbDecrypt)] + [[play](https://go.dev/play/p/zI6xsmuQRbn)] +- **AesCbcEncrypt** : 使用 AES CBC 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCbcEncrypt)] + [[play](https://go.dev/play/p/IOq_g8_lKZD)] +- **AesCbcDecrypt** : 使用 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)] +- **AesCtrCryptdeprecated** : 使用 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)] +- **AesCtrEncrypt** : 使用 AES CTR 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCtrCrypt)] + [[play](https://go.dev/play/p/x6pjPAvThRz)] +- **AesCtrDecrypt** : 使用 AES CTR 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCtrCrypt)] + [[play](https://go.dev/play/p/x6pjPAvThRz)] +- **AesCfbEncrypt** : 使用 AES CFB 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCfbEncrypt)] + [[play](https://go.dev/play/p/tfkF10B13kH)] +- **AesCfbDecrypt** : 使用 AES CFB 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesCfbDecrypt)] + [[play](https://go.dev/play/p/tfkF10B13kH)] +- **AesOfbEncrypt** : 使用 AES OFB 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesOfbEncrypt)] + [[play](https://go.dev/play/p/VtHxtkUj-3F)] +- **AesOfbDecrypt** : 使用 AES OFB 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesOfbDecrypt)] + [[play](https://go.dev/play/p/VtHxtkUj-3F)] +- **AesGcmEncrypt** : 使用 AES GCM 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmEncrypt)] + [[play](https://go.dev/play/p/rUt0-DmsPCs)] +- **AesGcmDecrypt** : 使用 AES GCM 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmDecrypt)] + [[play](https://go.dev/play/p/rUt0-DmsPCs)] +- **Base64StdEncode** : 将字符串 base64 编码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Base64StdEncode)] + [[play](https://go.dev/play/p/VOaUyQUreoK)] +- **Base64StdDecode** : 解码 base64 字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Base64StdDecode)] + [[play](https://go.dev/play/p/RWQylnJVgIe)] +- **DesEcbEncrypt** : 使用 DES ECB 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesEcbEncrypt)] + [[play](https://go.dev/play/p/8qivmPeZy4P)] +- **DesEcbDecrypt** : 使用 DES ECB 算法模解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesEcbDecrypt)] + [[play](https://go.dev/play/p/8qivmPeZy4P)] +- **DesCbcEncrypt** : 使用 DES CBC 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCbcEncrypt)] + [[play](https://go.dev/play/p/4cC4QvWfe3_1)] +- **DesCbcDecrypt** : 使用 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)] +- **DesCtrCryptdeprecated** : 使用 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)] +- **DesCtrEncrypt** : 使用 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)] +- **DesCtrDecrypt** : 使用 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)] +- **DesCfbEncrypt** : 使用 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)] +- **DesCfbDecrypt** : 使用 DES CFB 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesCfbDecrypt)] + [[play](https://go.dev/play/p/y-eNxcFBlxL)] +- **DesOfbEncrypt** : 使用 DES OFB 算法模式加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesOfbEncrypt)] + [[play](https://go.dev/play/p/74KmNadjN1J)] +- **DesOfbDecrypt** : 使用 DES OFB 算法模式解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#DesOfbDecrypt)] + [[play](https://go.dev/play/p/74KmNadjN1J)] +- **HmacMd5** : 返回字符串 md5 hmac 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacMd5)] + [[play](https://go.dev/play/p/uef0q1fz53I)] +- **HmacMd5WithBase64** : 获取字符串 md5 hmac base64 字符串值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacMd5WithBase64)] + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacMd5WithBase64)] +- **HmacSha1** : 返回字符串 sha1 hmac 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha1)] + [[play](https://go.dev/play/p/1UI4oQ4WXKM)] +- **HmacSha1WithBase64** : 获取字符串的 sha1 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha1WithBase64)] + [[play](https://go.dev/play/p/47JmmGrnF7B)] +- **HmacSha256** : 返回字符串 sha256 hmac 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha256)] + [[play](https://go.dev/play/p/HhpwXxFhhC0)] +- **HmacSha256WithBase64** : 获取字符串 sha256 hmac base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha256WithBase64)] + [[play](https://go.dev/play/p/EKbkUvPTLwO)] +- **HmacSha512** : 返回字符串 sha256 hmac 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha512)] + [[play](https://go.dev/play/p/59Od6m4A0Ud)] +- **HmacSha512WithBase64** : 获取字符串 sha512 hmac base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#HmacSha512WithBase64)] + [[play](https://go.dev/play/p/c6dSe3E2ydU)] +- **Md5Byte** : 返回 byte slice 的 md5 值. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Md5Byte)] + [[play](https://go.dev/play/p/suraalH8lyC)] +- **Md5ByteWithBase64** : 获取 byte slice 的 md5 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Md5ByteWithBase64)] + [[play](https://go.dev/play/p/Tcb-Z7LN2ax)] +- **Md5String** : 返回字符串 md5 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Md5String)] + [[play](https://go.dev/play/p/1bLcVetbTOI)] +- **Md5StringWithBase64** : 获取字符串 md5 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Md5StringWithBase64)] + [[play](https://go.dev/play/p/Lx4gH7Vdr5_y)] +- **Md5File** : 返回文件 md5 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Md5File)] +- **Sha1** : 返回字符串 sha1 哈希值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha1)] + [[play](https://go.dev/play/p/_m_uoD1deMT)] +- **Sha1WithBase64** : 获取字符串 sha1 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha1WithBase64)] + [[play](https://go.dev/play/p/fSyx-Gl2l2-)] +- **Sha256** :返回字符串 sha256 哈希值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha256)] + [[play](https://go.dev/play/p/tU9tfBMIAr1)] +- **Sha256WithBase64** : 获取字符串 sha256 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha256WithBase64)] + [[play](https://go.dev/play/p/85IXJHIal1k)] +- **Sha512** : 返回字符串 sha512 哈希值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha512)] + [[play](https://go.dev/play/p/3WsvLYZxsHa)] +- **Sha512WithBase64** : 获取字符串 sha512 base64 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sha512WithBase64)] + [[play](https://go.dev/play/p/q_fY2rA-k5I)] +- **GenerateRsaKey** : 在当前目录下创建 rsa 私钥文件和公钥文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKey)] + [[play](https://go.dev/play/p/zutRHrDqs0X)] +- **RsaEncrypt** : 用公钥文件 ras 加密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncrypt)] + [[play](https://go.dev/play/p/7_zo6mrx-eX)] +- **RsaDecrypt** : 用私钥文件 rsa 解密数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecrypt)] + [[play](https://go.dev/play/p/7_zo6mrx-eX)] +- **GenerateRsaKeyPair** : 创建 rsa 公钥私钥和 key。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKeyPair)] + [[play](https://go.dev/play/p/sSVmkfENKMz)] +- **RsaEncryptOAEP** : rsa OAEP 加密。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncryptOAEP)] + [[play](https://go.dev/play/p/sSVmkfENKMz)] +- **RsaDecryptOAEP** : rsa OAEP 解密。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecryptOAEP)] + [[play](https://go.dev/play/p/sSVmkfENKMz)] +- **RsaSign** : 应用 RSA 算法签名数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaSign)] + [[play](https://go.dev/play/p/qhsbf8BJ6Mf)] +- **RsaVerifySign** : 验证数据的签名是否符合 RSA 算法。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)] + [[play](https://go.dev/play/p/qhsbf8BJ6Mf)] + +

7. datetime日期时间处理包,格式化日期,比较日期。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/datetime" +``` + +#### 函数列表: + +- **AddDay** : 将日期加/减天数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddDay)] + [[play](https://go.dev/play/p/dIGbs_uTdFa)] +- **AddHour** : 将日期加/减小时数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddHour)] + [[play](https://go.dev/play/p/rcMjd7OCsi5)] +- **AddMinute** : 将日期加/减分钟数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMinute)] + [[play](https://go.dev/play/p/nT1heB1KUUK)] +- **AddWeek** : 将日期加/减星期数. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddWeek)] + [[play](https://go.dev/play/p/M9TqdMiaA2p)] +- **AddMonth** : 将日期加/减月数. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMonth)] + [[play](https://go.dev/play/p/DLoiOnpLvsN)] +- **AddYear** : 将日期加/减分年数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYear)] + [[play](https://go.dev/play/p/MqW2ujnBx10)] +- **AddDaySafe** : 增加/减少指定的天数,并确保日期是有效日期。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddDaySafe)] + [[play](https://go.dev/play/p/JTohZFpoDJ3)] +- **AddMonthSafe** : 增加/减少指定的月份,并确保日期是有效日期。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddMonthSafe)] + [[play](https://go.dev/play/p/KLw0lo6mbVW)] +- **AddYearSafe** : 增加/减少指定的年份,并确保日期是有效日期。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#AddYearSafe)] + [[play](https://go.dev/play/p/KVGXWZZ54ZH)] +- **BeginOfMinute** : 返回指定时间的分钟开始时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMinute)] + [[play](https://go.dev/play/p/ieOLVJ9CiFT)] +- **BeginOfHour** : 返回指定时间的小时开始时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfHour)] + [[play](https://go.dev/play/p/GhdGFnDWpYs)] +- **BeginOfDay** : 返回指定时间的当天开始时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfDay)] + [[play](https://go.dev/play/p/94m_UT6cWs9)] +- **BeginOfWeek** : 返回指定时间的每周开始时间,默认开始时间星期日。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfWeek)] + [[play](https://go.dev/play/p/DCHdcL6gnfV)] +- **BeginOfMonth** : 返回指定时间的当月开始时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfMonth)] + [[play](https://go.dev/play/p/bWXVFsmmzwL)] +- **BeginOfYear** : 返回指定时间的当年开始时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BeginOfYear)] + [[play](https://go.dev/play/p/i326DSwLnV8)] +- **EndOfMinute** : 返回指定时间的分钟结束时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfMinute)] + [[play](https://go.dev/play/p/yrL5wGzPj4z)] +- **EndOfHour** : 返回指定时间的小时结束时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfHour)] + [[play](https://go.dev/play/p/6ce3j_6cVqN)] +- **EndOfDay** : 返回指定时间的当天结束时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfDay)] + [[play](https://go.dev/play/p/eMBOvmq5Ih1)] +- **EndOfWeek** : 返回指定时间的星期结束时间,默认结束时间星期六。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfWeek)] + [[play](https://go.dev/play/p/mGSA162YgX9)] +- **EndOfMonth** : 返回指定时间的月份结束时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfMonth)] + [[play](https://go.dev/play/p/_GWh10B3Nqi)] +- **EndOfYear** : 返回指定时间的年份结束时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#EndOfYear)] + [[play](https://go.dev/play/p/G01cKlMCvNm)] +- **GetNowDate** : 获取当天日期,返回格式:yyyy-mm-dd。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetNowDate)] + [[play](https://go.dev/play/p/PvfkPpcpBBf)] +- **GetNowTime** : 获取当时时间,返回格式:hh:mm:ss。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetNowTime)] + [[play](https://go.dev/play/p/l7BNxCkTmJS)] +- **GetNowDateTime** : 获取当时日期和时间,返回格式:yyyy-mm-dd hh:mm:ss。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetNowDateTime)] + [[play](https://go.dev/play/p/pI4AqngD0al)] +- **GetTodayStartTime** : 返回当天开始时间, 格式: yyyy-mm-dd 00:00:00。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetTodayStartTime)] + [[play](https://go.dev/play/p/84siyYF7t99)] +- **GetTodayEndTime** : 返回当天结束时间,格式: yyyy-mm-dd 23:59:59。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetTodayEndTime)] + [[play](https://go.dev/play/p/jjrLnfoqgn3)] +- **GetZeroHourTimestamp** : 获取零时时间戳(timestamp of 00:00)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetZeroHourTimestamp)] + [[play](https://go.dev/play/p/QmL2oIaGE3q)] +- **GetNightTimestamp** : 获取午夜时间戳(timestamp of 23:59)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GetNightTimestamp)] + [[play](https://go.dev/play/p/UolysR3MYP1)] +- **FormatTimeToStr** : 将日期格式化成字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#FormatTimeToStr)] + [[play](https://go.dev/play/p/_Ia7M8H_OvE)] +- **FormatStrToTime** : 将字符串格式化成时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#FormatStrToTime)] + [[play](https://go.dev/play/p/1h9FwdU8ql4)] +- **NewUnix** : 创建一个 unix 时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#NewUnix)] + [[play](https://go.dev/play/p/psoSuh_kLRt)] +- **NewUnixNow** : 创建一个当前时间的 unix 时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#NewUnixNow)] + [[play](https://go.dev/play/p/U4PPx-9D0oz)] +- **NewFormat** : 创建一个 yyyy-mm-dd hh:mm:ss 格式时间字符串的 unix 时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#NewFormat)] + [[play](https://go.dev/play/p/VkW08ZOaXPZ)] +- **NewISO8601** : 创建一个 iso8601 格式时间字符串的 unix 时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#NewISO8601)] + [[play](https://go.dev/play/p/mkhOHQkdeA2)] +- **ToUnix** : 返回 unix 时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#ToUnix)] + [[play](https://go.dev/play/p/_LUiwAdocjy)] +- **ToFormat** : 返回格式'yyyy-mm-dd hh:mm:ss'的日期字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#ToFormat)] + [[play](https://go.dev/play/p/VkW08ZOaXPZ)] +- **ToFormatForTpl** : 返回 tpl 格式指定的日期字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#ToFormatForTpl)] + [[play](https://go.dev/play/p/nyXxXcQJ8L5)] +- **ToIso8601** : 返回 iso8601 日期字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#ToIso8601)] + [[play](https://go.dev/play/p/mkhOHQkdeA2)] +- **IsLeapYear** :验证是否是闰年。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#IsLeapYear)] + [[play](https://go.dev/play/p/xS1eS2ejGew)] +- **BetweenSeconds** : 返回两个时间的间隔秒数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BetweenSeconds)] + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BetweenSeconds)] +- **DayOfYear** : 返回参数日期是一年中的第几天。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DayOfYear)] + [[play](https://go.dev/play/p/0hjqhTwFNlH)] +- **IsWeekend** : 判断日期是否是周末。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#IsWeekend)] + [[play](https://go.dev/play/p/cupRM5aZOIY)] +- **NowDateOrTime** : 根据指定的格式和时区返回当前时间字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#NowDateOrTime)] + [[play](https://go.dev/play/p/EZ-begEjtT0)] +- **Timestamp** : 返回当前秒级时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Timestamp)] + [[play](https://go.dev/play/p/iU5b7Vvjx6x)] +- **TimestampMilli** : 返回当前毫秒级时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampMilli)] + [[play](https://go.dev/play/p/4gvEusOTu1T)] +- **TimestampMicro** : 返回当前微秒级时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampMicro)] + [[play](https://go.dev/play/p/2maANglKHQE)] +- **TimestampNano** : 返回当前纳秒级时间戳。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampNano)] + [[play](https://go.dev/play/p/A9Oq_COrcCF)] +- **TrackFuncTime** : 测试函数执行时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TrackFuncTime)] + [[play](https://go.dev/play/p/QBSEdfXHPTp)] +- **DaysBetween** : 返回两个日期之间的天数差。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)] + [[play](https://go.dev/play/p/qD6qGb3TbOy)] +- **GenerateDatetimesBetween** : 生成从 start 到 end 的所有日期时间的字符串列表。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)] + [[play](https://go.dev/play/p/6kHBpAxD9ZC)] +- **Min** : 返回最早时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Min)] + [[play](https://go.dev/play/p/MCIDvHNOGGb)] +- **Max** : 返回最晚时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#Max)] + [[play](https://go.dev/play/p/9m6JMk1LB7-)] +- **MaxMin** : 返回最早和最晚时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#MaxMin)] + [[play](https://go.dev/play/p/rbW51cDtM_2)] + +

8. datastructure 包含一些普通的数据结构实现。例如:list, linklist, stack, queue, set, tree, graph。       回到目录

```go -import "github.com/duke-git/lancet/cryptor" +import list "github.com/duke-git/lancet/v2/datastructure/list" +import copyonwritelist "github.com/duke-git/lancet/v2/datastructure/copyonwritelist" +import link "github.com/duke-git/lancet/v2/datastructure/link" +import stack "github.com/duke-git/lancet/v2/datastructure/stack" +import queue "github.com/duke-git/lancet/v2/datastructure/queue" +import set "github.com/duke-git/lancet/v2/datastructure/set" +import tree "github.com/duke-git/lancet/v2/datastructure/tree" +import heap "github.com/duke-git/lancet/v2/datastructure/heap" +import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" ``` #### 函数列表: -- [AesEcbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesEcbEncrypt) -- [AesEcbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesEcbDecrypt) -- [AesCbcEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCbcEncrypt) -- [AesCbcDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCbcDecrypt) -- [AesCtrCrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCtrCrypt) -- [AesCfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCfbEncrypt) -- [AesCfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCfbDecrypt) -- [AesOfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesOfbEncrypt) -- [AesOfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesOfbDecrypt) -- [Base64StdEncode](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Base64StdEncode) -- [Base64StdDecode](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Base64StdDecode) -- [DesEcbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesEcbEncrypt) -- [DesEcbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesEcbDecrypt) -- [DesCbcEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCbcEncrypt) -- [DesCbcDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCbcDecrypt) -- [DesCtrCrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCtrCrypt) -- [DesCfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCfbEncrypt) -- [DesCfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCfbDecrypt) -- [DesOfbEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesOfbEncrypt) -- [DesOfbDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesOfbDecrypt) -- [HmacMd5](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacMd5) -- [HmacSha1](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha1) -- [HmacSha256](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha256) -- [HmacSha512](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha512) -- [Md5String](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Md5String) -- [Md5File](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Md5File) -- [Sha1](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha1) -- [Sha256](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha256) -- [Sha512](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha512) -- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#GenerateRsaKey) -- [RsaEncrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaEncrypt) -- [RsaDecrypt](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaDecrypt) - -### datetime日期时间处理包,格式化日期,比较日期。 +- **List** : 线性表结构, 用切片实现。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/list.md)] +- **CopyOnWriteList** : 是一个线程安全的 List 实现,底层使用 go 切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/copyonwritelist.md)] +- **Link** : 链表解构, 包括单链表和双向链表。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/link.md)] +- **Stack** : 栈结构(fifo), 包括数组栈和链表栈。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/stack.md)] +- **Queue** : 队列结构(filo), 包括数组队列,链表队列,循环队列,优先级队列。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/queue.md)] +- **Set** : 集合(set)结构。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/set.md)] +- **Tree** : 二叉搜索树。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/tree.md)] +- **Heap** : 二叉 max 堆。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/heap.md)] +- **Hashmap** : 哈希映射。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datastructure/hashmap.md)] + +

9. EventbBus是一个事件总线,用于在应用程序中处理事件。       回到目录

```go -import "github.com/duke-git/lancet/datetime" +import "github.com/duke-git/lancet/v2/eventbus" ``` + #### 函数列表: -- [AddDay](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#AddDay) -- [AddHour](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#AddHour) -- [AddMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#AddMinute) -- [BeginOfMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfMinute) -- [BeginOfHour](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfHour) -- [BeginOfDay](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfDay) -- [BeginOfWeek](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfWeek) -- [BeginOfMonth](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfMonth) -- [BeginOfYear](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BeginOfYear) -- [EndOfMinute](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfMinute) -- [EndOfHour](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfHour) -- [EndOfDay](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfDay) -- [EndOfWeek](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfWeek) -- [EndOfMonth](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfMonth) -- [EndOfYear](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#EndOfYear) -- [GetNowDate](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#GetNowDate) -- [GetNowTime](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#GetNowTime) -- [GetNowDateTime](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#GetNowDateTime) -- [GetZeroHourTimestamp](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#GetZeroHourTimestamp) -- [GetNightTimestamp](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#GetNightTimestamp) -- [FormatTimeToStr](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#FormatTimeToStr) -- [FormatStrToTime](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#FormatStrToTime) - -### fileutil包支持文件基本操作。 + +- **NewEventBus** : 创建 EventBus 实例。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#NewEventBus)] + [[play](https://go.dev/play/p/gHbOPV_NUOJ)] +- **Subscribe** : 订阅具有特定事件主题和监听函数的事件。支持异步,事件优先级,事件过滤器。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Subscribe)] + [[play](https://go.dev/play/p/EYGf_8cHei-)] +- **Unsubscribe** : 取消订阅具有特定事件主题的事件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Unsubscribe)] + [[play](https://go.dev/play/p/Tmh7Ttfvprf)] +- **Publish** : 发布一个带有特定事件主题和数据负载的事件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#Publish)] + [[play](https://go.dev/play/p/gHTtVexFSH9)] +- **ClearListeners** : 清空所有事件监听器。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#ClearListeners)] + [[play](https://go.dev/play/p/KBfBYlKPgqD)] +- **ClearListenersByTopic** : 清空特定事件监听器。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#ClearListenersByTopic)] + [[play](https://go.dev/play/p/gvMljmJOZmU)] +- **GetListenersCount** : 获取特定事件的监听器数量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetListenersCount)] + [[play](https://go.dev/play/p/8VPJsMQgStM)] +- **GetAllListenersCount** : 获取所有事件的监听器数量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetAllListenersCount)] + [[play](https://go.dev/play/p/PUlr0xcpEOz)] +- **GetEvents** : 获取所有事件的 topic。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#GetEvents)] + [[play](https://go.dev/play/p/etgjjcOtAjX)] +- **SetErrorHandler** : 设置事件的错误处理函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/eventbus.md#SetErrorHandler)] + [[play](https://go.dev/play/p/gmB0gnFe5mc)] + +

10. fileutil 包含文件基本操作。       回到目录

```go -import "github.com/duke-git/lancet/fileutil" +import "github.com/duke-git/lancet/v2/fileutil" ``` #### 函数列表: -- [ClearFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#ClearFile) -- [CreateFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#CreateFile) -- [CopyFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#CopyFile) -- [FileMode](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#FileMode) -- [MiMeType](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#MiMeType) -- [IsExist](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#IsExist) -- [IsLink](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#IsLink) -- [IsDir](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#IsDir) -- [ListFileNames](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#ListFileNames) -- [RemoveFile](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#RemoveFile) -- [ReadFileToString](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#ReadFileToString) -- [ReadFileByLine](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#ReadFileByLine) -- [Zip](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#Zip) -- [UnZip](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#UnZip) - -### formatter格式化器包含一些数据格式化处理方法。 +- **ClearFile** : 清空文件内容。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ClearFile)] + [[play](https://go.dev/play/p/NRZ0ZT-G94H)] +- **CreateFile** : 创建文件,创建成功返回 true, 否则返回 false。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateFile)] + [[play](https://go.dev/play/p/lDt8PEsTNKI)] +- **CreateDir** : 创建嵌套目录,例如/a/, /a/b/。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateDir)] + [[play](https://go.dev/play/p/qUuCe1OGQnM)] +- **CopyFile** : 拷贝文件,会覆盖原有的文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyFile)] + [[play](https://go.dev/play/p/Jg9AMJMLrJi)] +- **CopyDir** : 拷贝目录。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyDir)] + [[play](https://go.dev/play/p/YAyFTA_UuPb)] +- **FileMode** : 获取文件 mode 信息。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileMode)] + [[play](https://go.dev/play/p/2l2hI42fA3p)] +- **MiMeType** : 获取文件 mime 类型, 参数的类型必须是 string 或者\*os.File。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#MiMeType)] + [[play](https://go.dev/play/p/bd5sevSUZNu)] +- **IsExist** : 判断文件或目录是否存在。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#IsExist)] + [[play](https://go.dev/play/p/nKKXt8ZQbmh)] +- **IsLink** : 判断文件是否是符号链接。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#IsLink)] + [[play](https://go.dev/play/p/TL-b-Kzvf44)] +- **IsDir** : 判断参数是否是目录。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#IsDir)] + [[play](https://go.dev/play/p/WkVwEKqtOWk)] +- **ListFileNames** : 返回目录下所有文件名。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ListFileNames)] + [[play](https://go.dev/play/p/Tjd7Y07rejl)] +- **RemoveFile** : 删除文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveFile)] + [[play](https://go.dev/play/p/P2y0XW8a1SH)] +- **RemoveDir** : 删除目录,支持传入删除前的回调函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#RemoveDir)] + [[play](https://go.dev/play/p/Oa6KnPek2uy)] +- **ReadFileToString** : 读取文件内容并返回字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFileToString)] + [[play](https://go.dev/play/p/cmfwp_5SQTp)] +- **ReadFileByLine** : 按行读取文件内容,返回字符串切片包含每一行。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFileByLine)] + [[play](https://go.dev/play/p/svJP_7ZrBrD)] +- **Zip** : zip 压缩文件, 参数可以是文件或目录。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#Zip)] + [[play](https://go.dev/play/p/j-3sWBp8ik_P)] +- **ZipAppendEntry** : 通过将单个文件或目录追加到现有的 zip 文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ZipAppendEntry)] +- **UnZip** : zip 解压缩文件并保存在目录中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#UnZip)] + [[play](https://go.dev/play/p/g0w34kS7B8m)] +- **CurrentPath** : 返回当前位置的绝对路径。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CurrentPath)] + [[play](https://go.dev/play/p/s74a9iBGcSw)] +- **IsZipFile** : 判断文件是否是 zip 压缩文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#IsZipFile)] + [[play](https://go.dev/play/p/9M0g2j_uF_e)] +- **FileSize** : 返回文件字节大小。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileSize)] + [[play](https://go.dev/play/p/H9Z05uD-Jjc)] +- **MTime** : 返回文件修改时间(unix timestamp)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#MTime)] + [[play](https://go.dev/play/p/s_Tl7lZoAaY)] +- **Sha** : 返回文件 sha 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#Sha)] + [[play](https://go.dev/play/p/VfEEcO2MJYf)] +- **ReadCsvFile** : 读取 csv 文件内容到切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)] + [[play](https://go.dev/play/p/OExTkhGEd3_u)] +- **WriteCsvFile** : 向 csv 文件写入切片数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)] +- **WriteMapsToCsv** : 将 map 切片写入 csv 文件中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)] + [[play](https://go.dev/play/p/umAIomZFV1c)] +- **WriteBytesToFile** : 将 bytes 写入文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)] + [[play](https://go.dev/play/p/s7QlDxMj3P8)] +- **WriteStringToFile** : 将字符串写入文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteStringToFile)] + [[play](https://go.dev/play/p/GhLS6d8lH_g)] +- **ReadFile** : 读取文件或者 URL。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)] +- **ChunkRead** : 从文件的指定偏移读取块并返回块内所有行。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)] + [[play](https://go.dev/play/p/r0hPmKWhsgf)] +- **ParallelChunkRead** : 并行读取文件并将每个块的行发送到指定通道。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)] + [[play](https://go.dev/play/p/teMXnCsdSEw)] +- **GetExeOrDllVersion** : 返回 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)] + +

11. formatter 格式化器包含一些数据格式化处理方法。       回到目录

```go -import "github.com/duke-git/lancet/formatter" +import "github.com/duke-git/lancet/v2/formatter" ``` + #### 函数列表: -- [Comma](https://github.com/duke-git/lancet/blob/main/docs/formatter_zh-CN.md#Comma) +- **Comma** : 用逗号每隔 3 位分割数字/字符串,支持添加前缀符号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#Comma)] + [[play](https://go.dev/play/p/eRD5k2vzUVX)] +- **Pretty** : 返回 pretty JSON 字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#Pretty)] + [[play](https://go.dev/play/p/YsciGj3FH2x)] +- **PrettyToWriter** : Pretty encode 数据到 writer。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#PrettyToWriter)] + [[play](https://go.dev/play/p/LPLZ3lDi5ma)] +- **DecimalBytes** : 返回十进制标准(以 1000 为基数)下的可读字节单位字符串。precision 参数指定小数点后的位数,默认为 4。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#DecimalBytes)] + [[play](https://go.dev/play/p/FPXs1suwRcs)] +- **BinaryBytes** : 返回 binary 标准(以 1024 为基数)下的可读字节单位字符串。precision 参数指定小数点后的位数,默认为 4。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#BinaryBytes)] + [[play](https://go.dev/play/p/G9oHHMCAZxP)] +- **ParseDecimalBytes** : 将字节单位字符串转换成其所表示的字节数(以 1000 为基数)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#ParseDecimalBytes)] + [[play](https://go.dev/play/p/Am98ybWjvjj)] +- **ParseBinaryBytes** : 将字节单位字符串转换成其所表示的字节数(以 1024 为基数)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#ParseBinaryBytes)] + [[play](https://go.dev/play/p/69v1tTT62x8)] + +

12. function 函数包控制函数执行流程,包含部分函数式编程。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/function" +``` + +#### 函数列表: -### function函数包控制函数执行流程,包含部分函数式编程。 +- **After** : 创建一个函数,当该函数被调用 n 或更多次之后将执行传入的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#After)] + [[play](https://go.dev/play/p/eRD5k2vzUVX)] +- **Before** : 创建一个函数,当该函数被调用不超过 n 次时,将执行执行传入的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Before)] + [[play](https://go.dev/play/p/0HqUDIFZ3IL)] +- **CurryFn** : 创建柯里化函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#CurryFn)] + [[play](https://go.dev/play/p/5HopfDwANKX)] +- **Compose** : 从右至左组合函数列表 fnList,返回组合后的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Compose)] + [[play](https://go.dev/play/p/KKfugD4PKYF)] +- **Delay** : 延迟 delay 时间后调用函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Delay)] + [[play](https://go.dev/play/p/Ivtc2ZE-Tye)] +- **Debounceddeprecated** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounced)] + [[play](https://go.dev/play/p/absuEGB_GN7)] +- **Debounce** : 创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounce)] + [[play](https://go.dev/play/p/-dGFrYn_1Zi)] +- **Throttle** : 创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Throttle)] + [[play](https://go.dev/play/p/HpoMov-tJSN)] +- **Schedule** : 每次持续时间调用函数,直到关闭返回的 channel。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Schedule)] + [[play](https://go.dev/play/p/hbON-Xeyn5N)] +- **Pipeline** : 从右至左执行函数列表。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)] + [[play](https://go.dev/play/p/mPdUVvj6HD6)] +- **AcceptIf** : AcceptIf 函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)] + [[play](https://go.dev/play/p/XlXHHtzCf7d)] +- **And** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 and 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)] + [[play](https://go.dev/play/p/dTBHJMQ0zD2)] +- **Or** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑 or 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)] + [[play](https://go.dev/play/p/LitCIsDFNDA)] +- **Negate** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)] + [[play](https://go.dev/play/p/jbI8BtgFnVE)] +- **Nor** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或 nor 的操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)] + [[play](https://go.dev/play/p/2KdCoBEOq84)] +- **Nand** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与 nand 的操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)] + [[play](https://go.dev/play/p/Rb-FdNGpgSO)] +- **Xnor** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或 xnor 的操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)] + [[play](https://go.dev/play/p/FJxko8SFbqc)] +- **Watcher** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)] + [[play](https://go.dev/play/p/l2yrOpCLd1I)] + +

13. maputil 包括一些操作 map 的函数。       回到目录

```go -import "github.com/duke-git/lancet/function" +import "github.com/duke-git/lancet/v2/maputil" ``` #### 函数列表: -- [After](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#After) -- [Before](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Before) -- [Curry](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Curry) -- [Compose](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Compose) -- [Debounced](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Debounced) -- [Delay](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Delay) -- [Watcher](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Watcher) -### mathutil包实现了一些数学计算的函数。 +- **MapTo** : 快速将 map 或者其他类型映射到结构体或者指定类型。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapTo)] + [[play](https://go.dev/play/p/4K7KBEPgS5M)] +- **ForEach** : 对 map 中的每对 key 和 value 执行 iteratee 函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ForEach)] + [[play](https://go.dev/play/p/OaThj6iNVXK)] +- **Filter** : 迭代 map 中的每对 key 和 value,返回 map,其中的 key 和 value 符合 predicate 函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Filter)] + [[play](https://go.dev/play/p/fSvF3wxuNG7)] +- **FilterByKeys** : 迭代 map, 返回一个新 map,其 key 都是给定的 key 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#FilterByKeys)] + [[play](https://go.dev/play/p/7ov6BJHbVqh)] +- **FilterByValues** : 迭代 map, 返回一个新 map,其 value 都是给定的 value 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#FilterByValues)] + [[play](https://go.dev/play/p/P3-9MdcXegR)] +- **OmitBy** : Filter 的反向操作, 迭代 map 中的每对 key 和 value, 删除符合 predicate 函数的 key, value, 返回新 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#OmitBy)] + [[play](https://go.dev/play/p/YJM4Hj5hNwm)] +- **OmitByKeys** : FilterByKeys 的反向操作, 迭代 map, 返回一个新 map,其 key 不包括给定的 key 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#OmitByKeys)] + [[play](https://go.dev/play/p/jXGrWDBfSRp)] +- **OmitByValues** : FilterByValues 的反向操作, 迭代 map, 返回一个新 map,其 value 不包括给定的 value 值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#OmitByValues)] + [[play](https://go.dev/play/p/XB7Y10uw20_U)] +- **Intersect** : 多个 map 的交集操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Intersect)] + [[play](https://go.dev/play/p/Zld0oj3sjcC)] +- **Keys** : 返回 map 中所有 key 组成的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Keys)] + [[play](https://go.dev/play/p/xNB5bTb97Wd)] +- **KeysBy** : 创建一个切片,其元素是每个 map 的 key 调用 mapper 函数的结果。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil#KeysBy)] + [[play](https://go.dev/play/p/hI371iB8Up8)] +- **Merge** : 合并多个 map, 相同的 key 会被之后的 key 覆盖。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Merge)] + [[play](https://go.dev/play/p/H95LENF1uB-)] +- **Minus** : 返回一个 map,其中的 key 存在于 mapA,不存在于 mapB。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Minus)] + [[play](https://go.dev/play/p/3u5U9K7YZ9m)] +- **Values** : 返回 map 中所有 values 组成的切片 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Values)] + [[play](https://go.dev/play/p/CBKdUc5FTW6)] +- **ValuesBy** : 创建一个切片,其元素是每个 map 的 value 调用 mapper 函数的结果。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ValuesBy)] + [[play](https://go.dev/play/p/sg9-oRidh8f)] +- **MapKeys** : 操作 map 的每个 key,然后转为新的 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapKeys)] + [[play](https://go.dev/play/p/8scDxWeBDKd)] +- **MapValues** : 操作 map 的每个 value,然后转为新的 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapValues)] + [[play](https://go.dev/play/p/g92aY3fc7Iw)] +- **Entries** : 将 map 转换为键/值对切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Entries)] + [[play](https://go.dev/play/p/Ltb11LNcElY)] +- **FromEntries** : 基于键/值对的切片创建 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#FromEntries)] + [[play](https://go.dev/play/p/fTdu4sCNjQO)] +- **Transform** : 将 map 转换为其他类型的 map。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#Transform)] + [[play](https://go.dev/play/p/P6ovfToM3zj)] +- **IsDisjoint** : 验证两个 map 是否具有不同的 key。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#IsDisjoint)] + [[play](https://go.dev/play/p/N9qgYg_Ho6f)] +- **HasKey** : 检查 map 是否包含某个 key。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)] + [[play](https://go.dev/play/p/isZZHOsDhFc)] +- **GetOrSet** : 返回给定键的值,如果不存在则设置该值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)] + [[play](https://go.dev/play/p/IVQwO1OkEJC)] +- **MapToStruct** : 将 map 转成 struct。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)] + [[play](https://go.dev/play/p/7wYyVfX38Dp)] +- **ToSortedSlicesDefault** : 将 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)] +- **ToSortedSlicesWithComparator** : 将 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)] +- **NewOrderedMap** : 创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewOrderedMap)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Set** : 设置给定的键值对。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Set)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Get** : 返回给定键的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Get)] + [[play](https://go.dev/play/p/Y4ZJ_oOc1FU)] +- **OrderedMap_Delete** : 删除给定键的键值对。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Delete)] + [[play](ttps://go.dev/play/p/5bIi4yaZ3K-)] +- **OrderedMap_Clear** : 清空 map 数据。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Clear)] + [[play](https://go.dev/play/p/8LwoJyEfuFr)] +- **OrderedMap_Front** : 返回第一个键值对。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Front)] + [[play](https://go.dev/play/p/ty57XSimpoe)] +- **OrderedMap_Back** : 返回最后一个键值对。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Back)] + [[play](https://go.dev/play/p/rQMjp1yQmpa)] +- **OrderedMap_Range** : 为每个键值对调用给定的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Range)] + [[play](https://go.dev/play/p/U-KpORhc7LZ)] +- **OrderedMap_Keys** : 按顺序返回键的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Keys)] + [[play](https://go.dev/play/p/Vv_y9ExKclA)] +- **OrderedMap_Values** : 按顺序返回值的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Values)] + [[play](https://go.dev/play/p/TWj5n1-PUfx)] +- **OrderedMap_Elements** : 按顺序返回键值对。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Elements)] + [[play](https://go.dev/play/p/4BHG4kKz6bB)] +- **OrderedMap_Len** : 返回键值对的数量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Len)] + [[play](https://go.dev/play/p/cLe6z2VX5N-)] +- **OrderedMap_Contains** : 如果给定的键存在则返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Contains)] + [[play](https://go.dev/play/p/QuwqqnzwDNX)] +- **OrderedMap_Iter** : 返回按顺序产生键值对的通道。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Iter)] + [[play](https://go.dev/play/p/tlq2tdvicPt)] +- **OrderedMap_ReverseIter** : 返回以相反顺序产生键值对的通道。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_ReverseIter)] + [[play](https://go.dev/play/p/8Q0ssg6hZzO)] +- **OrderedMap_SortByKey** : 使用传入的比较函数排序 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)] +- **OrderedMap_MarshalJSON** : 实现 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)] +- **OrderedMap_UnmarshalJSON** : 实现 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)] +- **NewConcurrentMap** : ConcurrentMap 协程安全的 map 结构。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_Set** : 在 map 中设置 key 和 value。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Set)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_Get** : 根据 key 获取 value, 如果不存在 key,返回零值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Get)] + [[play](https://go.dev/play/p/3PenTPETJT0)] +- **ConcurrentMap_GetOrSet** : 返回键的现有值(如果存在),否则,设置 key 并返回给定值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_GetOrSet)] + [[play](https://go.dev/play/p/aDcDApOK01a)] +- **ConcurrentMap_Delete** : 删除 key。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Delete)] + [[play](https://go.dev/play/p/uTIJZYhpVMS)] +- **ConcurrentMap_GetAndDelete** :获取 key,然后删除。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_GetAndDelete)] + [[play](https://go.dev/play/p/ZyxeIXSZUiM)] +- **ConcurrentMap_Has** : 验证是否包含 key。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Has)] + [[play](https://go.dev/play/p/C8L4ul9TVwf)] +- **ConcurrentMap_Range** : 为 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)] +- **SortByKey** : 对传入的 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)] +- **GetOrDefault** : 返回给定键的值,如果键不存在,则返回默认值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)] + [[play](https://go.dev/play/p/99QjSYSBdiM)] +- **FindValuesBy** : 返回一个切片,包含满足给定谓词判断函数的 map 中的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#FindValuesBy)] + [[play](https://go.dev/play/p/bvNwNBZDm6v)] + +

14. mathutil 包实现了一些数学计算的函数。       回到目录

```go -import "github.com/duke-git/lancet/mathutil" +import "github.com/duke-git/lancet/v2/mathutil" ``` -#### Function list: -- [Exponent](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Exponent) -- [Fibonacci](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Fibonacci) -- [Factorial](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Factorial) -- [Percent](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Percent) -- [RoundToFloat](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#RoundToFloat) -- [RoundToString](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#RoundToString) -- [TruncRound](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#TruncRound) +#### 函数列表: -### netutil网络包支持获取ip地址,发送http请求。 +- **Average** :计算平均数,可能需要对结果调用 RoundToFloat 方法四舍五入。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Average)] + [[play](https://go.dev/play/p/Vv7LBwER-pz)] +- **Exponent** : 指数计算(x 的 n 次方)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Exponent)] + [[play](https://go.dev/play/p/uF3HGNPk8wr)] +- **Fibonacci** :计算斐波那契数列的第 n 个数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Fibonacci)] + [[play](https://go.dev/play/p/IscseUNMuUc)] +- **Factorial** : 计算阶乘。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Factorial)] + [[play](https://go.dev/play/p/tt6LdOK67Nx)] +- **Max** : 返回参数中的最大数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Max)] + [[play](https://go.dev/play/p/cN8DHI0rTkH)] +- **MaxBy** : 使用给定的比较器函数返回切片的最大值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#MaxBy)] + [[play](https://go.dev/play/p/pbe2MT-7DV2)] +- **Min** : 返回参数中的最小数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Min)] + [[play](https://go.dev/play/p/21BER_mlGUj)] +- **MinBy** : 使用给定的比较器函数返回切片的最小值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#MinBy)] + [[play](https://go.dev/play/p/N9qgYg_Ho6f)] +- **Percent** : 计算百分比,可以指定保留 n 位小数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Percent)] + [[play](https://go.dev/play/p/s0NdFCtwuyd)] +- **RoundToFloat** : 四舍五入,保留 n 位小数,返回 float64。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#RoundToFloat)] + [[play](https://go.dev/play/p/ghyb528JRJL)] +- **RoundToString** : 四舍五入,保留 n 位小数,返回 string。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#RoundToString)] + [[play](https://go.dev/play/p/kZwpBRAcllO)] +- **TruncRound** : 截短 n 位小数(不进行四舍五入)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)] + [[play](https://go.dev/play/p/aumarSHIGzP)] +- **CeilToFloat** : 向上舍入(进一法),保留 n 位小数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)] + [[play](https://go.dev/play/p/8hOeSADZPCo)] +- **CeilToString** : 向上舍入(进一法),保留 n 位小数,返回字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)] + [[play](https://go.dev/play/p/wy5bYEyUKKG)] +- **FloorToFloat** : 向下舍入(去尾法),保留 n 位小数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)] + [[play](https://go.dev/play/p/vbCBrQHZEED)] +- **FloorToString** : 向下舍入(去尾法),保留 n 位小数,返回字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)] + [[play](https://go.dev/play/p/Qk9KPd2IdDb)] +- **Range** : 根据指定的起始值和数量,创建一个数字切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)] + [[play](https://go.dev/play/p/9ke2opxa8ZP)] +- **RangeWithStep** : 根据指定的起始值,结束值,步长,创建一个数字切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#RangeWithStep)] + [[play](https://go.dev/play/p/akLWz0EqOSM)] +- **AngleToRadian** : 将角度值转为弧度值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#AngleToRadian)] + [[play](https://go.dev/play/p/CIvlICqrHql)] +- **RadianToAngle** : 将弧度值转为角度值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#RadianToAngle)] + [[play](https://go.dev/play/p/dQtmOTUOMgi)] +- **PointDistance** : 计算两个坐标点的距离。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#PointDistance)] + [[play](https://go.dev/play/p/RrG4JIaziM8)] +- **IsPrime** : 判断质数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#IsPrime)] + [[play](https://go.dev/play/p/Rdd8UTHZJ7u)] +- **GCD** : 求最大公约数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#GCD)] + [[play](https://go.dev/play/p/CiEceLSoAKB)] +- **LCM** : 求最小公倍数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#LCM)] + [[play](https://go.dev/play/p/EjcZxfY7G_g)] +- **Cos** : 计算弧度的余弦值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Cos)] + [[play](https://go.dev/play/p/Sm89LoIfvFq)] +- **Sin** : 计算弧度的正弦值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sin)] + [[play](https://go.dev/play/p/TWMQlMywDsP)] +- **Log** : 计算以 base 为底 n 的对数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Log)] + [[play](https://go.dev/play/p/_d4bi8oyhat)] +- **Sum** : 求传入参数之和。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)] + [[play](https://go.dev/play/p/1To2ImAMJA7)] +- **Abs** : 求绝对值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)] + [[play](https://go.dev/play/p/fsyBh1Os-1d)] +- **Div** : 除法运算。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)] + [[play](https://go.dev/play/p/WLxDdGXXYat)] +- **Variance** : 计算方差。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Variance)] + [[play](https://go.dev/play/p/uHuV4YgXf8F)] +- **StdDev** : 计算标准差。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#StdDev)] + [[play](https://go.dev/play/p/FkNZDXvHD2l)] +- **Permutation** : 计算排列数 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)] +- **Combination** : 计算组合数 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)] + +

15. netutil 网络包支持获取 ip 地址,发送 http 请求。       回到目录

```go -import "github.com/duke-git/lancet/netutil" +import "github.com/duke-git/lancet/v2/netutil" ``` #### 函数列表: -- [ConvertMapToQueryString](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ConvertMapToQueryString) -- [GetInternalIp](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetInternalIp) -- [GetIps](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetIps) -- [GetMacAddrs](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#GetMacAddrs) -- [GetPublicIpInfo](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetPublicIpInfo) -- [IsPublicIP](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#IsPublicIP) -- [HttpGet](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpGet) -- [HttpDelete](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpDelete) -- [HttpPost](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPost) -- [HttpPut](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPut) -- [HttpPatch](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPatch) -- [ParseHttpResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ParseHttpResponse) - -### random随机数生成器包,可以生成随机[]bytes, int, string。 + +- **ConvertMapToQueryString** : 将 map 转换成 http 查询字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#ConvertMapToQueryString)] + [[play](https://go.dev/play/p/jnNt_qoSnRi)] +- **EncodeUrl** : 编码 url query string 的值(?a=1&b=[2] -> ?a=1&b=%5B2%5D)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#EncodeUrl)] + [[play](https://go.dev/play/p/bsZ6BRC4uKI)] +- **GetInternalIp** : 获取内部 ipv4。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#GetInternalIp)] + [[play](https://go.dev/play/p/5mbu-gFp7ei)] +- **GetIps** : 获取系统 ipv4 地址列表。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#GetIps)] + [[play](https://go.dev/play/p/NUFfcEmukx1)] +- **GetMacAddrs** : 获取系统 mac 地址列。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#GetMacAddrs)] + [[play](https://go.dev/play/p/Rq9UUBS_Xp1)] +- **GetPublicIpInfo** : 获取[公网 ip 信息](http://ip-api.com/json/). + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#GetPublicIpInfo)] + [[play](https://go.dev/play/p/YDxIfozsRHR)] +- **GetRequestPublicIp** : 获取 http 请求 ip。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#GetRequestPublicIp)] + [[play](https://go.dev/play/p/kxU-YDc_eBo)] +- **IsPublicIP** : 判断 ip 是否是公共 ip。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsPublicIP)] + [[play](https://go.dev/play/p/nmktSQpJZnn)] +- **IsInternalIP** : 判断 ip 是否是局域网 ip。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsInternalIP)] + [[play](https://go.dev/play/p/sYGhXbgO4Cb)] +- **HttpRequest** : 用于抽象 HTTP 请求实体的结构。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpRequest)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **HttpClient** : 用于发送 HTTP 请求。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpClient)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **SendRequest** : 发送 http 请求。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#SendRequest)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **DecodeResponse** : 解析 http 响应体到目标结构体。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#DecodeResponse)] + [[play](https://go.dev/play/p/jUSgynekH7G)] +- **StructToUrlValues** : 将结构体转为 url values, 仅转化结构体导出字段并且包含`json` tag。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#StructToUrlValues)] + [[play](https://go.dev/play/p/pFqMkM40w9z)] +- **HttpGetdeprecated** : 发送 http get 请求(已弃用:SendRequest 代替)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpGet)] +- **HttpDeletedeprecated** : 发送 http delete 请求(已弃用:SendRequest 代替)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpDelete)] +- **HttpPostdeprecated** : 发送 http post 请求(已弃用:SendRequest 代替)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpPost)] +- **HttpPutdeprecated** : 发送 http put 请求(已弃用:SendRequest 代替)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpPut)] +- **HttpPatchdeprecated** : 发送 http patch 请求(已弃用:SendRequest 代替)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#HttpPatch)] +- **ParseHttpResponse** : 解析 http 响应体到目标结构体。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#ParseHttpResponse)] +- **DownloadFile** : 从指定的 server 地址下载文件。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#DownloadFile)] +- **UploadFile** : 将文件上传指定的 server 地址。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#UploadFile)] +- **IsPingConnected** : 检查能否 ping 通主机。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsPingConnected)] + [[play](https://go.dev/play/p/q8OzTijsA87)] +- **IsTelnetConnected** : 检查能否 telnet 到主机。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#IsTelnetConnected)] + [[play](https://go.dev/play/p/yiLCGtQv_ZG)] +- **BuildUrl** : 创建 url 字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#BuildUrl)] + [[play](https://go.dev/play/p/JLXl1hZK7l4)] +- **AddQueryParams** : 向 url 添加查询参数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/netutil.md#AddQueryParams)] + [[play](https://go.dev/play/p/JLXl1hZK7l4)] + +

16. pointer 包支持一些指针类型的操作。       回到目录

```go -import "github.com/duke-git/lancet/random" +import "github.com/duke-git/lancet/v2/pointer" ``` #### 函数列表: -- [RandBytes](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandBytes) -- [RandInt](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandInt) -- [RandString](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandString) -- [UUIdV4](https://github.com/duke-git/lancet/blob/main/docs/random.md#UUIdV4) -### retry重试执行函数直到函数运行成功或被context cancel。 + +- **ExtractPointer** : 返回传入 interface 的底层值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#ExtractPointer)] + [[play](https://go.dev/play/p/D7HFjeWU2ZP)] +- **Of** : 返回传入参数的指针值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#Of)] + [[play](https://go.dev/play/p/HFd70x4DrMj)] +- **Unwrap** : 返回传入指针指向的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#Unwrap)] + [[play](https://go.dev/play/p/cgeu3g7cjWb) +- **UnwarpOr** : 返回指针的值,如果指针为零值,则返回 fallback。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#UnwrapOr)] + [[play](https://go.dev/play/p/mmNaLC38W8C)] +- **UnwarpOrDefault** : 返回指针的值,如果指针为零值,则返回相应零值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/pointer.md#UnwrapOrDefault)] + [[play](https://go.dev/play/p/ZnGIHf8_o4E)] + +

17. random 随机数生成器包,可以生成随机[]bytes, int, string。       回到目录

```go -import "github.com/duke-git/lancet/retry" +import "github.com/duke-git/lancet/v2/random" ``` #### 函数列表: -- [Context](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#Context) -- [Retry](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#Retry) -- [RetryFunc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryFunc) -- [RetryDuration](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryDuration) -- [RetryTimes](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryTimes) +- **RandBytes** : 生成随机字节切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBytes)] + [[play](https://go.dev/play/p/EkiLESeXf8d)] +- **RandInt** : 生成随机 int, 范围[min, max)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandInt)] + [[play](https://go.dev/play/p/pXyyAAI5YxD)] +- **RandString** : 生成给定长度的随机字符串,只包含字母(a-zA-Z)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandString)] + [[play](https://go.dev/play/p/W2xvRUXA7Mi)] +- **RandUpper** : 生成给定长度的随机大写字母字符串(A-Z)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUpper)] + [[play](https://go.dev/play/p/29QfOh0DVuh)] +- **RandLower** : 生成给定长度的随机小写字母字符串(a-z)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandLower)] + [[play](https://go.dev/play/p/XJtZ471cmtI)] +- **RandNumeral** : 生成给定长度的随机数字字符串(0-9)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumeral)] + [[play](https://go.dev/play/p/g4JWVpHsJcf)] +- **RandNumeralOrLetter** : 生成给定长度的随机字符串(数字+字母)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumeralOrLetter)] + [[play](https://go.dev/play/p/19CEQvpx2jD)] +- **UUIdV4** : 生成 UUID v4 字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)] + [[play](https://go.dev/play/p/_Z9SFmr28ft)] +- **RandUniqueIntSlice** : 生成一个不重复的随机 int 切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)] + [[play](https://go.dev/play/p/uBkRSOz73Ec)] +- **RandSymbolChar** : 生成给定长度的随机符号字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSymbolChar)] + [[play](https://go.dev/play/p/Im6ZJxAykOm)] +- **RandFloat** : 生成随机 float64 数字,可以指定范围和精度。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloat)] + [[play](https://go.dev/play/p/zbD_tuobJtr)] +- **RandFloats** : 生成随机 float64 数字切片,可以指定长度,范围和精度. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)] + [[play](https://go.dev/play/p/uBkRSOz73Ec)] +- **RandStringSlice** : 生成随机字符串 slice。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)] + [[play](https://go.dev/play/p/2_-PiDv3tGn)] +- **RandBool** : 生成随机 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)] +- **RandBoolSlice** : 生成特定长度的随机 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)] +- **RandIntSlice** : 生成一个特定长度的随机 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)] +- **RandFromGivenSlice** : 从给定切片中随机生成元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFromGivenSlice)] + [[play](https://go.dev/play/p/UrkWueF6yYo)] +- **RandSliceFromGivenSlice** : 从给定切片中生成长度为 num 的随机切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSliceFromGivenSlice)] + [[play](https://go.dev/play/p/68UikN9d6VT)] +- **RandNumberOfLength** : 生成指定长度的随机数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandNumberOfLength)] + [[play](https://go.dev/play/p/oyZbuV7bu7b)] + +

18. retry 重试执行函数直到函数运行成功或被 context cancel。       回到目录

-### slice包包含操作切片的方法集合。 +```go +import "github.com/duke-git/lancet/v2/retry" +``` + +#### 函数列表: + +- **Context** : 设置重试 context 参数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#Context)] + [[play](https://go.dev/play/p/xnAOOXv9GkS)] +- **Retry** : 重试执行函数 retryFunc,直到函数运行成功,或被 context 取消。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#Retry)] + [[play](https://go.dev/play/p/nk2XRmagfVF)] +- **RetryFunc** : 重试执行的函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryFunc)] + [[play](https://go.dev/play/p/nk2XRmagfVF)] +- **RetryTimes** : 设置重试次数,默认 5。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)] + [[play](https://go.dev/play/p/ssfVeU2SwLO)] +- **BackoffStrategy** : 定义计算退避间隔的方法的接口。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)] +- **RetryWithCustomBackoff** : 设置自定义退避策略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)] + [[play](https://go.dev/play/p/jIm_o2vb5Y4)] +- **RetryWithLinearBackoff** : 设置线性策略退避。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)] + [[play](https://go.dev/play/p/PDet2ZQZwcB)] +- **RetryWithExponentialWithJitterBackoff** : 设置指数策略退避。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)] + [[play](https://go.dev/play/p/xp1avQmn16X)] + +

19. slice 包含操作切片的方法集合。        回到目录

```go -import "github.com/duke-git/lancet/slice" +import "github.com/duke-git/lancet/v2/slice" ``` #### 函数列表: -- [Contain](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Contain) -- [ContainSubSlice](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainSubSlice) -- [Chunk](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Chunk) -- [Compact](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Compact) -- [Concat](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Concat) -- [Count](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Count) -- [Difference](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Difference) -- [DifferenceBy](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DifferenceBy) -- [DeleteByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DeleteByIndex) -- [Drop](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Drop) -- [Every](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Every) -- [Filter](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Filter) -- [Find](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Find) -- [FindLast](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FindLast) -- [FlattenDeep](#FlattenDeep) -- [ForEach](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ForEach) -- [GroupBy](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#GroupBy) -- [IntSlice](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IntSlice) -- [InterfaceSlice](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#InterfaceSlice) -- [Intersection](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Intersection) -- [InsertByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#InsertByIndex) -- [Map](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Map) -- [ReverseSlice](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ReverseSlice) -- [Reduce](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Reduce) -- [Shuffle](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Shuffle) -- [SortByField](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#SortByField) -- [Some](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Some) -- [StringSlice](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#StringSlice) -- [Unique](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Unique) -- [Union](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Union) -- [UpdateByIndex](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#UpdateByIndex) -- [Without](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Without) - - -### strutil包含处理字符串的相关函数。 + +- **AppendIfAbsent** : 当前切片中不包含值时,将该值追加到切片中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#AppendIfAbsent)] + [[play](https://go.dev/play/p/GNdv7Jg2Taj)] +- **Contain** : 判断 slice 是否包含 value。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Contain)] + [[play](https://go.dev/play/p/_454yEHcNjf)] +- **ContainBy** : 根据 predicate 函数判断切片是否包含某个值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ContainBy)] + [[play](https://go.dev/play/p/49tkHfX4GNc)] +- **ContainSubSlice** : 判断 slice 是否包含 subslice。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ContainSubSlice)] + [[play](https://go.dev/play/p/bcuQ3UT6Sev)] +- **Chunk** : 按照 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)] +- **Compact** : 去除 slice 中的假值(false values are false, nil, 0, "")。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Compact)] + [[play](https://go.dev/play/p/pO5AnxEr3TK)] +- **Concat** : 合并多个 slices 到一个 slice 中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Concat)] + [[play](https://go.dev/play/p/gPt-q7zr5mk)] +- **Count** : 返回切片中指定元素的个数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Count)] + [[play](https://go.dev/play/p/Mj4oiEnQvRJ)] +- **CountBy** : 遍历切片,对每个元素执行函数 predicate. 返回符合函数返回值为 true 的元素的个数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#CountBy)] + [[play](https://go.dev/play/p/tHOccTMDZCC)] +- **Difference** : 创建一个切片,其元素不包含在另一个给定切片中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Difference)] + [[play](https://go.dev/play/p/VXvadzLzhDa)] +- **DifferenceBy** : 将两个 slice 中的每个元素调用 iteratee 函数,并比较它们的返回值,如果不相等返回在 slice 中对应的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceBy)] + [[play](https://go.dev/play/p/DiivgwM5OnC)] +- **DifferenceWith** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceWith)] + [[play](https://go.dev/play/p/v2U2deugKuV)] +- **DeleteAt** : 删除切片中指定索引到的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)] + [[play](https://go.dev/play/p/800B1dPBYyd)] +- **DeleteRange** : 删除切片中指定开始索引到结束索引的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteRange)] + [[play](https://go.dev/play/p/945HwiNrnle)] +- **Drop** : 从切片头部删除 n 个元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)] + [[play](https://go.dev/play/p/jnPO2yQsT8H)] +- **DropRight** : 从切片尾部删除 n 个元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DropRight)] + [[play](https://go.dev/play/p/8bcXvywZezG)] +- **DropWhile** : 从切片的头部删除 n 个元素,这个 n 个元素满足 predicate 函数返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DropWhile)] + [[play](https://go.dev/play/p/4rt252UV_qs)] +- **DropRightWhile** : 从切片的尾部删除 n 个元素,这个 n 个元素满足 predicate 函数返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DropRightWhile)] + [[play](https://go.dev/play/p/6wyK3zMY56e)] +- **Equal** : 检查两个切片是否相等,相等条件:切片长度相同,元素顺序和值都相同。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Equal)] + [[play](https://go.dev/play/p/WcRQJ37ifPa)] +- **EqualWith** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数 comparator,返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualWith)] + [[play](https://go.dev/play/p/b9iygtgsHI1)] +- **EqualUnordered** : 检查两个切片是否相等,元素数量相同,值相等,不考虑元素顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#EqualUnordered)] + [[play](https://go.dev/play/p/n8fSc2w8ZgX)] +- **Every** : 如果切片中的所有值都通过谓词函数,则返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Every)] + [[play](https://go.dev/play/p/R8U6Sl-j8cD)] +- **Filter** : 返回切片中通过 predicate 函数真值测试的所有元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Filter)] + [[play](https://go.dev/play/p/SdPna-7qK4T)] +- **FilterMap** : 返回一个将 filter 和 map 操作应用于给定切片的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FilterMap)] + [[play](https://go.dev/play/p/J94SZ_9MiIe)] +- **Finddeprecated** : 遍历切片的元素,返回第一个通过 predicate 函数真值测试的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Find)] + [[play](https://go.dev/play/p/CBKeBoHVLgq)] +- **FindBy** : 遍历切片的元素,返回第一个通过 predicate 函数真值测试的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FindBy)] + [[play](https://go.dev/play/p/n1lysBYl-GB)] +- **FindLastdeprecated** : 从头到尾遍历 slice 的元素,返回最后一个通过 predicate 函数真值测试的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FindLast)] + [[play](https://go.dev/play/p/FFDPV_j7URd)] +- **FindLastBy** : 从遍历 slice 的元素,返回最后一个通过 predicate 函数真值测试的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FindLastBy)] + [[play](https://go.dev/play/p/8iqomzyCl_s)] +- **Flatten** : 将多维切片展平一层。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Flatten)] + [[play](https://go.dev/play/p/hYa3cBEevtm)] +- **FlattenDeep** : 将多维切片递归展平到一层。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FlattenDeep)] + [[play](https://go.dev/play/p/yjYNHPyCFaF)] +- **FlatMap** : 将切片转换为其它类型切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#FlatMap)] + [[play](https://go.dev/play/p/_QARWlWs1N_F)] +- **ForEach** : 遍历切片的元素并为每个元素调用 iteratee 函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEach)] + [[play](https://go.dev/play/p/DrPaa4YsHRF)] +- **ForEachConcurrent** : 对 slice 并发执行 foreach 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachConcurrent)] + [[play](https://go.dev/play/p/kT4XW7DKVoV)] +- **ForEachWithBreak** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachWithBreak)] + [[play](https://go.dev/play/p/qScs39f3D9W)] +- **GroupBy** : 迭代切片的元素,每个元素将按条件分组,返回两个切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#GroupBy)] + [[play](https://go.dev/play/p/QVkPxzPR0iA)] +- **GroupWith** : 创建一个 map,key 是 iteratee 遍历 slice 中的每个元素返回的结果。值是切片元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#GroupWith)] + [[play](https://go.dev/play/p/ApCvMNTLO8a)] +- **IntSlicedeprecated** : 将接口切片转换为 int 切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IntSlice)] + [[play](https://go.dev/play/p/FdQXF0Vvqs-)] +- **InterfaceSlicedeprecated** : 将值转换为 interface 切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#InterfaceSlice)] + [[play](https://go.dev/play/p/FdQXF0Vvqs-)] +- **Intersection** : 返回多个切片的交集。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Intersection)] + [[play](https://go.dev/play/p/anJXfB5wq_t)] +- **InsertAt** : 将元素插入到索引处的切片中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#InsertAt)] + [[play](https://go.dev/play/p/hMLNxPEGJVE)] +- **IndexOf** : 返回在切片中找到值的第一个匹配项的索引,如果找不到值,则返回-1。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IndexOf)] + [[play](https://go.dev/play/p/MRN1f0FpABb)] +- **LastIndexOf** : 返回在切片中找到最后一个值的索引,如果找不到该值,则返回-1。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LastIndexOf)] + [[play](https://go.dev/play/p/DokM4cf1IKH)] +- **Map** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Map)] + [[play](https://go.dev/play/p/biaTefqPquw)] +- **MapConcurrent** : 对 slice 并发执行 map 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#MapConcurrent)] + [[play](https://go.dev/play/p/H1ehfPkPen0)] +- **Merge** : 合并多个切片(不会消除重复元素)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Merge)] + [[play](https://go.dev/play/p/lbjFp784r9N)] +- **Reverse** : 反转切片中的元素顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reverse)] + [[play](https://go.dev/play/p/8uI8f1lwNrQ)] +- **ReverseCopy** : 反转切片中的元素顺序, 不改变原 slice。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ReverseCopy)] + [[play](https://go.dev/play/p/c9arEaP7Cg-)] +- **Reducedeprecated** : 将切片中的元素依次运行 iteratee 函数,返回运行结果。(废弃:建议使用 ReduceBy) + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Reduce)] + [[play](https://go.dev/play/p/_RfXJJWIsIm)] +- **ReduceBy** : 对切片元素执行 reduce 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceBy)] + [[play](https://go.dev/play/p/YKDpLi7gtee)] +- **ReduceRight** : 类似 ReduceBy 操作,迭代切片元素顺序从右至左。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceRight)] + [[play](https://go.dev/play/p/qT9dZC03A1K)] +- **ReduceConcurrent** : 对切片元素执行并发 reduce 操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceConcurrent)] + [[play](https://go.dev/play/p/Tjwe6OtaG07)] +- **Replace** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Replace)] + [[play](https://go.dev/play/p/P5mZp7IhOFo)] +- **ReplaceAll** : 返回切片的副本,将其中 old 全部替换为 new。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReplaceAll)] + [[play](https://go.dev/play/p/CzqXMsuYUrx)] +- **Repeat** : 创建一个切片,包含 n 个传入的 item。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Repeat)] + [[play](https://go.dev/play/p/1CbOmtgILUU)] +- **Shuffle** : 随机打乱切片中的元素顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Shuffle)] + [[play](https://go.dev/play/p/YHvhnWGU3Ge)] +- **ShuffleCopy** : 随机打乱切片中的元素顺序, 不改变原切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ShuffleCopy)] + [[play](https://go.dev/play/p/vqDa-Gs1vT0)] +- **IsAscending** : 检查切片元素是否按升序排列。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsAscending)] + [[play](https://go.dev/play/p/9CtsFjet4SH)] +- **IsDescending** : 检查切片元素是否按降序排列。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsDescending)] + [[play](https://go.dev/play/p/U_LljFXma14)] +- **IsSorted** : 检查切片元素是否是有序的(升序或降序)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsSorted)] + [[play](https://go.dev/play/p/nCE8wPLwSA-)] +- **IsSortedByKey** : 通过 iteratee 函数,检查切片元素是否是有序的。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#IsSortedByKey)] + [[play](https://go.dev/play/p/tUoGB7DOHI4)] +- **Sort** : 对任何有序类型(数字或字符串)的切片进行排序,使用快速排序算法。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Sort)] + [[play](https://go.dev/play/p/V9AVjzf_4Fk)] +- **SortBy** : 按照 less 函数确定的升序规则对切片进行排序。排序不保证稳定性。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SortBy)] + [[play](https://go.dev/play/p/DAhLQSZEumm)] +- **SortByFielddeprecated** : 按字段对结构切片进行排序。slice 元素应为 struct,字段类型应为 int、uint、string 或 bool。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SortByField)] + [[play](https://go.dev/play/p/fU1prOBP9p1)] +- **Some** : 如果列表中的任何值通过谓词函数,则返回 true。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Some)] + [[play](https://go.dev/play/p/4pO9Xf9NDGS)] +- **StringSlicedeprecated** : 将接口切片转换为字符串切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#StringSlice)] + [[play](https://go.dev/play/p/W0TZDWCPFcI)] +- **SymmetricDifference** : 返回一个切片,其中的元素存在于参数切片中,但不同时存储在于参数切片中(交集取反)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#h42nJX5xMln)] + [[play](https://go.dev/play/p/1CbOmtgILUU)] +- **ToSlice** : 将可变参数转为切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ToSlice)] + [[play](https://go.dev/play/p/YzbzVq5kscN)] +- **ToSlicePointer** : 将可变参数转为指针切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ToSlicePointer)] + [[play](https://go.dev/play/p/gx4tr6_VXSF)] +- **Unique** : 删除切片中的重复元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)] + [[play](https://go.dev/play/p/AXw0R3ZTE6a)] +- **UniqueBy** : 根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)] + [[play](https://go.dev/play/p/GY7JE4yikrl)] +- **UniqueByComparator** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)] + [[play](https://go.dev/play/p/rwSacr-ZHsR)] +- **UniqueByField** : 根据 struct 字段对 struct 切片去重复。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)] + [[play](https://go.dev/play/p/6cifcZSPIGu)] +- **UniqueByConcurrent** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByConcurrent)] + [[play](https://go.dev/play/p/wXZ7LcYRMGL)] +- **Union** : 合并多个切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)] + [[play](https://go.dev/play/p/hfXV1iRIZOf)] +- **UnionBy** : 对切片的每个元素调用函数后,合并多个切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UnionBy)] + [[play](https://go.dev/play/p/HGKHfxKQsFi)] +- **UpdateAt** : 更新索引处的切片元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UpdateAt)] + [[play](https://go.dev/play/p/f3mh2KloWVm)] +- **Without** : 创建一个不包括所有给定值的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Without)] + [[play](https://go.dev/play/p/bwhEXEypThg)] + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#KeyBy)] + [[play](https://go.dev/play/p/uXod2LWD1Kg)] +- **Join** : 用指定的分隔符链接切片元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Join)] + [[play](https://go.dev/play/p/huKzqwNDD7V)] +- **Partition** : 根据给定的 predicate 判断函数分组切片元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Partition)] + [[play](https://go.dev/play/p/lkQ3Ri2NQhV)] +- **Random** : 随机返回切片中元素以及下标, 当切片长度为 0 时返回下标-1。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)] + [[play](https://go.dev/play/p/UzpGQptWppw)] +- **SetToDefaultIf** : 根据给定给定的 predicate 判定函数来修改切片中的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)] + [[play](https://go.dev/play/p/9AXGlPRC0-A)] +- **Break** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Break)] +- **RightPadding** : 在切片的右部添加元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#RightPadding)] + [[play](https://go.dev/play/p/0_2rlLEMBXL)] +- **LeftPadding** : 在切片的左部添加元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)] + [[play](https://go.dev/play/p/jlQVoelLl2k)] +- **Frequency** : 计算切片中每个元素出现的频率。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Frequency)] + [[play](https://go.dev/play/p/CW3UVNdUZOq)] +- **JoinFunc** : 将切片元素用给定的分隔符连接成一个单一的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#JoinFunc)] + [[play](https://go.dev/play/p/55ib3SB5fM2)] +- **ConcatBy** : 将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ConcatBy)] + [[play](https://go.dev/play/p/6QcUpcY4UMW)] + +

20. stream 流,该包仅验证简单的 stream 实现,功能有限。       回到目录

```go -import "github.com/duke-git/lancet/strutil" +import "github.com/duke-git/lancet/v2/stream" ``` #### 函数列表: -- [After](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#After) -- [AfterLast](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#AfterLast) -- [Before](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Before) -- [BeforeLast](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#BeforeLast) -- [CamelCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#CamelCase) -- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Capitalize) -- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#IsString) -- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#KebabCase) -- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#LowerFirst) -- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperFirst) -- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd) -- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadStart) -- [ReverseStr](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#ReverseStr) -- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SnakeCase) -- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Wrap) -- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Unwrap) - - -### system包含os, runtime, shell command相关函数。 +- **Of** : 创建元素为指定值的 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Of)] + [[play](https://go.dev/play/p/jI6_iZZuVFE)] +- **FromSlice** : 从切片创建 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FromSlice)] + [[play](https://go.dev/play/p/wywTO0XZtI4)] +- **FromChannel** : 从通道创建 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FromChannel)] + [[play](https://go.dev/play/p/9TZYugGMhXZ)] +- **FromRange** : 指定一个数字范围创建 stream, 范围两端点值都包括在内。. [start, end] + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FromRange)] + [[play](https://go.dev/play/p/9Ex1-zcg-B-)] +- **Generate** : 创建一个 stream,其中每个元素都由提供的生成器函数生成。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Generate)] + [[play](https://go.dev/play/p/rkOWL1yA3j9)] +- **Concat** : 创建一个延迟连接 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)] +- **Distinct** : 创建并返回一个 stream,用于删除重复的项。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Distinct)] + [[play](https://go.dev/play/p/eGkOSrm64cB)] +- **Filter** : 返回一个通过判定函数的 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Filter)] + [[play](https://go.dev/play/p/MFlSANo-buc)] +- **FilterConcurrent** : 对 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)] +- **Map** : 返回一个 stream,该 stream 由将给定函数应用于源 stream 元素的元素组成。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Map)] + [[play](https://go.dev/play/p/OtNQUImdYko)] +- **Peek** : 返回一个由源 stream 的元素组成的 stream,并在从生成的 stream 中消耗元素时对每个元素执行所提供的操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Peek)] + [[play](https://go.dev/play/p/u1VNzHs6cb2)] +- **Skip** : 在丢弃 stream 的前 n 个元素后,返回由源 stream 的其余元素组成的 stream。如果此 stream 包含的元素少于 n 个,则将返回一个空 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Skip)] + [[play](https://go.dev/play/p/fNdHbqjahum)] +- **Limit** : 返回由源 stream 的元素组成的 stream,该 stream 被截断为长度不超过 maxSize。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Limit)] + [[play](https://go.dev/play/p/qsO4aniDcGf)] +- **Reverse** : 返回元素与源 stream 的顺序相反的 stream。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Reverse)] + [[play](https://go.dev/play/p/A8_zkJnLHm4)] +- **Range** : 返回一个 stream,该 stream 的元素在从源 stream 的开始(包含)到结束(排除)的范围内。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Range)] + [[play](https://go.dev/play/p/indZY5V2f4j)] +- **Sorted** : 返回一个 stream,该 stream 由源 stream 的元素组成,并根据提供的 less 函数进行排序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Sorted)] + [[play](https://go.dev/play/p/XXtng5uonFj)] +- **ForEach** : 对 stream 的每个元素执行一个操作。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ForEach)] + [[play](https://go.dev/play/p/Dsm0fPqcidk)] +- **Reduce** : 使用关联累加函数对 stream 的元素执行 reduce 操作,并 reduce 操作结果(如果有)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Reduce)] + [[play](https://go.dev/play/p/6uzZjq_DJLU)] +- **FindFirst** : 返回此 stream 的第一个元素,如果 stream 为空,则返回零值和 false。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FindFirst)] + [[play](https://go.dev/play/p/9xEf0-6C1e3)] +- **FindLast** : 返回此 stream 的最后一个元素,如果 stream 为空,则返回零值和 false。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FindLast)] + [[play](https://go.dev/play/p/WZD2rDAW-2h)] +- **Max** : 根据提供的 less 函数返回 stream 的最大元素。less 函数: a > b + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Max)] + [[play](https://go.dev/play/p/fm-1KOPtGzn)] +- **Min** : 根据提供的 less 函数返回 stream 的最小元素。less 函数: a < b + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Min)] + [[play](https://go.dev/play/p/vZfIDgGNRe_0)] +- **AllMatch** : 判断 stream 的所有元素是否全部匹配指定判定函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#AllMatch)] + [[play](https://go.dev/play/p/V5TBpVRs-Cx)] +- **AnyMatch** : 判断 stream 是否包含匹配指定判定函数的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#AnyMatch)] + [[play](https://go.dev/play/p/PTCnWn4OxSn)] +- **NoneMatch** : 判断 stream 的元素是否全部不匹配指定的判定函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#NoneMatch)] + [[play](https://go.dev/play/p/iWS64pL1oo3)] +- **Count** : 返回 stream 中元素的数量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Count)] + [[play](https://go.dev/play/p/r3koY6y_Xo-)] +- **ToSlice** : 返回 stream 中的元素切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)] + [[play](https://go.dev/play/p/jI6_iZZuVFE)] +- **IndexOf** : 返回在 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)] +- **LastIndexOf** : 返回在 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)] + +

21. structs 提供操作 struct, tag, field 的相关函数。       回到目录

```go -import "github.com/duke-git/lancet/system" +import "github.com/duke-git/lancet/v2/structs" ``` #### 函数列表: -- [IsWindows](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#IsWindows) -- [IsLinux](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#IsLinux) -- [IsMac](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#IsMac) -- [GetOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#GetOsEnv) -- [SetOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#SetOsEnv) -- [RemoveOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#RemoveOsEnv) -- [CompareOsEnv](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#CompareOsEnv) -- [ExecCommand](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN.md#ExecCommand) -### validator验证器包,包含常用字符串格式验证函数。 +- **New** : `Struct`结构体的构造函数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#New)] +- **ToMap** : 将一个合法的 struct 对象转换为 map[string]any。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#ToMap)] +- **Fields** : 获取一个 struct 对象的属性列表。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)] +- **Field** : 根据属性名获取一个 struct 对象的属性。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)] +- **IsStruct** : 判断是否为一个合法的 struct 对象。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsStruct)] +- **Tag** : 获取`Field`的`Tag`,默认的 tag key 是 json。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Tag)] +- **Name** : 获取属性名。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Name)] +- **Value** : 获取`Field`属性的值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Value)] +- **Kind** : 获取属性 Kind。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Kind)] +- **IsEmbedded** : 判断属性是否为嵌入。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsEmbedded)] +- **IsExported** : 判断属性是否导出。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsExported)] +- **IsZero** : 判断属性是否为零值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsZero)] +- **IsSlice** : 判断属性是否是切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsSlice)] +- **IsTargetType** : 判断属性是否是目标类型。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsTargetType)] + +

22. strutil 包含字符串处理的相关函数。       回到目录

```go -import "github.com/duke-git/lancet/validator" +import "github.com/duke-git/lancet/v2/strutil" ``` + #### 函数列表: -- [ContainChinese](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#ContainChinese) -- [ContainLetter](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#ContainLetter) -- [ContainLower](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#ContainLower) -- [ContainUpper](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#ContainUpper) -- [IsAlpha](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsAlpha) -- [IsAllUpper](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsAllUpper) -- [IsAllLower](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsAllLower) -- [IsBase64](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsBase64) -- [IsChineseMobile](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsChineseMobile) -- [IsChineseIdNum](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsChineseIdNum) -- [IsChinesePhone](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsChinesePhone) -- [IsCreditCard](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsCreditCard) -- [IsDns](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsDns) -- [IsEmail](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsEmail) -- [IsEmptyString](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsEmptyString) -- [IsFloatStr](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsFloatStr) -- [IsNumberStr](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsNumberStr) -- [IsJSON](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsJSON) -- [IsRegexMatch](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsRegexMatch) -- [IsIntStr](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIntStr) -- [IsIp](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIp) -- [IsIpV4](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIpV4) -- [IsIpV6](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIpV6) -- [IsStrongPassword](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsStrongPassword) -- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsUrl) -- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsWeakPassword) +- **After** : 返回源字符串中指定字符串首次出现时的位置之后的子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#After)] + [[play](https://go.dev/play/p/RbCOQqCDA7m)] +- **AfterLast** : 返回源字符串中指定字符串最后一次出现时的位置之后的子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#AfterLast)] + [[play](https://go.dev/play/p/1TegARrb8Yn)] +- **Before** : 返回源字符串中指定字符串第一次出现时的位置之前的子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Before)] + [[play](https://go.dev/play/p/JAWTZDS4F5w)] +- **BeforeLast** : 返回源字符串中指定字符串最后一次出现时的位置之前的子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#BeforeLast)] + [[play](https://go.dev/play/p/pJfXXAoG_Te)] +- **CamelCase** : 将字符串转换为 CamelCase 驼峰式字符串, 非字母和数字会被忽略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#CamelCase)] + [[play](https://go.dev/play/p/9eXP3tn2tUy)] +- **Capitalize** : 将字符串的第一个字符转换为大写。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Capitalize)] + [[play](https://go.dev/play/p/2OAjgbmAqHZ)] +- **ContainsAll** : 判断字符串是否包括全部给定的子字符串切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ContainsAll)] + [[play](https://go.dev/play/p/KECtK2Os4zq)] +- **ContainsAny** : 判断字符串是否包括给定的子字符串切片中任意一个子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ContainsAny)] + [[play](https://go.dev/play/p/dZGSSMB3LXE)] +- **IsString** : 判断传入参数的数据类型是否为字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#IsString)] + [[play](https://go.dev/play/p/IOgq7oF9ERm)] +- **KebabCase** : 将字符串转换为 kebab-case 形式字符串, 非字母和数字会被忽略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#KebabCase)] + [[play](https://go.dev/play/p/dcZM9Oahw-Y)] +- **UpperKebabCase** : 将字符串转换为大写 KEBAB-CASE 形式字符串, 非字母和数字会被忽略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#UpperKebabCase)] + [[play](https://go.dev/play/p/zDyKNneyQXk)] +- **LowerFirst** : 将字符串的第一个字符转换为小写形式。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#LowerFirst)] + [[play](https://go.dev/play/p/CbzAyZmtJwL)] +- **UpperFirst** : 将字符串的第一个字符转换为大写形式。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#UpperFirst)] + [[play](https://go.dev/play/p/sBbBxRbs8MM)] +- **Pad** : 如果字符串长度短于 size,则在左右两侧填充字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Pad)] + [[play](https://go.dev/play/p/NzImQq-VF8q)] +- **PadEnd** : 如果字符串短于限制大小,则在右侧用给定字符填充字符串。 如果填充字符超出大小,它们将被截断。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#PadEnd)] + [[play](https://go.dev/play/p/9xP8rN0vz--)] +- **PadStart** : 如果字符串短于限制大小,则在左侧用给定字符填充字符串。 如果填充字符超出大小,它们将被截断。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#PadStart)] + [[play](https://go.dev/play/p/xpTfzArDfvT)] +- **Reverse** : 返回字符顺序与给定字符串相反的字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Reverse)] + [[play](https://go.dev/play/p/adfwalJiecD)] +- **SnakeCase** : 将字符串转换为 snake_case 形式, 非字母和数字会被忽略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SnakeCase)] + [[play](https://go.dev/play/p/tgzQG11qBuN)] +- **UpperSnakeCase** : 将字符串转换为大写 SNAKE_CASE 形式, 非字母和数字会被忽略。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#UpperSnakeCase)] + [[play](https://go.dev/play/p/4COPHpnLx38)] +- **SplitEx** : 拆分给定的字符串可以控制结果切片是否包含空字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SplitEx)] + [[play](https://go.dev/play/p/Us-ySSbWh-3)] +- **Substring** : 根据指定的位置和长度截取子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Substring)] + [[play](https://go.dev/play/p/q3sM6ehnPDp)] +- **Wrap** : 用给定字符包裹传入的字符串 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Wrap)] + [[play](https://go.dev/play/p/KoZOlZDDt9y)] +- **Unwrap** : 从另一个字符串中解开一个给定的字符串。 将更改源字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Unwrap)] + [[play](https://go.dev/play/p/Ec2q4BzCpG-)] +- **SplitWords** : 将字符串拆分为单词,只支持字母字符单词。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SplitWords)] + [[play](https://go.dev/play/p/KLiX4WiysMM)] +- **WordCount** : 返回有意义单词的数量,只支持字母字符单词。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#WordCount)] + [[play](https://go.dev/play/p/bj7_odx3vRf)] +- **RemoveNonPrintable** : 删除字符串中不可打印的字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveNonPrintable)] + [[play](https://go.dev/play/p/og47F5x_jTZ)] +- **StringToBytes** : 在不分配内存的情况下将字符串转换为字节片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#StringToBytes)] + [[play](https://go.dev/play/p/7OyFBrf9AxA)] +- **BytesToString** : 在不分配内存的情况下将字节切片转换为字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#BytesToString)] + [[play](https://go.dev/play/p/6c68HRvJecH)] +- **IsBlank** : 检查字符串是否为空格或空。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#IsBlank)] + [[play](https://go.dev/play/p/6zXRH_c0Qd3)] +- **IsNotBlank** : 检查字符串是否不为空。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#IsNotBlank)] + [[play](https://go.dev/play/p/e_oJW0RAquA)] +- **HasPrefixAny** : 检查字符串是否以指定字符串数组中的任何一个开头。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HasPrefixAny)] + [[play](https://go.dev/play/p/8UUTl2C5slo)] +- **HasSuffixAny** : 检查字符串是否以指定字符串数组中的任何一个结尾。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HasSuffixAny)] + [[play](https://go.dev/play/p/sKWpCQdOVkx)] +- **IndexOffset** : 将字符串偏移 idxFrom 后,返回字符串中第一个 substr 实例的索引。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#IndexOffset)] + [[play](https://go.dev/play/p/qZo4lV2fomB)] +- **ReplaceWithMap** : 返回 string 的副本,以无序的方式被 map 替换,区分大小写。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ReplaceWithMap)] + [[play](https://go.dev/play/p/h3t7CNj2Vvu)] +- **Trim** : 从字符串的开头和结尾去除空格(或其他字符)。 可选参数 characterMask 指定额外的剥离字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Trim)] + [[play](https://go.dev/play/p/Y0ilP0NRV3j)] +- **SplitAndTrim** : 将字符串 str 按字符串 delimiter 拆分为一个切片,并对该数组的每个元素调用 Trim。忽略 Trim 后为空的元素。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SplitAndTrim)] + [[play](https://go.dev/play/p/ZNL6o4SkYQ7)] +- **HideString** : 隐藏源字符串中的一些字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HideString)] + [[play](https://go.dev/play/p/pzbaIVCTreZ)] +- **RemoveWhiteSpace** : 删除字符串中的空格。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)] + [[play](https://go.dev/play/p/HzLC9vsTwkf)] +- **SubInBetween** : 获取字符串中指定的起始字符串 start 和终止字符串 end 直接的子字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)] + [[play](https://go.dev/play/p/EDbaRvjeNsv)] +- **HammingDistance** : 计算两个字符串之间的汉明距离。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)] + [[play](https://go.dev/play/p/glNdQEA9HUi)] +- **Concat** : 拼接字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Concat)] + [[play](https://go.dev/play/p/gD52SZHr4Kp)] +- **Ellipsis** : 将字符串截断到指定长度,并在末尾添加省略号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Ellipsis)] + [[play](https://go.dev/play/p/i1vbdQiQVRR)] +- **Shuffle** : 打乱给定字符串中的字符顺序。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Shuffle)] + [[play](https://go.dev/play/p/iStFwBwyGY7)] +- **Rotate** : 按指定的字符数旋转字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)] + [[play](https://go.dev/play/p/Kf03iOeT5bd)] +- **TemplateReplace** : 将模板字符串中的占位符替换为 map 中的相应值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)] + [[play](https://go.dev/play/p/cXSuFvyZqv9)] +- **RegexMatchAllGroups** : 使用正则表达式匹配字符串中的所有子组并返回结果。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RegexMatchAllGroups)] + [[play](https://go.dev/play/p/JZiu0RXpgN-)] +- **ExtractContent** : 提取两个标记之间的内容。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#ExtractContent)] + [[play](https://go.dev/play/p/Ay9UIk7Rum9)] +- **FindAllOccurrences** : 返回子字符串在字符串中所有出现的位置。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#FindAllOccurrences)] + [[play](https://go.dev/play/p/uvyA6azGLB1)] + +

23. system 包含 os, runtime, shell command 的相关函数。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/system" +``` + +#### 函数列表: +- **IsWindows** : 检查当前操作系统是否是 windows。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#IsWindows)] + [[play](https://go.dev/play/p/XzJULbzmf9m)] +- **IsLinux** : 检查当前操作系统是否是 linux。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#IsLinux)] + [[play](https://go.dev/play/p/zIflQgZNuxD)] +- **IsMac** : 检查当前操作系统是否是 macos。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#IsMac)] + [[play](https://go.dev/play/p/Mg4Hjtyq7Zc)] +- **GetOsEnv** : 根据 key 获取对应的环境变量值 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#GetOsEnv)] + [[play](https://go.dev/play/p/D88OYVCyjO-)] +- **SetOsEnv** : 设置环境变量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#SetOsEnv)] + [[play](https://go.dev/play/p/D88OYVCyjO-)] +- **RemoveOsEnv** : 删除环境变量。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#RemoveOsEnv)] + [[play](https://go.dev/play/p/fqyq4b3xUFQ)] +- **CompareOsEnv** : 换取环境变量并与传入值进行比较。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#CompareOsEnv)] + [[play](https://go.dev/play/p/BciHrKYOHbp)] +- **ExecCommand** : 执行 shell 命令。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#ExecCommand)] + [[play](https://go.dev/play/p/n-2fLyZef-4)] +- **GetOsBits** : 获取当前操作系统位数(32/64)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#GetOsBits)] + [[play](https://go.dev/play/p/ml-_XH3gJbW)] +- **StartProcess** :创建进程。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StartProcess)] + [[play](https://go.dev/play/p/5GVol6ryS_X)] +- **StopProcess** : 停止进程。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StopProcess)] + [[play](https://go.dev/play/p/jJZhRYGGcmD)] +- **KillProcess** : 杀掉进程。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)] + [[play](https://go.dev/play/p/XKmvV-ExBWa)] +- **GetProcessInfo** : 根据进程 id 获取进程信息。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)] + [[play](https://go.dev/play/p/NQDVywEYYx7)] + +

24. Tuple 包实现一个元组数据类型。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/tuple" +``` + +#### 函数列表: + +- **Tuple2** : 2 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple2)] + [[play](https://go.dev/play/p/3sHVqBQpLYN)] +- **Tuple2_Unbox** : 返回 2 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple2_Unbox)] + [[play](https://go.dev/play/p/0fD1qfCVwjm)] +- **Zip2** : 创建一个 Tuple2 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip2)] + [[play](https://go.dev/play/p/4ncWJJ77Xio)] +- **Unzip2** : 根据传入的 Tuple2 切片,创建一组和 Tuple2 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip2)] + [[play](https://go.dev/play/p/KBecr60feXb)] +- **Tuple3** : 3 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple3)] + [[play](https://go.dev/play/p/FtH2sdCLlCf)] +- **Tuple3_Unbox** : 返回 3 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple3_Unbox)] + [[play](https://go.dev/play/p/YojLy-id1BS)] +- **Zip3** : 创建一个 Tuple3 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip3)] + [[play](https://go.dev/play/p/97NgmsTILfu)] +- **Unzip3** : 根据传入的 Tuple3 切片,创建一组和 Tuple3 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip3)] + [[play](https://go.dev/play/p/bba4cpAa7KO)] +- **Tuple4** : 4 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple4)] + [[play](https://go.dev/play/p/D2EqDz096tk)] +- **Tuple4_Unbox** : 返回 4 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple4_Unbox)] + [[play](https://go.dev/play/p/ACj9YuACGgW)] +- **Zip4** : 创建一个 Tuple4 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip4)] + [[play](https://go.dev/play/p/PEmTYVK5hL4)] +- **Unzip4** : 根据传入的 Tuple4 切片,创建一组和 Tuple4 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip4)] + [[play](https://go.dev/play/p/rb8z4gyYSRN)] +- **Tuple5** : 5 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple5)] + [[play](https://go.dev/play/p/2WndmVxPg-r)] +- **Tuple5_Unbox** : 返回 5 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple5_Unbox)] + [[play](https://go.dev/play/p/GyIyZHjCvoS)] +- **Zip5** : 创建一个 Tuple5 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip5)] + [[play](https://go.dev/play/p/fCAAJLMfBIP)] +- **Unzip5** : 根据传入的 Tuple5 切片,创建一组和 Tuple5 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip5)] + [[play](https://go.dev/play/p/gyl6vKfhqPb)] +- **Tuple6** : 6 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple6)] + [[play](https://go.dev/play/p/VjqcCwEJZbs)] +- **Tuple6_Unbox** : 返回 6 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple6_Unbox)] + [[play](https://go.dev/play/p/FjIHV7lpxmW)] +- **Zip6** : 创建一个 Tuple6 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip6)] + [[play](https://go.dev/play/p/oWPrnUYuFHo)] +- **Unzip6** : 根据传入的 Tuple6 切片,创建一组和 Tuple6 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip6)] + [[play](https://go.dev/play/p/l41XFqCyh5E)] +- **Tuple7** : 7 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple7)] + [[play](https://go.dev/play/p/dzAgv_Ezub9)] +- **Tuple7_Unbox** : 返回 7 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple7_Unbox)] + [[play](https://go.dev/play/p/R9I8qeDk0zs)] +- **Zip7** : 创建一个 Tuple7 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip7)] + [[play](https://go.dev/play/p/WUJuo897Egf)] +- **Unzip7** : 根据传入的 Tuple7 切片,创建一组和 Tuple7 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip7)] + [[play](https://go.dev/play/p/hws_P1Fr2j3)] +- **Tuple8** : 8 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple8)] + [[play](https://go.dev/play/p/YA9S0rz3dRz)] +- **Tuple8_Unbox** : 返回 8 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple8_Unbox)] + [[play](https://go.dev/play/p/PRxLBBb4SMl)] +- **Zip8** : 创建一个 Tuple8 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip8)] + [[play](https://go.dev/play/p/8V9jWkuJfaQ)] +- **Unzip8** : 根据传入的 Tuple8 切片,创建一组和 Tuple8 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip8)] + [[play](https://go.dev/play/p/1SndOwGsZB4)] +- **Tuple9** : 9 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple9)] + [[play](https://go.dev/play/p/yS2NGGtZpQr)] +- **Tuple9_Unbox** : 返回 9 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple9_Unbox)] + [[play](https://go.dev/play/p/oFJFGTAuOa8)] +- **Zip9** : 创建一个 Tuple9 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip9)] + [[play](https://go.dev/play/p/cgsL15QYnfz)] +- **Unzip9** : 根据传入的 Tuple9 切片,创建一组和 Tuple9 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip9)] + [[play](https://go.dev/play/p/91-BU_KURSA)] +- **Tuple10** : 10 元元组 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple10)] + [[play](https://go.dev/play/p/799qqZg0hUv)] +- **Tuple10_Unbox** : 返回 10 元元组的字段值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Tuple10_Unbox)] + [[play](https://go.dev/play/p/qfyx3x_X0Cu)] +- **Zip10** : 创建一个 Tuple10 元组切片, 其中元组的元素和传入切片元素相对应。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Zip10)] + [[play](https://go.dev/play/p/YSR-2cXnrY4)] +- **Unzip10** : 根据传入的 Tuple10 切片,创建一组和 Tuple10 元素相对应的切片。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/tuple.md#Unzip10)] + [[play](https://go.dev/play/p/-taQB6Wfre_z)] + +

25. validator 验证器包,包含常用字符串格式验证函数。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/validator" +``` + +#### 函数列表: + +- **ContainChinese** : 验证字符串是否包含中文字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#ContainChinese)] + [[play](https://go.dev/play/p/7DpU0uElYeM)] +- **ContainLetter** : 验证字符串是否包含至少一个英文字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#ContainLetter)] + [[play](https://go.dev/play/p/lqFD04Yyewp)] +- **ContainLower** : 验证字符串是否包含至少一个英文小写字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#ContainLower)] + [[play](https://go.dev/play/p/Srqi1ItvnAA)] +- **ContainUpper** : 验证字符串是否包含至少一个英文大写字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#ContainUpper)] + [[play](https://go.dev/play/p/CmWeBEk27-z)] +- **IsAlpha** : 验证字符串是否只包含英文字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAlpha)] + [[play](https://go.dev/play/p/7Q5sGOz2izQ)] +- **IsAllUpper** : 验证字符串是否全是大写英文字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAllUpper)] + [[play](https://go.dev/play/p/ZHctgeK1n4Z)] +- **IsAllLower** : 验证字符串是否全是小写英文字母。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAllLower)] + [[play](https://go.dev/play/p/GjqCnOfV6cM)] +- **IsBase64** : 验证字符串是否是 base64 编码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBase64)] + [[play](https://go.dev/play/p/sWHEySAt6hl)] +- **IsChineseMobile** : 验证字符串是否是中国手机号码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChineseMobile)] + [[play](https://go.dev/play/p/GPYUlGTOqe3)] +- **IsChineseIdNum** : 验证字符串是否是中国身份证号码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChineseIdNum)] + [[play](https://go.dev/play/p/d8EWhl2UGDF)] +- **IsChinesePhone** : 验证字符串是否是中国电话座机号码(xxx-xxxxxxxx or xxxx-xxxxxxx.)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChinesePhone)] + [[play](https://go.dev/play/p/RUD_-7YZJ3I)] +- **IsCreditCard** : 验证字符串是否是信用卡号码。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsCreditCard)] + [[play](https://go.dev/play/p/sNwwL6B0-v4)] +- **IsDns** : 验证字符串是否是有效 dns。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsDns)] + [[play](https://go.dev/play/p/jlYApVLLGTZ)] +- **IsEmail** : 验证字符串是否是有效电子邮件地址。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsEmail)] + [[play](https://go.dev/play/p/Os9VaFlT33G)] +- **IsEmptyString** : 验证字符串是否是空字符串。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsEmptyString)] + [[play](https://go.dev/play/p/dpzgUjFnBCX)] +- **IsFloat** : 验证参数是否是浮点数((float32,float34)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsFloat)] + [[play](https://go.dev/play/p/vsyG-sxr99_Z)] +- **IsFloatStr** : 验证字符串是否是可以转换为浮点数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsFloatStr)] + [[play](https://go.dev/play/p/LOYwS_Oyl7U)] +- **IsNumber** : 验证参数是否是数字(integer,float)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsNumber)] + [[play](https://go.dev/play/p/mdJHOAvtsvF)] +- **IsNumberStr** : 验证字符串是否是可以转换为数字。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsNumberStr)] + [[play](https://go.dev/play/p/LzaKocSV79u)] +- **IsAlphaNumeric** : 验证字符串是字母或数字。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAlphaNumeric)] + [[play](https://go.dev/play/p/RHeESLrLg9c)] +- **IsJSON** : 验证字符串是否是有效 json。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJSON)] + [[play](https://go.dev/play/p/8Kip1Itjiil)] +- **IsRegexMatch** : 验证字符串是否可以匹配正则表达式。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsRegexMatch)] + [[play](https://go.dev/play/p/z_XeZo_litG)] +- **IsInt** : 验证参数是否是整数(int, unit)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsInt)] + [[play](https://go.dev/play/p/eFoIHbgzl-z)] +- **IsIntStr** : 验证字符串是否是可以转换为整数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIntStr)] + [[play](https://go.dev/play/p/jQRtFv-a0Rk)] +- **IsIp** : 验证字符串是否是 ip 地址。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIp)] + [[play](https://go.dev/play/p/FgcplDvmxoD)] +- **IsIpV4** : 验证字符串是否是 ipv4 地址。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV4)] + [[play](https://go.dev/play/p/zBGT99EjaIu)] +- **IsIpV6** : 验证字符串是否是 ipv6 地址。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsIpV6)] + [[play](https://go.dev/play/p/AHA0r0AzIdC)] +- **IsIpPort** : 检查字符串是否是 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)] +- **IsStrongPassword** : 验证字符串是否是强密码:(字母+数字+特殊字符)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsStrongPassword)] + [[play](https://go.dev/play/p/QHdVcSQ3uDg)] +- **IsUrl** : 验证字符串是否是 url。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsUrl)] + [[play](https://go.dev/play/p/pbJGa7F98Ka)] +- **IsWeakPassword** : 验证字符串是否是弱密码(只包含字母+数字)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsWeakPassword)] + [[play](https://go.dev/play/p/wqakscZH5gH)] +- **IsZeroValue** : 判断传入的参数值是否为零值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsZeroValue)] + [[play](https://go.dev/play/p/UMrwaDCi_t4)] +- **IsGBK** : 检查数据编码是否为 gbk(汉字内部代码扩展规范)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsGBK)] + [[play](https://go.dev/play/p/E2nt3unlmzP)] +- **IsASCII** : 验证字符串全部为 ASCII 字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsASCII)] + [[play](https://go.dev/play/p/hfQNPLX0jNa)] +- **IsPrintable** : 检查字符串是否全部为可打印字符。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsPrintable)] + [[play](https://go.dev/play/p/Pe1FE2gdtTP)] +- **IsBin** : 检查字符串是否是有效的二进制数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBin)] + [[play](https://go.dev/play/p/ogPeg2XJH4P)] +- **IsHex** : 检查字符串是否是有效的十六进制数。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsHex)] + [[play](https://go.dev/play/p/M2qpHbEwmm7)] +- **IsBase64URL** : 检查字符串是否是有效的 base64 url。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBase64URL)] + [[play](https://go.dev/play/p/vhl4mr8GZ6S)] +- **IsJWT** : 检查字符串是否是有效的 JSON Web Token (JWT)。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJWT)] + [[play](https://go.dev/play/p/R6Op7heJbKI)] +- **IsVisa** : 检查字符串是否是有效的 visa 卡号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsVisa)] + [[play](https://go.dev/play/p/SdS2keOyJsl)] +- **IsMasterCard** : 检查字符串是否是有效的 MasterCard 卡号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsMasterCard)] + [[play](https://go.dev/play/p/CwWBFRrG27b)] +- **IsAmericanExpress** : 检查字符串是否是有效的 American Express 卡号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsAmericanExpress)] + [[play](https://go.dev/play/p/HIDFpcOdpkd)] +- **IsUnionPay** : 检查字符串是否是有效的美国银联卡号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsUnionPay)] + [[play](https://go.dev/play/p/CUHPEwEITDf)] +- **IsChinaUnionPay** : 检查字符串是否是有效的中国银联卡号。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsChinaUnionPay)] + [[play](https://go.dev/play/p/yafpdxLiymu)] + +

26. xerror 包实现一些错误处理函数。       回到目录

+ +```go +import "github.com/duke-git/lancet/v2/xerror" +``` + +#### 函数列表: + +- **New** : 创建 XError 对象实例。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#New)] + [[play](https://go.dev/play/p/w4oWZts7q7f)] +- **Wrap** : 根据 error 对象创建 XError 对象实例,可添加 message。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#Wrap)] + [[play](https://go.dev/play/p/5385qT2dCi4)] +- **Unwrap** : 从 error 对象中解构出 XError。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#Unwrap)] + [[play](https://go.dev/play/p/LKMLep723tu)] +- **XError_Wrap** : 创建新的 XError 对象并将消息和 id 复制到新的对象中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Wrap)] + [[play](https://go.dev/play/p/RpjJ5u5sc97)] +- **XError_Unwrap** : 解构 XEerror 为 error 对象。适配 github.com/pkg/errors。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Unwrap) + [[play](https://go.dev/play/p/VUXJ8BST4c6)] +- **XError_With** : 添加与 XError 对象的键和值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_With)] + [[play](https://go.dev/play/p/ow8UISXX_Dp)] +- **XError_Id** : 设置 XError 对象的 id。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Id)] + [[play](https://go.dev/play/p/X6HBlsy58U9)] +- **XError_Is** : 检查目标 error 是否为 XError,两个错误中的 error.id 是否匹配。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Is)] + [[play](https://go.dev/play/p/X6HBlsy58U9)] +- **XError_Values** : 返回由 With 设置的键和值的映射。将合并所有 XError 键和值。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Values)] + [[play](https://go.dev/play/p/ow8UISXX_Dp)] +- **XError_StackTrace** : 返回与 pkg/error 兼容的堆栈信息。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_StackTrace)] + [[play](https://go.dev/play/p/6FAvSQpa7pc)] +- **XError_Info** : 返回可打印的 XError 对象信息。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Info)] + [[play](https://go.dev/play/p/1ZX0ME1F-Jb)] +- **XError_Error** : 实现标准库的 error 接口。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/xerror.md#XError_Error)] + [[play](https://go.dev/play/p/w4oWZts7q7f)] +- **TryUnwrap** : 检查 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)] +- **TryCatch** : 简单实现的 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)] ## 如何贡献代码 -非常感激任何的代码提交以使lancet的功能越来越强大。创建pull request时请遵守以下规则。 +#### [代码贡献指南](./CONTRIBUTION.zh-CN.md) + +## 贡献者 + +感谢所有为 lancet 贡献过代码的人! + + + + + +## GitHub Stars -1. Fork lancet仓库。 -2. 创建自己的特性分支。 -3. 提交变更。 -4. Push分支。 -5. 创建新的pull request。 \ No newline at end of file +[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..ad4de077 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions +Here is the lancet version and compatibility with go language version. + +| Version | Supported | +| ------- | ------------------| +| 2.x.x | +go v1.18 | +| 1.x.x | +go v1.12 | + + +## Reporting a Vulnerability + +For now, there is no public website to report a vulnerability, If you find security issue in lancet, you can send it to me via my email `lanliddd.2007@163.com`. +we can discuss it. I am appreciate if someone can create a public page for reporting vulnerability. diff --git a/algorithm/lrucache.go b/algorithm/lrucache.go new file mode 100644 index 00000000..db320532 --- /dev/null +++ b/algorithm/lrucache.go @@ -0,0 +1,121 @@ +package algorithm + +type lruNode[K comparable, V any] struct { + key K + value V + pre *lruNode[K, V] + next *lruNode[K, V] +} + +// newLruNode return a lruNode pointer +func newLruNode[K comparable, V any](key K, value V) *lruNode[K, V] { + return &lruNode[K, V]{ + key: key, + value: value, + pre: nil, + next: nil, + } +} + +// LRUCache lru cache (thread unsafe) +type LRUCache[K comparable, V any] struct { + cache map[K]*lruNode[K, V] + head *lruNode[K, V] + tail *lruNode[K, V] + capacity int + length int +} + +// NewLRUCache creates a LRUCache pointer instance. +func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] { + return &LRUCache[K, V]{ + cache: make(map[K]*lruNode[K, V], capacity), + head: nil, + tail: nil, + capacity: capacity, + length: 0, + } +} + +// Get value of key from lru cache. +// Play: https://go.dev/play/p/iUynEfOP8G0 +func (l *LRUCache[K, V]) Get(key K) (V, bool) { + var value V + + node, ok := l.cache[key] + if ok { + l.moveToTail(node) + return node.value, true + } + + return value, false +} + +// Put value of key into lru cache. +// Play: https://go.dev/play/p/iUynEfOP8G0 +func (l *LRUCache[K, V]) Put(key K, value V) { + node, ok := l.cache[key] + if !ok { + newNode := newLruNode(key, value) + l.cache[key] = newNode + l.addNode(newNode) + + if len(l.cache) > l.capacity { + oldKey := l.deleteNode(l.head) + delete(l.cache, oldKey) + } + } else { + node.value = value + l.moveToTail(node) + } + l.length = len(l.cache) +} + +// Delete item from lru cache. +func (l *LRUCache[K, V]) Delete(key K) bool { + node, ok := l.cache[key] + if ok { + key := l.deleteNode(node) + delete(l.cache, key) + return true + } + l.length = len(l.cache) + return false +} + +// Len returns the number of items in the cache. +func (l *LRUCache[K, V]) Len() int { + return l.length +} + +func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) { + if l.tail != nil { + l.tail.next = node + node.pre = l.tail + node.next = nil + } + l.tail = node + if l.head == nil { + l.head = node + } +} + +func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K { + if node == l.tail { + l.tail = l.tail.pre + } else if node == l.head { + l.head = l.head.next + } else { + node.pre.next = node.next + node.next.pre = node.pre + } + return node.key +} + +func (l *LRUCache[K, V]) moveToTail(node *lruNode[K, V]) { + if l.tail == node { + return + } + l.deleteNode(node) + l.addNode(node) +} diff --git a/algorithm/lrucache_example_test.go b/algorithm/lrucache_example_test.go new file mode 100644 index 00000000..b792b741 --- /dev/null +++ b/algorithm/lrucache_example_test.go @@ -0,0 +1,79 @@ +package algorithm + +import "fmt" + +func ExampleLRUCache_Put() { + cache := NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result1, ok1 := cache.Get(1) + result2, ok2 := cache.Get(2) + result3, ok3 := cache.Get(3) + + fmt.Println(result1, ok1) + fmt.Println(result2, ok2) + fmt.Println(result3, ok3) + + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleLRUCache_Get() { + cache := NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result1, ok1 := cache.Get(1) + result2, ok2 := cache.Get(2) + result3, ok3 := cache.Get(3) + + fmt.Println(result1, ok1) + fmt.Println(result2, ok2) + fmt.Println(result3, ok3) + + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleLRUCache_Delete() { + cache := NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result1, ok1 := cache.Get(1) + + ok2 := cache.Delete(2) + + _, ok3 := cache.Get(2) + + fmt.Println(result1, ok1) + fmt.Println(ok2) + fmt.Println(ok3) + + // Output: + // 1 true + // true + // false +} + +func ExampleLRUCache_Len() { + cache := NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result := cache.Len() + + fmt.Println(result) + + // Output: + // 2 +} diff --git a/algorithm/lrucache_test.go b/algorithm/lrucache_test.go new file mode 100644 index 00000000..61454450 --- /dev/null +++ b/algorithm/lrucache_test.go @@ -0,0 +1,34 @@ +package algorithm + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestLRUCache(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestLRUCache") + + cache := NewLRUCache[int, int](3) + + cache.Put(1, 1) + cache.Put(2, 2) + cache.Put(3, 3) + + assert.Equal(3, cache.Len()) + + v, ok := cache.Get(1) + assert.Equal(true, ok) + assert.Equal(1, v) + + v, ok = cache.Get(2) + assert.Equal(true, ok) + assert.Equal(2, v) + + ok = cache.Delete(2) + assert.Equal(true, ok) + + _, ok = cache.Get(2) + assert.Equal(false, ok) +} diff --git a/algorithm/search.go b/algorithm/search.go new file mode 100644 index 00000000..da1202fd --- /dev/null +++ b/algorithm/search.go @@ -0,0 +1,66 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package algorithm contain some basic algorithm functions. eg. sort, search, list, linklist, stack, queue, tree, graph. +package algorithm + +import "github.com/duke-git/lancet/v2/constraints" + +// Search algorithms see https://github.com/TheAlgorithms/Go/tree/master/search + +// LinearSearch return the index of target in slice base on equal function. +// If not found return -1 +// Play: https://go.dev/play/p/IsS7rgn5s3x +func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int { + for i, v := range slice { + if equal(v, target) { + return i + } + } + return -1 +} + +// BinarySearch return the index of target within a sorted slice, use binary search (recursive call itself). +// If not found return -1. +// Play: https://go.dev/play/p/t6MeGiUSN47 +func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int { + if highIndex < lowIndex || len(sortedSlice) == 0 { + return -1 + } + + midIndex := int(lowIndex + (highIndex-lowIndex)/2) + isMidValGreatTarget := comparator.Compare(sortedSlice[midIndex], target) == 1 + isMidValLessTarget := comparator.Compare(sortedSlice[midIndex], target) == -1 + + if isMidValGreatTarget { + return BinarySearch(sortedSlice, target, lowIndex, midIndex-1, comparator) + } else if isMidValLessTarget { + return BinarySearch(sortedSlice, target, midIndex+1, highIndex, comparator) + } + + return midIndex +} + +// BinaryIterativeSearch return the index of target within a sorted slice, use binary search (no recursive). +// If not found return -1. +// Play: https://go.dev/play/p/Anozfr8ZLH3 +func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int { + startIndex := lowIndex + endIndex := highIndex + + var midIndex int + for startIndex <= endIndex { + midIndex = int(startIndex + (endIndex-startIndex)/2) + isMidValGreatTarget := comparator.Compare(sortedSlice[midIndex], target) == 1 + isMidValLessTarget := comparator.Compare(sortedSlice[midIndex], target) == -1 + + if isMidValGreatTarget { + endIndex = midIndex - 1 + } else if isMidValLessTarget { + startIndex = midIndex + 1 + } else { + return midIndex + } + } + return -1 +} diff --git a/algorithm/search_example_test.go b/algorithm/search_example_test.go new file mode 100644 index 00000000..7d5583b0 --- /dev/null +++ b/algorithm/search_example_test.go @@ -0,0 +1,51 @@ +package algorithm + +import "fmt" + +func ExampleLinearSearch() { + numbers := []int{3, 4, 5, 3, 2, 1} + + equalFunc := func(a, b int) bool { + return a == b + } + + result1 := LinearSearch(numbers, 3, equalFunc) + result2 := LinearSearch(numbers, 6, equalFunc) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} + +func ExampleBinarySearch() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := BinarySearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := BinarySearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} + +func ExampleBinaryIterativeSearch() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} diff --git a/algorithm/search_test.go b/algorithm/search_test.go new file mode 100644 index 00000000..b7665efa --- /dev/null +++ b/algorithm/search_test.go @@ -0,0 +1,41 @@ +package algorithm + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestLinearSearch(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestLinearSearch") + + numbers := []int{3, 4, 5, 3, 2, 1} + equalFunc := func(a, b int) bool { + return a == b + } + + assert.Equal(0, LinearSearch(numbers, 3, equalFunc)) + assert.Equal(-1, LinearSearch(numbers, 6, equalFunc)) +} + +func TestBinarySearch(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBinarySearch") + + sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + assert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator)) + assert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator)) +} + +func TestBinaryIterativeSearch(t *testing.T) { + assert := internal.NewAssert(t, "TestBinaryIterativeSearch") + + sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + assert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator)) + assert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator)) +} diff --git a/algorithm/sort.go b/algorithm/sort.go new file mode 100644 index 00000000..ad2b6ad4 --- /dev/null +++ b/algorithm/sort.go @@ -0,0 +1,204 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +package algorithm + +import "github.com/duke-git/lancet/v2/constraints" + +// BubbleSort applys the bubble sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/GNdv7Jg2Taj +func BubbleSort[T any](slice []T, comparator constraints.Comparator) { + for i := 0; i < len(slice); i++ { + breakTag := false + for j := 0; j < len(slice)-1-i; j++ { + isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1 + if isCurrGreatThanNext { + swap(slice, j, j+1) + breakTag = true + } + } + if !breakTag { + break + } + } +} + +// InsertionSort applys the insertion sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/G5LJiWgJJW6 +func InsertionSort[T any](slice []T, comparator constraints.Comparator) { + for i := 0; i < len(slice); i++ { + for j := i; j > 0; j-- { + isPreLessThanCurrent := comparator.Compare(slice[j], slice[j-1]) == -1 + if isPreLessThanCurrent { + swap(slice, j, j-1) + } else { + break + } + } + } +} + +// SelectionSort applys the selection sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/oXovbkekayS +func SelectionSort[T any](slice []T, comparator constraints.Comparator) { + for i := 0; i < len(slice); i++ { + min := i + for j := i + 1; j < len(slice); j++ { + if comparator.Compare(slice[j], slice[min]) == -1 { + min = j + } + } + swap(slice, i, min) + } +} + +// ShellSort applys the shell sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/3ibkszpJEu3 +func ShellSort[T any](slice []T, comparator constraints.Comparator) { + size := len(slice) + + gap := 1 + for gap < size/3 { + gap = 3*gap + 1 + } + + for gap >= 1 { + for i := gap; i < size; i++ { + for j := i; j >= gap && comparator.Compare(slice[j], slice[j-gap]) == -1; j -= gap { + swap(slice, j, j-gap) + } + } + gap = gap / 3 + } +} + +// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1. +// Play: https://go.dev/play/p/7Y7c1Elk3ax +func QuickSort[T any](slice []T, comparator constraints.Comparator) { + quickSort(slice, 0, len(slice)-1, comparator) +} + +func quickSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) { + if lowIndex < highIndex { + p := partition(slice, lowIndex, highIndex, comparator) + quickSort(slice, lowIndex, p-1, comparator) + quickSort(slice, p+1, highIndex, comparator) + } +} + +// partition split slice into two parts +func partition[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) int { + p := slice[highIndex] + i := lowIndex + for j := lowIndex; j < highIndex; j++ { + if comparator.Compare(slice[j], p) == -1 { //slice[j] < p + swap(slice, i, j) + i++ + } + } + + swap(slice, i, highIndex) + + return i +} + +// HeapSort applys the heap sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/u6Iwa1VZS_f +func HeapSort[T any](slice []T, comparator constraints.Comparator) { + size := len(slice) + + for i := size/2 - 1; i >= 0; i-- { + sift(slice, i, size-1, comparator) + } + for j := size - 1; j > 0; j-- { + swap(slice, 0, j) + sift(slice, 0, j-1, comparator) + } +} + +func sift[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) { + i := lowIndex + j := 2*i + 1 + + temp := slice[i] + for j <= highIndex { + if j < highIndex && comparator.Compare(slice[j], slice[j+1]) == -1 { //slice[j] < slice[j+1] + j++ + } + if comparator.Compare(temp, slice[j]) == -1 { //tmp < slice[j] + slice[i] = slice[j] + i = j + j = 2*i + 1 + } else { + break + } + } + slice[i] = temp +} + +// MergeSort applys the merge sort algorithm to sort the collection, will change the original collection data. +// Play: https://go.dev/play/p/ydinn9YzUJn +func MergeSort[T any](slice []T, comparator constraints.Comparator) { + mergeSort(slice, 0, len(slice)-1, comparator) +} + +func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) { + if lowIndex < highIndex { + mid := (lowIndex + highIndex) / 2 + mergeSort(slice, lowIndex, mid, comparator) + mergeSort(slice, mid+1, highIndex, comparator) + merge(slice, lowIndex, mid, highIndex, comparator) + } +} + +func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator constraints.Comparator) { + i := lowIndex + j := midIndex + 1 + temp := []T{} + + for i <= midIndex && j <= highIndex { + //slice[i] < slice[j] + if comparator.Compare(slice[i], slice[j]) == -1 { + temp = append(temp, slice[i]) + i++ + } else { + temp = append(temp, slice[j]) + j++ + } + } + + if i <= midIndex { + temp = append(temp, slice[i:midIndex+1]...) + } else { + temp = append(temp, slice[j:highIndex+1]...) + } + + for k := 0; k < len(temp); k++ { + slice[lowIndex+k] = temp[k] + } +} + +// CountSort applys the count sort algorithm to sort the collection, don't change the original collection data. +// Play: https://go.dev/play/p/tB-Umgm0DrP +func CountSort[T any](slice []T, comparator constraints.Comparator) []T { + size := len(slice) + out := make([]T, size) + + for i := 0; i < size; i++ { + count := 0 + for j := 0; j < size; j++ { + //slice[i] > slice[j] + if comparator.Compare(slice[i], slice[j]) == 1 { + count++ + } + } + out[count] = slice[i] + } + + return out +} + +// swap two slice value at index i and j +func swap[T any](slice []T, i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} diff --git a/algorithm/sort_example_test.go b/algorithm/sort_example_test.go new file mode 100644 index 00000000..b9c6047e --- /dev/null +++ b/algorithm/sort_example_test.go @@ -0,0 +1,93 @@ +package algorithm + +import "fmt" + +func ExampleBubbleSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + BubbleSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleCountSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + sortedNumber := CountSort(numbers, comparator) + + fmt.Println(numbers) + fmt.Println(sortedNumber) + // Output: + // [2 1 5 3 6 4] + // [1 2 3 4 5 6] +} + +func ExampleHeapSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + HeapSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleMergeSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + MergeSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleInsertionSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + InsertionSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleSelectionSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + SelectionSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleShellSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + ShellSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleQuickSort() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + QuickSort(numbers, comparator) + + fmt.Println(numbers) + // Output: + // [1 2 3 4 5 6] +} diff --git a/algorithm/sort_test.go b/algorithm/sort_test.go new file mode 100644 index 00000000..e96a5e68 --- /dev/null +++ b/algorithm/sort_test.go @@ -0,0 +1,217 @@ +package algorithm + +import ( + "fmt" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +// People test mock data +type people struct { + Name string + Age int +} + +// PeopleAageComparator sort people slice by age field +type peopleAgeComparator struct{} + +// Compare implements github.com/duke-git/lancet/v2/constraints/constraints.go/Comparator +func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int { + p1, _ := v1.(people) + p2, _ := v2.(people) + + //ascending order + if p1.Age < p2.Age { + return -1 + } else if p1.Age > p2.Age { + return 1 + } + return 0 +} + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func TestBubbleSortForStructSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBubbleSortForStructSlice") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + BubbleSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestBubbleSortForIntSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBubbleSortForIntSlice") + + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + BubbleSort(numbers, comparator) + + assert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers) +} + +func TestInsertionSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestInsertionSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + InsertionSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestSelectionSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSelectionSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + SelectionSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestShellSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestShellSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + ShellSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestQuickSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestQuickSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + QuickSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestHeapSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestHeapSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + HeapSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestMergeSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestMergeSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + MergeSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", peoples) + + assert.Equal(expected, actual) +} + +func TestCountSort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCountSort") + + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + comparator := &peopleAgeComparator{} + sortedPeopleByAge := CountSort(peoples, comparator) + + expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" + actual := fmt.Sprintf("%v", sortedPeopleByAge) + + assert.Equal(expected, actual) +} diff --git a/compare/compare.go b/compare/compare.go new file mode 100644 index 00000000..e3cd33b9 --- /dev/null +++ b/compare/compare.go @@ -0,0 +1,72 @@ +// Copyright 2023 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package compare provides a lightweight comparison function on any type. +// reference: https://github.com/stretchr/testify +package compare + +import ( + "reflect" + "time" + + "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/mathutil" + "golang.org/x/exp/constraints" +) + +// operator type +const ( + equal = "eq" + lessThan = "lt" + greaterThan = "gt" + lessOrEqual = "le" + greaterOrEqual = "ge" +) + +var ( + timeType = reflect.TypeOf(time.Time{}) + bytesType = reflect.TypeOf([]byte{}) +) + +// Equal checks if two values are equal or not. (check both type and value) +// Play: https://go.dev/play/p/wmVxR-to4lz +func Equal(left, right any) bool { + return compareValue(equal, left, right) +} + +// EqualValue checks if two values are equal or not. (check value only) +// Play: https://go.dev/play/p/fxnna_LLD9u +func EqualValue(left, right any) bool { + ls, rs := convertor.ToString(left), convertor.ToString(right) + return ls == rs +} + +// LessThan checks if value `left` less than value `right`. +// Play: https://go.dev/play/p/cYh7FQQj0ne +func LessThan(left, right any) bool { + return compareValue(lessThan, left, right) +} + +// GreaterThan checks if value `left` greater than value `right`. +// Play: https://go.dev/play/p/9-NYDFZmIMp +func GreaterThan(left, right any) bool { + return compareValue(greaterThan, left, right) +} + +// LessOrEqual checks if value `left` less than or equal to value `right`. +// Play: https://go.dev/play/p/e4T_scwoQzp +func LessOrEqual(left, right any) bool { + return compareValue(lessOrEqual, left, right) +} + +// GreaterOrEqual checks if value `left` greater than or equal to value `right`. +// Play: https://go.dev/play/p/vx8mP0U8DFk +func GreaterOrEqual(left, right any) bool { + return compareValue(greaterOrEqual, left, right) +} + +// InDelta checks if two values are equal or not within a delta. +// Play: https://go.dev/play/p/TuDdcNtMkjo +func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool { + return float64(mathutil.Abs(left-right)) <= delta +} diff --git a/compare/compare_example_test.go b/compare/compare_example_test.go new file mode 100644 index 00000000..055183d8 --- /dev/null +++ b/compare/compare_example_test.go @@ -0,0 +1,196 @@ +package compare + +import ( + "fmt" + "time" +) + +func ExampleEqual() { + result1 := Equal(1, 1) + result2 := Equal("1", "1") + result3 := Equal([]int{1, 2, 3}, []int{1, 2, 3}) + result4 := Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}) + + result5 := Equal(1, "1") + result6 := Equal(1, int64(1)) + result7 := Equal([]int{1, 2}, []int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} + +func ExampleEqualValue() { + result1 := EqualValue(1, 1) + result2 := EqualValue(int(1), int64(1)) + result3 := EqualValue(1, "1") + result4 := EqualValue(1, "2") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} + +func ExampleLessThan() { + result1 := LessThan(1, 2) + result2 := LessThan(1.1, 2.2) + result3 := LessThan("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := LessThan(time1, time2) + + result5 := LessThan(2, 1) + result6 := LessThan(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} + +func ExampleGreaterThan() { + result1 := GreaterThan(2, 1) + result2 := GreaterThan(2.2, 1.1) + result3 := GreaterThan("b", "a") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := GreaterThan(time2, time1) + + result5 := GreaterThan(1, 2) + result6 := GreaterThan(int64(2), 1) + result7 := GreaterThan("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} + +func ExampleLessOrEqual() { + result1 := LessOrEqual(1, 1) + result2 := LessOrEqual(1.1, 2.2) + result3 := LessOrEqual("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := LessOrEqual(time1, time2) + + result5 := LessOrEqual(2, 1) + result6 := LessOrEqual(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} + +func ExampleGreaterOrEqual() { + result1 := GreaterOrEqual(1, 1) + result2 := GreaterOrEqual(2.2, 1.1) + result3 := GreaterOrEqual("b", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := GreaterOrEqual(time2, time1) + + result5 := GreaterOrEqual(1, 2) + result6 := GreaterOrEqual(int64(2), 1) + result7 := GreaterOrEqual("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} + +func ExampleInDelta() { + result1 := InDelta(1, 1, 0) + result2 := InDelta(1, 2, 0) + + result3 := InDelta(2.0/3.0, 0.66667, 0.001) + result4 := InDelta(2.0/3.0, 0.0, 0.001) + + result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0) + result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // false + // true + // false + // false + // true +} diff --git a/compare/compare_internal.go b/compare/compare_internal.go new file mode 100644 index 00000000..fab1c073 --- /dev/null +++ b/compare/compare_internal.go @@ -0,0 +1,291 @@ +package compare + +import ( + "bytes" + "encoding/json" + "math/big" + "reflect" + "time" + + "github.com/duke-git/lancet/v2/convertor" +) + +func compareValue(operator string, left, right any) bool { + leftType, rightType := reflect.TypeOf(left), reflect.TypeOf(right) + + if leftType.Kind() != rightType.Kind() { + return false + } + + switch leftType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.Bool, reflect.String: + return compareBasicValue(operator, left, right) + + 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 +} + +func compareRefValue(operator string, leftObj, rightObj any, kind reflect.Kind) bool { + leftVal, rightVal := reflect.ValueOf(leftObj), reflect.ValueOf(rightObj) + + switch kind { + case reflect.Struct: + + // compare time + if leftVal.CanConvert(timeType) { + timeObj1, ok := leftObj.(time.Time) + if !ok { + timeObj1 = leftVal.Convert(timeType).Interface().(time.Time) + } + + timeObj2, ok := rightObj.(time.Time) + if !ok { + timeObj2 = rightVal.Convert(timeType).Interface().(time.Time) + } + + return compareBasicValue(operator, timeObj1.UnixNano(), timeObj2.UnixNano()) + } + + // for other struct type, only process equal operator + switch operator { + case equal: + return objectsAreEqualValues(leftObj, rightObj) + } + + case reflect.Slice: + // compare []byte + if leftVal.CanConvert(bytesType) { + bytesObj1, ok := leftObj.([]byte) + if !ok { + bytesObj1 = leftVal.Convert(bytesType).Interface().([]byte) + } + bytesObj2, ok := rightObj.([]byte) + if !ok { + bytesObj2 = rightVal.Convert(bytesType).Interface().([]byte) + } + + switch operator { + case equal: + if bytes.Equal(bytesObj1, bytesObj2) { + return true + } + case lessThan: + if bytes.Compare(bytesObj1, bytesObj2) == -1 { + return true + } + case greaterThan: + if bytes.Compare(bytesObj1, bytesObj2) == 1 { + return true + } + case lessOrEqual: + if bytes.Compare(bytesObj1, bytesObj2) <= 0 { + return true + } + case greaterOrEqual: + if bytes.Compare(bytesObj1, bytesObj2) >= 0 { + return true + } + } + + } + + // for other type slice, only process equal operator + switch operator { + case equal: + return reflect.DeepEqual(leftObj, rightObj) + } + + case reflect.Map: + // only process equal operator + switch operator { + case equal: + return reflect.DeepEqual(leftObj, rightObj) + } + } + + return false +} + +func objectsAreEqualValues(expected, actual interface{}) bool { + if objectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +func objectsAreEqual(expected, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) + } + + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) +} + +// compareBasic compare basic value: integer, float, string, bool +func compareBasicValue(operator string, leftValue, rightValue any) bool { + if leftValue == nil && rightValue == nil && operator == equal { + return true + } + + switch leftVal := leftValue.(type) { + case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64: + left, err := convertor.ToBigInt(leftValue) + if err != nil { + return false + } + + 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 + } + 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: + return compareStrings(operator, left, right) + } + + case bool: + left := leftVal + switch right := rightValue.(type) { + case bool: + 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 +} diff --git a/compare/compare_test.go b/compare/compare_test.go new file mode 100644 index 00000000..add0df17 --- /dev/null +++ b/compare/compare_test.go @@ -0,0 +1,203 @@ +package compare + +import ( + "encoding/json" + "math/big" + "testing" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestEqual(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEqual") + + 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), 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}, + } + + for _, tt := range tests { + assert.Equal(tt.want, Equal(tt.left, tt.right)) + } + +} + +func TestEqualValue(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEqualValue") + + 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}, + } + + 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") + + 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}, + } + + 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") + + 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}, + } + + for _, tt := range tests { + assert.Equal(tt.want, GreaterThan(tt.left, tt.right)) + } + +} + +func TestLessOrEqual(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestLessOrEqual") + + 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}, + } + + 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") + + 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}, + } + + 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") + + 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}, + } + + for _, tt := range tests { + assert.Equal(tt.want, InDelta(tt.left, tt.right, tt.delta)) + } +} diff --git a/concurrency/channel.go b/concurrency/channel.go new file mode 100644 index 00000000..876d14ec --- /dev/null +++ b/concurrency/channel.go @@ -0,0 +1,250 @@ +// Copyright 2021 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. +package concurrency + +import ( + "context" + "sync" +) + +// Channel is a logic object which can generate or manipulate go channel +// all methods of Channel are in the book tilted《Concurrency in Go》 +type Channel[T any] struct { +} + +// NewChannel return a Channel instance +func NewChannel[T any]() *Channel[T] { + return &Channel[T]{} +} + +// Generate creates channel, then put values into the channel. +// Play: https://go.dev/play/p/7aB4KyMMp9A +func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T { + dataStream := make(chan T) + + go func() { + defer close(dataStream) + + for _, v := range values { + select { + case <-ctx.Done(): + return + case dataStream <- v: + } + } + }() + + return dataStream +} + +// Repeat create channel, put values into the channel repeatly until cancel the context. +// Play: https://go.dev/play/p/k5N_ALVmYjE +func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T { + dataStream := make(chan T) + + go func() { + defer close(dataStream) + for { + for _, v := range values { + select { + case <-ctx.Done(): + return + case dataStream <- v: + } + } + } + }() + return dataStream +} + +// RepeatFn create a channel, excutes fn repeatly, and put the result into the channel +// until close context. +// Play: https://go.dev/play/p/4J1zAWttP85 +func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T { + dataStream := make(chan T) + + go func() { + defer close(dataStream) + for { + select { + case <-ctx.Done(): + return + case dataStream <- fn(): + } + } + }() + return dataStream +} + +// Take create a channel whose values are taken from another channel with limit number. +// Play: https://go.dev/play/p/9Utt-1pDr2J +func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T { + takeStream := make(chan T) + + go func() { + defer close(takeStream) + + for i := 0; i < number; i++ { + select { + case <-ctx.Done(): + return + case takeStream <- <-valueStream: + } + } + }() + + return takeStream +} + +// FanIn merge multiple channels into one channel. +// Play: https://go.dev/play/p/2VYFMexEvTm +func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T { + out := make(chan T) + + go func() { + var wg sync.WaitGroup + wg.Add(len(channels)) + + for _, c := range channels { + go func(c <-chan T) { + defer wg.Done() + for v := range c { + select { + case <-ctx.Done(): + return + case out <- v: + } + } + }(c) + } + wg.Wait() + close(out) + }() + + return out +} + +// Tee split one chanel into two channels, until cancel the context. +// Play: https://go.dev/play/p/3TQPKnCirrP +func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) { + out1 := make(chan T) + out2 := make(chan T) + + go func() { + defer close(out1) + defer close(out2) + + for val := range c.OrDone(ctx, in) { + var out1, out2 = out1, out2 + for i := 0; i < 2; i++ { + select { + case <-ctx.Done(): + case out1 <- val: + out1 = nil + case out2 <- val: + out2 = nil + } + } + } + }() + + return out1, out2 +} + +// Bridge link multiply channels into one channel. +// 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 { + case maybeStream, ok := <-chanStream: + if !ok { + return + } + stream = maybeStream + wg.Add(1) + case <-ctx.Done(): + return + } + + go func() { + defer wg.Done() + for val := range c.OrDone(ctx, stream) { + select { + case valStream <- val: + case <-ctx.Done(): + } + } + }() + } + }() + return valStream +} + +// Or read one or more channels into one channel, will close when any readin channel is closed. +// Play: https://go.dev/play/p/Wqz9rwioPww +func (c *Channel[T]) Or(channels ...<-chan T) <-chan T { + switch len(channels) { + case 0: + return nil + case 1: + return channels[0] + } + + orDone := make(chan T) + + go func() { + defer close(orDone) + + switch len(channels) { + case 2: + select { + case <-channels[0]: + case <-channels[1]: + } + default: + select { + case <-channels[0]: + case <-channels[1]: + case <-channels[2]: + case <-c.Or(append(channels[3:], orDone)...): + } + } + }() + + return orDone +} + +// OrDone read a channel into another channel, will close until cancel context. +// Play: https://go.dev/play/p/lm_GoS6aDjo +func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T { + valStream := make(chan T) + + go func() { + defer close(valStream) + for { + select { + case <-ctx.Done(): + return + case v, ok := <-channel: + if !ok { + return + } + select { + case valStream <- v: + case <-ctx.Done(): + } + } + } + + }() + + return valStream +} diff --git a/concurrency/channel_example_test.go b/concurrency/channel_example_test.go new file mode 100644 index 00000000..2f0d2f56 --- /dev/null +++ b/concurrency/channel_example_test.go @@ -0,0 +1,358 @@ +package concurrency + +import ( + "context" + "fmt" + "log" + "time" +) + +func ExampleChannel_Generate() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Generate(ctx, 1, 2, 3) + + fmt.Println(<-intStream) + fmt.Println(<-intStream) + fmt.Println(<-intStream) + + // Output: + // 1 + // 2 + // 3 +} + +func ExampleChannel_Repeat() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4) + + for v := range intStream { + fmt.Println(v) + } + // Output: + // 1 + // 2 + // 1 + // 2 +} + +func ExampleChannel_RepeatFn() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + fn := func() string { + return "hello" + } + + c := NewChannel[string]() + intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3) + + for v := range intStream { + fmt.Println(v) + } + // Output: + // hello + // hello + // hello +} + +func ExampleChannel_Take() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + numbers := make(chan int, 5) + numbers <- 1 + numbers <- 2 + numbers <- 3 + numbers <- 4 + numbers <- 5 + defer close(numbers) + + c := NewChannel[int]() + intStream := c.Take(ctx, numbers, 3) + + for v := range intStream { + fmt.Println(v) + } + // Output: + // 1 + // 2 + // 3 +} + +func ExampleChannel_FanIn() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + channels := make([]<-chan int, 2) + + for i := 0; i < 2; i++ { + channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2) + } + + chs := c.FanIn(ctx, channels...) + + for v := range chs { + fmt.Println(v) //1 1 0 0 or 0 0 1 1 + } + +} + +func ExampleChannel_Or() { + sig := func(after time.Duration) <-chan any { + c := make(chan any) + go func() { + defer close(c) + time.Sleep(after) + }() + return c + } + + start := time.Now() + + c := NewChannel[any]() + <-c.Or( + sig(1*time.Second), + sig(2*time.Second), + sig(3*time.Second), + ) + + if time.Since(start).Seconds() < 2 { + fmt.Println("ok") + } + // Output: + // ok +} + +func ExampleChannel_OrDone() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) + + for v := range c.OrDone(ctx, intStream) { + fmt.Println(v) + } + // Output: + // 1 + // 1 + // 1 +} + +func ExampleChannel_Tee() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 2) + + ch1, ch2 := c.Tee(ctx, intStream) + + for v := range ch1 { + fmt.Println(v) + fmt.Println(<-ch2) + } + // Output: + // 1 + // 1 + // 1 + // 1 +} + +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)) + go func() { + defer close(out) + for i := 1; i <= 5; i++ { + stream := make(chan int, 1) + stream <- i + m1[i]++ + close(stream) + out <- stream + } + }() + return out + } + + for v := range c.Bridge(ctx, genVals()) { + m2[v]++ + } + for k, v := range m1 { + fmt.Println(m2[k] == v) + } + // Output: + // 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 +} diff --git a/concurrency/channel_test.go b/concurrency/channel_test.go new file mode 100644 index 00000000..4c9da23b --- /dev/null +++ b/concurrency/channel_test.go @@ -0,0 +1,200 @@ +package concurrency + +import ( + "context" + "testing" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestGenerate(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestGenerate") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Generate(ctx, 1, 2, 3) + + assert.Equal(1, <-intStream) + assert.Equal(2, <-intStream) + assert.Equal(3, <-intStream) +} + +func TestRepeat(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRepeat") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5) + + assert.Equal(1, <-intStream) + assert.Equal(2, <-intStream) + assert.Equal(1, <-intStream) + assert.Equal(2, <-intStream) + assert.Equal(1, <-intStream) +} + +func TestRepeatFn(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRepeatFn") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + fn := func() string { + s := "a" + return s + } + c := NewChannel[string]() + dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3) + + assert.Equal("a", <-dataStream) + assert.Equal("a", <-dataStream) + assert.Equal("a", <-dataStream) +} + +func TestTake(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestTake") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + numbers := make(chan int, 5) + numbers <- 1 + numbers <- 2 + numbers <- 3 + numbers <- 4 + numbers <- 5 + defer close(numbers) + + c := NewChannel[int]() + intStream := c.Take(ctx, numbers, 3) + + assert.Equal(1, <-intStream) + assert.Equal(2, <-intStream) + assert.Equal(3, <-intStream) +} + +func TestFanIn(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestFanIn") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + channels := make([]<-chan int, 3) + + for i := 0; i < 3; i++ { + channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3) + } + + mergedChannel := c.FanIn(ctx, channels...) + + for val := range mergedChannel { + t.Logf("\t%d\n", val) + } + + assert.Equal(1, 1) +} + +func TestOr(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestOr") + + sig := func(after time.Duration) <-chan any { + c := make(chan interface{}) + go func() { + defer close(c) + time.Sleep(after) + }() + return c + } + + start := time.Now() + + c := NewChannel[any]() + <-c.Or( + sig(1*time.Second), + sig(2*time.Second), + sig(3*time.Second), + sig(4*time.Second), + sig(5*time.Second), + ) + + assert.Equal(true, time.Since(start).Seconds() < 2) +} + +func TestOrDone(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestOrDone") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) + + for val := range c.OrDone(ctx, intStream) { + assert.Equal(1, val) + } +} + +func TestTee(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestTee") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := NewChannel[int]() + inStream := c.Take(ctx, c.Repeat(ctx, 1), 4) + + out1, out2 := c.Tee(ctx, inStream) + for val := range out1 { + val1 := val + val2 := <-out2 + assert.Equal(1, val1) + assert.Equal(1, val2) + } +} + +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() + + c := NewChannel[int]() + genVals := func() <-chan <-chan int { + chanStream := make(chan (<-chan int)) + go func() { + defer close(chanStream) + for i := 0; i < 10; i++ { + stream := make(chan int, 1) + stream <- i + m1[i]++ + close(stream) + chanStream <- stream + } + }() + return chanStream + } + + for val := range c.Bridge(ctx, genVals()) { + m2[val]++ + } + + for k, v := range m1 { + assert.Equal(m2[k], v) + } +} diff --git a/concurrency/keyed_locker.go b/concurrency/keyed_locker.go new file mode 100644 index 00000000..c222d706 --- /dev/null +++ b/concurrency/keyed_locker.go @@ -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) +} diff --git a/concurrency/keyed_locker_test.go b/concurrency/keyed_locker_test.go new file mode 100644 index 00000000..5c077529 --- /dev/null +++ b/concurrency/keyed_locker_test.go @@ -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)) +} diff --git a/condition/condition.go b/condition/condition.go new file mode 100644 index 00000000..82db1129 --- /dev/null +++ b/condition/condition.go @@ -0,0 +1,88 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package condition contains some functions for conditional judgment. eg. And, Or, TernaryOperator ... +// The implementation of this package refers to the implementation of carlmjohnson's truthy package, you may find more +// useful information in truthy(https://github.com/carlmjohnson/truthy), thanks carlmjohnson. +package condition + +import "reflect" + +// Bool returns the truthy value of anything. +// If the value's type has a Bool() bool method, the method is called and returned. +// If the type has an IsZero() bool method, the opposite value is returned. +// Slices and maps are truthy if they have a length greater than zero. +// All other types are truthy if they are not their zero value. +// Play: https://go.dev/play/p/ETzeDJRSvhm +func Bool[T any](value T) bool { + switch m := any(value).(type) { + case interface{ Bool() bool }: + return m.Bool() + case interface{ IsZero() bool }: + return !m.IsZero() + } + return reflectValue(&value) +} + +func reflectValue(vp any) bool { + switch rv := reflect.ValueOf(vp).Elem(); rv.Kind() { + case reflect.Map, reflect.Slice: + return rv.Len() != 0 + default: + is := rv.IsZero() + return !is + } +} + +// And returns true if both a and b are truthy. +// Play: https://go.dev/play/p/W1SSUmt6pvr +func And[T, U any](a T, b U) bool { + return Bool(a) && Bool(b) +} + +// Or returns false if neither a nor b is truthy. +// Play: https://go.dev/play/p/UlQTxHaeEkq +func Or[T, U any](a T, b U) bool { + return Bool(a) || Bool(b) +} + +// Xor returns true if a or b but not both is truthy. +// Play: https://go.dev/play/p/gObZrW7ZbG8 +func Xor[T, U any](a T, b U) bool { + return Bool(a) != Bool(b) +} + +// Nor returns true if neither a nor b is truthy. +// Play: https://go.dev/play/p/g2j08F_zZky +func Nor[T, U any](a T, b U) bool { + return !(Bool(a) || Bool(b)) +} + +// Xnor returns true if both a and b or neither a nor b are truthy. +// Play: https://go.dev/play/p/OuDB9g51643 +func Xnor[T, U any](a T, b U) bool { + return Bool(a) == Bool(b) +} + +// Nand returns false if both a and b are truthy. +// Play: https://go.dev/play/p/vSRMLxLIbq8 +func Nand[T, U any](a T, b U) bool { + return !Bool(a) || !Bool(b) +} + +// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue. +// Play: https://go.dev/play/p/ElllPZY0guT +func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U { + if Bool(isTrue) { + return ifValue + } else { + return elseValue + } +} + +// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue. +// Play: https://go.dev/play/p/ElllPZY0guT +// Deprecated: Use Ternary instead. +func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U { + return Ternary(isTrue, ifValue, elseValue) +} diff --git a/condition/condition_example_test.go b/condition/condition_example_test.go new file mode 100644 index 00000000..80f3042f --- /dev/null +++ b/condition/condition_example_test.go @@ -0,0 +1,163 @@ +package condition + +import "fmt" + +func ExampleBool() { + // bool + result1 := Bool(false) + result2 := Bool(true) + fmt.Println(result1) + fmt.Println(result2) + + // integer + result3 := Bool(0) + result4 := Bool(1) + fmt.Println(result3) + fmt.Println(result4) + + // string + result5 := Bool("") + result6 := Bool(" ") + fmt.Println(result5) + fmt.Println(result6) + + // slice + var nums = []int{} + result7 := Bool(nums) + nums = append(nums, 1, 2) + result8 := Bool(nums) + fmt.Println(result7) + fmt.Println(result8) + + // Output: + // false + // true + // false + // true + // false + // true + // false + // true +} + +func ExampleAnd() { + result1 := And(0, 1) + result2 := And(0, "") + result3 := And(0, "0") + result4 := And(1, "0") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} + +func ExampleOr() { + result1 := Or(0, "") + result2 := Or(0, 1) + result3 := Or(0, "0") + result4 := Or(1, "0") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // true + // true + // true +} + +func ExampleXor() { + result1 := Xor(0, 0) + result2 := Xor(1, 1) + result3 := Xor(0, 1) + result4 := Xor(1, 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // true + // true +} + +func ExampleNor() { + result1 := Nor(0, 0) + result2 := Nor(1, 1) + result3 := Nor(0, 1) + result4 := Nor(1, 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // false + // false + // false +} + +func ExampleXnor() { + result1 := Xnor(0, 0) + result2 := Xnor(1, 1) + result3 := Xnor(0, 1) + result4 := Xnor(1, 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleNand() { + result1 := Nand(0, 0) + result2 := Nand(1, 0) + result3 := Nand(0, 1) + result4 := Nand(1, 1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} + +func ExampleTernary() { + conditionTrue := 2 > 1 + result1 := Ternary(conditionTrue, 0, 1) + fmt.Println(result1) + + conditionFalse := 2 > 3 + result2 := Ternary(conditionFalse, 0, 1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} diff --git a/condition/condition_test.go b/condition/condition_test.go new file mode 100644 index 00000000..45103aba --- /dev/null +++ b/condition/condition_test.go @@ -0,0 +1,136 @@ +package condition + +import ( + "errors" + "testing" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +type TestStruct struct{} + +func TestBool(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBool") + + // bool + assert.Equal(false, Bool(false)) + assert.Equal(true, Bool(true)) + + // integer + assert.Equal(false, Bool(0)) + assert.Equal(true, Bool(1)) + + // float + assert.Equal(false, Bool(0.0)) + assert.Equal(true, Bool(0.1)) + + // string + assert.Equal(false, Bool("")) + assert.Equal(true, Bool(" ")) + assert.Equal(true, Bool("0")) + + // slice + var nums [2]int + assert.Equal(false, Bool(nums)) + nums = [2]int{0, 1} + assert.Equal(true, Bool(nums)) + + // map + assert.Equal(false, Bool(map[string]string{})) + assert.Equal(true, Bool(map[string]string{"a": "a"})) + + // channel + var ch chan int + assert.Equal(false, Bool(ch)) + ch = make(chan int) + assert.Equal(true, Bool(ch)) + + // interface + var err error + assert.Equal(false, Bool(err)) + err = errors.New("error message") + assert.Equal(true, Bool(err)) + + // struct + assert.Equal(false, Bool(struct{}{})) + assert.Equal(true, Bool(time.Now())) + + // struct pointer + ts := TestStruct{} + assert.Equal(false, Bool(ts)) + assert.Equal(true, Bool(&ts)) +} + +func TestAnd(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAnd") + assert.Equal(false, And(0, 1)) + assert.Equal(false, And(0, "")) + assert.Equal(false, And(0, "0")) + assert.Equal(true, And(1, "0")) +} + +func TestOr(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOr") + assert.Equal(false, Or(0, "")) + assert.Equal(true, Or(0, 1)) + assert.Equal(true, Or(0, "0")) + assert.Equal(true, Or(1, "0")) +} + +func TestXor(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOr") + assert.Equal(false, Xor(0, 0)) + assert.Equal(true, Xor(0, 1)) + assert.Equal(true, Xor(1, 0)) + assert.Equal(false, Xor(1, 1)) +} + +func TestNor(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestNor") + assert.Equal(true, Nor(0, 0)) + assert.Equal(false, Nor(0, 1)) + assert.Equal(false, Nor(1, 0)) + assert.Equal(false, Nor(1, 1)) +} + +func TestXnor(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestXnor") + assert.Equal(true, Xnor(0, 0)) + assert.Equal(false, Xnor(0, 1)) + assert.Equal(false, Xnor(1, 0)) + assert.Equal(true, Xnor(1, 1)) +} + +func TestNand(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestNand") + assert.Equal(true, Nand(0, 0)) + assert.Equal(true, Nand(0, 1)) + assert.Equal(true, Nand(1, 0)) + assert.Equal(false, Nand(1, 1)) +} + +func TestTernary(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTernary") + + trueValue := "1" + falseValue := "0" + + assert.Equal(trueValue, Ternary(true, trueValue, falseValue)) +} diff --git a/constraints/constraints.go b/constraints/constraints.go new file mode 100644 index 00000000..fc8aa4a0 --- /dev/null +++ b/constraints/constraints.go @@ -0,0 +1,13 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package constraints contain some custom interface. +package constraints + +// Comparator is for comparing two values +type Comparator interface { + // Compare v1 and v2 + // Ascending order: should return 1 -> v1 > v2, 0 -> v1 = v2, -1 -> v1 < v2 + // Descending order: should return 1 -> v1 < v2, 0 -> v1 = v2, -1 -> v1 > v2 + Compare(v1, v2 any) int +} diff --git a/convertor/convertor.go b/convertor/convertor.go index 9d03bbe7..e8d46907 100644 --- a/convertor/convertor.go +++ b/convertor/convertor.go @@ -6,34 +6,76 @@ package convertor import ( "bytes" + "encoding/base64" + "encoding/binary" "encoding/gob" "encoding/json" + "errors" "fmt" + "io" + "math" + "math/big" "reflect" - "regexp" "strconv" "strings" + + "github.com/duke-git/lancet/v2/structs" + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/transform" ) -// ToBool convert string to a boolean +// ToBool convert string to boolean. +// Play: https://go.dev/play/p/ARht2WnGdIN func ToBool(s string) (bool, error) { return strconv.ParseBool(s) } -// ToBytes convert interface to bytes -func ToBytes(data interface{}) ([]byte, error) { - var buf bytes.Buffer - enc := gob.NewEncoder(&buf) - err := enc.Encode(data) - if err != nil { - return nil, err +// ToBytes convert value to byte slice. +// Play: https://go.dev/play/p/fAMXYFDvOvr +func ToBytes(value any) ([]byte, error) { + v := reflect.ValueOf(value) + + switch value.(type) { + case int, int8, int16, int32, int64: + number := v.Int() + buf := bytes.NewBuffer([]byte{}) + buf.Reset() + err := binary.Write(buf, binary.BigEndian, number) + return buf.Bytes(), err + case uint, uint8, uint16, uint32, uint64: + number := v.Uint() + buf := bytes.NewBuffer([]byte{}) + buf.Reset() + err := binary.Write(buf, binary.BigEndian, number) + return buf.Bytes(), err + case float32: + number := float32(v.Float()) + bits := math.Float32bits(number) + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, bits) + return bytes, nil + case float64: + number := v.Float() + bits := math.Float64bits(number) + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, bits) + return bytes, nil + case bool: + return strconv.AppendBool([]byte{}, v.Bool()), nil + case string: + return []byte(v.String()), nil + case []byte: + return v.Bytes(), nil + default: + newValue, err := json.Marshal(value) + return newValue, err } - return buf.Bytes(), nil } -// ToChar convert string to char slice +// ToChar convert string to char slice. +// Play: https://go.dev/play/p/JJ1SvbFkVdM func ToChar(s string) []string { - c := make([]string, 0) + c := make([]string, 0, len(s)) if len(s) == 0 { c = append(c, "") } @@ -43,133 +85,182 @@ func ToChar(s string) []string { return c } +// ToChannel convert a slice of elements to a read-only channel. +// Play: https://go.dev/play/p/hOx_oYZbAnL +func ToChannel[T any](array []T) <-chan T { + ch := make(chan T) + + go func() { + for _, item := range array { + ch <- item + } + close(ch) + }() + + return ch +} + // ToString convert value to string -func ToString(value interface{}) string { - res := "" +// for number, string, []byte, will convert to string +// for other type (slice, map, array, struct) will call json.Marshal. +// Play: https://go.dev/play/p/nF1zOOslpQq +func ToString(value any) string { if value == nil { - return res + return "" + } + rv := reflect.ValueOf(value) + if rv.Kind() == reflect.Ptr { + if rv.IsNil() { + return "" + } + return ToString(rv.Elem().Interface()) } - v := reflect.ValueOf(value) - - switch value.(type) { - case float32, float64: - res = strconv.FormatFloat(v.Float(), 'f', -1, 64) - return res - case int, int8, int16, int32, int64: - res = strconv.FormatInt(v.Int(), 10) - return res - case uint, uint8, uint16, uint32, uint64: - res = strconv.FormatUint(v.Uint(), 10) - return res + switch val := value.(type) { + case float32: + return strconv.FormatFloat(float64(val), 'f', -1, 32) + case float64: + return strconv.FormatFloat(val, 'f', -1, 64) + case int: + return strconv.FormatInt(int64(val), 10) + case int8: + return strconv.FormatInt(int64(val), 10) + case int16: + return strconv.FormatInt(int64(val), 10) + case int32: + return strconv.FormatInt(int64(val), 10) + case int64: + return strconv.FormatInt(val, 10) + case uint: + return strconv.FormatUint(uint64(val), 10) + case uint8: + return strconv.FormatUint(uint64(val), 10) + case uint16: + return strconv.FormatUint(uint64(val), 10) + case uint32: + return strconv.FormatUint(uint64(val), 10) + case uint64: + return strconv.FormatUint(val, 10) case string: - res = v.String() - return res + return val case []byte: - res = string(v.Bytes()) - return res + return string(val) default: - newValue, _ := json.Marshal(value) - res = string(newValue) - return res + b, err := json.Marshal(val) + if err != nil { + return "" + } + + return string(b) } } -// ToJson convert value to a valid json string -func ToJson(value interface{}) (string, error) { - res, err := json.Marshal(value) +// ToJson convert value to a json string. +// Play: https://go.dev/play/p/2rLIkMmXWvR +func ToJson(value any) (string, error) { + result, err := json.Marshal(value) if err != nil { return "", err } - return string(res), nil + return string(result), nil } -// ToFloat convert value to a float64, if input is not a float return 0.0 and error -func ToFloat(value interface{}) (float64, error) { +// ToFloat convert value to float64, if input is not a float return 0.0 and error. +// Play: https://go.dev/play/p/4YTmPCibqHJ +func ToFloat(value any) (float64, error) { v := reflect.ValueOf(value) - res := 0.0 + result := 0.0 err := fmt.Errorf("ToInt: unvalid interface type %T", value) switch value.(type) { case int, int8, int16, int32, int64: - res = float64(v.Int()) - return res, nil + result = float64(v.Int()) + return result, nil case uint, uint8, uint16, uint32, uint64: - res = float64(v.Uint()) - return res, nil + result = float64(v.Uint()) + return result, nil case float32, float64: - res = v.Float() - return res, nil + result = v.Float() + return result, nil case string: - res, err = strconv.ParseFloat(v.String(), 64) + result, err = strconv.ParseFloat(v.String(), 64) if err != nil { - res = 0.0 + result = 0.0 } - return res, err + return result, err default: - return res, err + return result, err } } -// ToInt convert value to a int64, if input is not a numeric format return 0 and error -func ToInt(value interface{}) (int64, error) { +// ToInt convert value to int64 value, if input is not numerical, return 0 and error. +// Play: https://go.dev/play/p/9_h9vIt-QZ_b +func ToInt(value any) (int64, error) { v := reflect.ValueOf(value) - var res int64 - err := fmt.Errorf("ToInt: invalid interface type %T", value) + var result int64 + err := fmt.Errorf("ToInt: invalid value type %T", value) switch value.(type) { case int, int8, int16, int32, int64: - res = v.Int() - return res, nil + result = v.Int() + return result, nil case uint, uint8, uint16, uint32, uint64: - res = int64(v.Uint()) - return res, nil + result = int64(v.Uint()) + return result, nil case float32, float64: - res = int64(v.Float()) - return res, nil + result = int64(v.Float()) + return result, nil case string: - res, err = strconv.ParseInt(v.String(), 0, 64) + result, err = strconv.ParseInt(v.String(), 0, 64) if err != nil { - res = 0 + result = 0 } - return res, err + return result, err default: - return res, err + return result, err } } -// StructToMap convert struct to map, only convert exported struct field -// map key is specified same as struct field tag `json` value -func StructToMap(value interface{}) (map[string]interface{}, error) { - v := reflect.ValueOf(value) - t := reflect.TypeOf(value) +// ToPointer returns a pointer to passed value. +// Play: https://go.dev/play/p/ASf_etHNlw1 +func ToPointer[T any](value T) *T { + return &value +} - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - if t.Kind() != reflect.Struct { - return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value) +// 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 { + result := make(map[K]V, len(array)) + for _, item := range array { + k, v := iteratee(item) + result[k] = v } - res := make(map[string]interface{}) + return result +} - fieldNum := t.NumField() - pattern := `^[A-Z]` - regex := regexp.MustCompile(pattern) - for i := 0; i < fieldNum; i++ { - name := t.Field(i).Name - tag := t.Field(i).Tag.Get("json") - if regex.MatchString(name) && tag != "" { - //res[name] = v.Field(i).Interface() - res[tag] = v.Field(i).Interface() - } +// StructToMap convert struct to map, only convert exported struct field +// map key is specified same as struct field tag `json` value. +// Play: https://go.dev/play/p/KYGYJqNUBOI +func StructToMap(value any) (map[string]any, error) { + return structs.ToMap(value) +} + +// MapToSlice convert map to slice based on iteratee function. +// Play: https://go.dev/play/p/dmX4Ix5V6Wl +func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T { + result := make([]T, 0, len(aMap)) + + for k, v := range aMap { + result = append(result, iteratee(k, v)) } - return res, nil + return result } -// ColorHexToRGB convert hex color to rgb color +// ColorHexToRGB convert hex color to rgb color. +// Play: https://go.dev/play/p/o7_ft-JCJBV func ColorHexToRGB(colorHex string) (red, green, blue int) { colorHex = strings.TrimPrefix(colorHex, "#") color64, err := strconv.ParseInt(colorHex, 16, 32) @@ -180,7 +271,8 @@ func ColorHexToRGB(colorHex string) (red, green, blue int) { return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF } -// ColorRGBToHex convert rgb color to hex color +// ColorRGBToHex convert rgb color to hex color. +// Play: https://go.dev/play/p/nzKS2Ro87J1 func ColorRGBToHex(red, green, blue int) string { r := strconv.FormatInt(int64(red), 16) g := strconv.FormatInt(int64(green), 16) @@ -198,3 +290,233 @@ func ColorRGBToHex(red, green, blue int) string { return "#" + r + g + b } + +// EncodeByte encode data to byte slice. +// Play: https://go.dev/play/p/DVmM1G5JfuP +func EncodeByte(data any) ([]byte, error) { + buffer := bytes.NewBuffer(nil) + encoder := gob.NewEncoder(buffer) + err := encoder.Encode(data) + if err != nil { + return nil, err + } + return buffer.Bytes(), nil +} + +// DecodeByte decode byte slice data to target object. +// Play: https://go.dev/play/p/zI6xsmuQRbn +func DecodeByte(data []byte, target any) error { + buffer := bytes.NewBuffer(data) + decoder := gob.NewDecoder(buffer) + return decoder.Decode(target) +} + +// DeepClone creates a deep copy of passed item. +// can't clone unexported field of struct +// Play: https://go.dev/play/p/j4DP5dquxnk +func DeepClone[T any](src T) T { + c := cloner{ + ptrs: map[reflect.Type]map[uintptr]reflect.Value{}, + } + result := c.clone(reflect.ValueOf(src)) + if result.Kind() == reflect.Invalid { + var zeroValue T + return zeroValue + } + + return result.Interface().(T) +} + +// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. +// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct. +// Play: https://go.dev/play/p/oZujoB5Sgg5 +func CopyProperties[T, U any](dst T, src U) error { + dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src) + + if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct { + return errors.New("CopyProperties: parameter dst should be struct pointer") + } + + if srcType.Kind() == reflect.Ptr { + srcType = srcType.Elem() + } + if srcType.Kind() != reflect.Struct { + return errors.New("CopyProperties: parameter src should be a struct or struct pointer") + } + + bytes, err := json.Marshal(src) + if err != nil { + return fmt.Errorf("CopyProperties: unable to marshal src: %s", err) + } + err = json.Unmarshal(bytes, dst) + if err != nil { + return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err) + } + + return nil +} + +// ToInterface converts reflect value to its interface type. +// Play: https://go.dev/play/p/syqw0-WG7Xd +func ToInterface(v reflect.Value) (value interface{}, ok bool) { + if v.IsValid() && v.CanInterface() { + return v.Interface(), true + } + switch v.Kind() { + case reflect.Bool: + return v.Bool(), true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint(), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Complex64, reflect.Complex128: + return v.Complex(), true + case reflect.String: + return v.String(), true + case reflect.Ptr: + return ToInterface(v.Elem()) + case reflect.Interface: + return ToInterface(v.Elem()) + default: + return nil, false + } +} + +// Utf8ToGbk convert utf8 encoding data to GBK encoding data. +// Play: https://go.dev/play/p/9FlIaFLArIL +func Utf8ToGbk(bs []byte) ([]byte, error) { + r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewEncoder()) + b, err := io.ReadAll(r) + return b, err +} + +// GbkToUtf8 convert GBK encoding data to utf8 encoding data. +// Play: https://go.dev/play/p/OphmHCN_9u8 +func GbkToUtf8(bs []byte) ([]byte, error) { + r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder()) + b, err := io.ReadAll(r) + return b, err +} + +// ToStdBase64 convert data to standard base64 encoding. +// Play: https://go.dev/play/p/_fLJqJD3NMo +func ToStdBase64(value any) string { + if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { + return "" + } + switch value.(type) { + case []byte: + return base64.StdEncoding.EncodeToString(value.([]byte)) + case string: + return base64.StdEncoding.EncodeToString([]byte(value.(string))) + case error: + return base64.StdEncoding.EncodeToString([]byte(value.(error).Error())) + default: + marshal, err := json.Marshal(value) + if err != nil { + return "" + } + return base64.StdEncoding.EncodeToString(marshal) + } +} + +// ToUrlBase64 convert data to URL base64 encoding. +// Play: https://go.dev/play/p/C_d0GlvEeUR +func ToUrlBase64(value any) string { + if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { + return "" + } + switch value.(type) { + case []byte: + return base64.URLEncoding.EncodeToString(value.([]byte)) + case string: + return base64.URLEncoding.EncodeToString([]byte(value.(string))) + case error: + return base64.URLEncoding.EncodeToString([]byte(value.(error).Error())) + default: + marshal, err := json.Marshal(value) + if err != nil { + return "" + } + return base64.URLEncoding.EncodeToString(marshal) + } +} + +// ToRawStdBase64 convert data to raw standard base64 encoding. +// Play: https://go.dev/play/p/wSAr3sfkDcv +func ToRawStdBase64(value any) string { + if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { + return "" + } + switch value.(type) { + case []byte: + return base64.RawStdEncoding.EncodeToString(value.([]byte)) + case string: + return base64.RawStdEncoding.EncodeToString([]byte(value.(string))) + case error: + return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error())) + default: + marshal, err := json.Marshal(value) + if err != nil { + return "" + } + return base64.RawStdEncoding.EncodeToString(marshal) + } +} + +// ToRawUrlBase64 convert data to raw URL base64 encoding. +// Play: https://go.dev/play/p/HwdDPFcza1O +func ToRawUrlBase64(value any) string { + if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { + return "" + } + switch value.(type) { + case []byte: + return base64.RawURLEncoding.EncodeToString(value.([]byte)) + case string: + return base64.RawURLEncoding.EncodeToString([]byte(value.(string))) + case error: + return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error())) + default: + marshal, err := json.Marshal(value) + 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 +} diff --git a/convertor/convertor_example_test.go b/convertor/convertor_example_test.go new file mode 100644 index 00000000..53660d24 --- /dev/null +++ b/convertor/convertor_example_test.go @@ -0,0 +1,582 @@ +package convertor + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "unicode/utf8" + + "github.com/duke-git/lancet/v2/validator" +) + +func ExampleToBool() { + cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"} + + for i := 0; i < len(cases); i++ { + result, _ := ToBool(cases[i]) + fmt.Println(result) + } + + // Output: + // true + // true + // true + // false + // false + // false + // false + // false + // false +} + +func ExampleToBytes() { + result1, _ := ToBytes(1) + result2, _ := ToBytes("abc") + result3, _ := ToBytes(true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [0 0 0 0 0 0 0 1] + // [97 98 99] + // [116 114 117 101] +} + +func ExampleToChar() { + result1 := ToChar("") + result2 := ToChar("abc") + result3 := ToChar("1 2#3") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [] + // [a b c] + // [1 2 # 3] +} + +func ExampleToChannel() { + ch := ToChannel([]int{1, 2, 3}) + result1 := <-ch + result2 := <-ch + result3 := <-ch + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 3 +} + +func ExampleToString() { + result1 := ToString("") + result2 := ToString(nil) + result3 := ToString(0) + result4 := ToString(1.23) + result5 := ToString(true) + result6 := ToString(false) + result7 := ToString([]int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // + // + // 0 + // 1.23 + // true + // false + // [1,2,3] +} + +func ExampleToJson() { + + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result1, err := ToJson(aMap) + if err != nil { + fmt.Printf("%v", err) + } + fmt.Println(result1) + + // Output: + // {"a":1,"b":2,"c":3} +} + +func ExampleToFloat() { + result1, _ := ToFloat("") + result2, _ := ToFloat("abc") + result3, _ := ToFloat("-1") + result4, _ := ToFloat("-.11") + result5, _ := ToFloat("1.23e3") + result6, _ := ToFloat(true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // 0 + // 0 + // -1 + // -0.11 + // 1230 + // 0 +} + +func ExampleToInt() { + result1, _ := ToInt("123") + result2, _ := ToInt("-123") + result3, _ := ToInt(float64(12.3)) + result4, _ := ToInt("abc") + result5, _ := ToInt(true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 123 + // -123 + // 12 + // 0 + // 0 +} + +func ExampleToPointer() { + result := ToPointer(123) + fmt.Println(*result) + + // Output: + // 123 +} + +func ExampleToMap() { + type Message struct { + name string + code int + } + messages := []Message{ + {name: "Hello", code: 100}, + {name: "Hi", code: 101}, + } + result := ToMap(messages, func(msg Message) (int, string) { + return msg.code, msg.name + }) + + fmt.Println(result) + + // Output: + // map[100:Hello 101:Hi] +} + +func ExampleStructToMap() { + type People struct { + Name string `json:"name"` + age int + } + p := People{ + "test", + 100, + } + pm, _ := StructToMap(p) + + fmt.Println(pm) + + // Output: + // map[name:test] +} + +func ExampleMapToSlice() { + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result := MapToSlice(aMap, func(key string, value int) string { + return key + ":" + strconv.Itoa(value) + }) + + fmt.Println(result) //[]string{"a:1", "c:3", "b:2"} (random order) +} + +func ExampleColorHexToRGB() { + colorHex := "#003366" + r, g, b := ColorHexToRGB(colorHex) + + fmt.Println(r, g, b) + + // Output: + // 0 51 102 +} + +func ExampleColorRGBToHex() { + r := 0 + g := 51 + b := 102 + colorHex := ColorRGBToHex(r, g, b) + + fmt.Println(colorHex) + + // Output: + // #003366 +} + +func ExampleEncodeByte() { + byteData, _ := EncodeByte("abc") + fmt.Println(byteData) + + // Output: + // [6 12 0 3 97 98 99] +} + +func ExampleDecodeByte() { + var obj string + byteData := []byte{6, 12, 0, 3, 97, 98, 99} + err := DecodeByte(byteData, &obj) + if err != nil { + return + } + + fmt.Println(obj) + + // Output: + // abc +} + +func ExampleDeepClone() { + type Struct struct { + Str string + Int int + Float float64 + Bool bool + Nil interface{} + // unexported string + } + + cases := []interface{}{ + true, + 1, + 0.1, + map[string]int{ + "a": 1, + "b": 2, + }, + &Struct{ + Str: "test", + Int: 1, + Float: 0.1, + Bool: true, + Nil: nil, + // unexported: "can't be cloned", + }, + } + + for _, item := range cases { + cloned := DeepClone(item) + + isPointerEqual := &cloned == &item + fmt.Println(cloned, isPointerEqual) + } + + // Output: + // true false + // 1 false + // 0.1 false + // map[a:1 b:2] false + // &{test 1 0.1 true } false +} + +func ExampleCopyProperties() { + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + CopyProperties(&indicatorVO, indicator) + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) + + // Output: + // 001 + // 127.0.0.1 + // 3 +} + +func ExampleToInterface() { + val := reflect.ValueOf("abc") + iVal, ok := ToInterface(val) + + fmt.Printf("%T\n", iVal) + fmt.Printf("%v\n", iVal) + fmt.Println(ok) + + // Output: + // string + // abc + // true +} + +func ExampleUtf8ToGbk() { + utf8Data := []byte("hello") + gbkData, _ := Utf8ToGbk(utf8Data) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(validator.IsGBK(gbkData)) + + // Output: + // true + // true +} + +func ExampleGbkToUtf8() { + gbkData, _ := Utf8ToGbk([]byte("hello")) + utf8Data, _ := GbkToUtf8(gbkData) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(string(utf8Data)) + + // Output: + // true + // hello +} + +func ExampleToStdBase64() { + // if you want to see the result, please use 'base64.StdEncoding.DecodeString()' to decode the result + + afterEncode := ToStdBase64(nil) + fmt.Println(afterEncode) + + stringVal := "hello" + afterEncode = ToStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = ToStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = ToStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = ToStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = ToStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = ToStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = ToStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +func ExampleToUrlBase64() { + // if you want to see the result, please use 'base64.URLEncoding.DecodeString()' to decode the result + + stringVal := "hello" + afterEncode := ToUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = ToUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = ToUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = ToUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = ToUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = ToUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = ToUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +func ExampleToRawStdBase64() { + // if you want to see the result, please use 'base64.RawStdEncoding.DecodeString()' to decode the result + stringVal := "hello" + afterEncode := ToRawStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = ToRawStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = ToRawStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = ToRawStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = ToRawStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = ToRawStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = ToRawStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} + +func ExampleToRawUrlBase64() { + // if you want to see the result, please use 'base64.RawURLEncoding.DecodeString()' to decode the result + + stringVal := "hello" + afterEncode := ToRawUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = ToRawUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = ToRawUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = ToRawUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = ToRawUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = ToRawUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = ToRawUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} + +func ExampleToBigInt() { + n := 9876543210 + bigInt, _ := ToBigInt(n) + + fmt.Println(bigInt) + // Output: + // 9876543210 +} diff --git a/convertor/convertor_internal.go b/convertor/convertor_internal.go new file mode 100644 index 00000000..553d5448 --- /dev/null +++ b/convertor/convertor_internal.go @@ -0,0 +1,216 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package convertor implements some functions to convert data. +package convertor + +import "reflect" + +type cloner struct { + ptrs map[reflect.Type]map[uintptr]reflect.Value +} + +// clone return a duplicate of passed item. +func (c *cloner) clone(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Invalid: + return reflect.ValueOf(nil) + + // bool + case reflect.Bool: + return reflect.ValueOf(v.Bool()) + + //int + case reflect.Int: + return reflect.ValueOf(int(v.Int())) + case reflect.Int8: + return reflect.ValueOf(int8(v.Int())) + case reflect.Int16: + return reflect.ValueOf(int16(v.Int())) + case reflect.Int32: + return reflect.ValueOf(int32(v.Int())) + case reflect.Int64: + return reflect.ValueOf(v.Int()) + + // uint + case reflect.Uint: + return reflect.ValueOf(uint(v.Uint())) + case reflect.Uint8: + return reflect.ValueOf(uint8(v.Uint())) + case reflect.Uint16: + return reflect.ValueOf(uint16(v.Uint())) + case reflect.Uint32: + return reflect.ValueOf(uint32(v.Uint())) + case reflect.Uint64: + return reflect.ValueOf(v.Uint()) + + // float + case reflect.Float32: + return reflect.ValueOf(float32(v.Float())) + case reflect.Float64: + return reflect.ValueOf(v.Float()) + + // complex + case reflect.Complex64: + return reflect.ValueOf(complex64(v.Complex())) + case reflect.Complex128: + return reflect.ValueOf(v.Complex()) + + // string + case reflect.String: + return reflect.ValueOf(v.String()) + + // array + case reflect.Array, reflect.Slice: + return c.cloneArray(v) + + // map + case reflect.Map: + return c.cloneMap(v) + + // Ptr + case reflect.Ptr: + return c.clonePtr(v) + + // struct + case reflect.Struct: + return c.cloneStruct(v) + + // func + case reflect.Func: + return v + + // interface + case reflect.Interface: + return c.clone(v.Elem()) + + } + + return reflect.Zero(v.Type()) +} + +func (c *cloner) cloneArray(v reflect.Value) reflect.Value { + if v.IsNil() { + return reflect.Zero(v.Type()) + } + + arr := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) + + for i := 0; i < v.Len(); i++ { + val := c.clone(v.Index(i)) + + if !val.IsValid() { + continue + } + + item := arr.Index(i) + if !item.CanSet() { + continue + } + + item.Set(val.Convert(item.Type())) + } + + return arr +} + +func (c *cloner) cloneMap(v reflect.Value) reflect.Value { + if v.IsNil() { + return reflect.Zero(v.Type()) + } + + clonedMap := reflect.MakeMap(v.Type()) + + for _, key := range v.MapKeys() { + value := v.MapIndex(key) + clonedKey := c.clone(key) + clonedValue := c.clone(value) + + if !isNillable(clonedKey) || !clonedKey.IsNil() { + clonedKey = clonedKey.Convert(key.Type()) + } + + if (!isNillable(clonedValue) || !clonedValue.IsNil()) && clonedValue.IsValid() { + clonedValue = clonedValue.Convert(value.Type()) + } + + if !clonedValue.IsValid() { + clonedValue = reflect.Zero(clonedMap.Type().Elem()) + } + + clonedMap.SetMapIndex(clonedKey, clonedValue) + } + + return clonedMap +} + +func isNillable(v reflect.Value) bool { + switch v.Kind() { + case reflect.Chan, reflect.Interface, reflect.Ptr, reflect.Func: + return true + } + return false +} + +func (c *cloner) clonePtr(v reflect.Value) reflect.Value { + if v.IsNil() { + return reflect.Zero(v.Type()) + } + + var newVal reflect.Value + + if v.Elem().CanAddr() { + ptrs, exists := c.ptrs[v.Type()] + if exists { + if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists { + return newVal + } + } + } + + newVal = c.clone(v.Elem()) + + if v.Elem().CanAddr() { + ptrs, exists := c.ptrs[v.Type()] + if exists { + if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists { + return newVal + } + } + } + + clonedPtr := reflect.New(newVal.Type()) + clonedPtr.Elem().Set(newVal) + + return clonedPtr +} + +func (c *cloner) cloneStruct(v reflect.Value) reflect.Value { + clonedStructPtr := reflect.New(v.Type()) + clonedStruct := clonedStructPtr.Elem() + + if v.CanAddr() { + ptrs := c.ptrs[clonedStructPtr.Type()] + if ptrs == nil { + ptrs = make(map[uintptr]reflect.Value) + c.ptrs[clonedStructPtr.Type()] = ptrs + } + ptrs[v.UnsafeAddr()] = clonedStructPtr + } + + for i := 0; i < v.NumField(); i++ { + newStructValue := clonedStruct.Field(i) + if !newStructValue.CanSet() { + continue + } + + clonedVal := c.clone(v.Field(i)) + if !clonedVal.IsValid() { + continue + } + + newStructValue.Set(clonedVal.Convert(newStructValue.Type())) + } + + return clonedStruct +} diff --git a/convertor/convertor_test.go b/convertor/convertor_test.go index 90b0d6d5..21d97ec4 100644 --- a/convertor/convertor_test.go +++ b/convertor/convertor_test.go @@ -1,13 +1,25 @@ package convertor import ( + "encoding/base64" + "errors" "fmt" + "io" + "math/big" + "reflect" + "strconv" "testing" + "unicode/utf8" + "unsafe" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" + "github.com/duke-git/lancet/v2/slice" + "github.com/duke-git/lancet/v2/validator" ) func TestToChar(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToChar") cases := []string{"", "abc", "1 2#3"} @@ -21,7 +33,23 @@ func TestToChar(t *testing.T) { } } +func TestToChannel(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToChannel") + + ch := ToChannel([]int{1, 2, 3}) + assert.Equal(1, <-ch) + assert.Equal(2, <-ch) + assert.Equal(3, <-ch) + + _, ok := <-ch + assert.Equal(false, ok) +} + func TestToBool(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToBool") cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"} @@ -34,28 +62,39 @@ func TestToBool(t *testing.T) { } func TestToBytes(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToBytes") - cases := []interface{}{ + cases := []any{ 0, false, "1", } expected := [][]byte{ - {3, 4, 0, 0}, - {3, 2, 0, 0}, - {4, 12, 0, 1, 49}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {102, 97, 108, 115, 101}, + {49}, } for i := 0; i < len(cases); i++ { actual, _ := ToBytes(cases[i]) assert.Equal(expected[i], actual) } + + bytesData, err := ToBytes("abc") + if err != nil { + t.Error(err) + t.Fail() + } + assert.Equal("abc", ToString(bytesData)) } func TestToInt(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToInt") - cases := []interface{}{"123", "-123", 123, + cases := []any{"123", "-123", 123, uint(123), uint8(123), uint16(123), uint32(123), uint64(123), float32(12.3), float64(12.3), "abc", false, "111111111111111111111111111111111111111"} @@ -69,9 +108,11 @@ func TestToInt(t *testing.T) { } func TestToFloat(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToFloat") - cases := []interface{}{ + cases := []any{ "", "-1", "-.11", "1.23e3", ".123e10", "abc", int(0), int8(1), int16(-1), int32(123), int64(123), uint(123), uint8(123), uint16(123), uint32(123), uint64(123), @@ -87,6 +128,8 @@ func TestToFloat(t *testing.T) { } func TestToString(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToString") aMap := make(map[string]int) @@ -99,21 +142,35 @@ func TestToString(t *testing.T) { } aStruct := TestStruct{Name: "TestStruct"} - cases := []interface{}{ + 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{ "", "", "0", "1", "-1", "123", "123", "123", "123", "123", "123", "123", - "12.3", "12.300000190734863", + "12.3", "12.3", "true", "false", - "[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"} + "[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello", + "123", "123", "12.3", "abc", "{\"Name\":\"TestStruct\"}", "", "", + "0", + } for i := 0; i < len(cases); i++ { actual := ToString(cases[i]) @@ -121,6 +178,8 @@ func TestToString(t *testing.T) { } } func TestToJson(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToJson") var aMap = map[string]int{"a": 1, "b": 2, "c": 3} @@ -135,23 +194,83 @@ func TestToJson(t *testing.T) { assert.Equal("{\"Name\":\"TestStruct\"}", structJsonStr) } -func TestStructToMap(t *testing.T) { - assert := internal.NewAssert(t, "TestStructToMap") +func TestToMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToMap") - type People struct { - Name string `json:"name"` - age int + type Message struct { + name string + code int } - p := People{ - "test", - 100, + messages := []Message{ + {name: "Hello", code: 100}, + {name: "Hi", code: 101}, } - pm, _ := StructToMap(p) - var expected = map[string]interface{}{"name": "test"} - assert.Equal(expected, pm) + result := ToMap(messages, func(msg Message) (int, string) { + return msg.code, msg.name + }) + expected := map[int]string{100: "Hello", 101: "Hi"} + + assert.Equal(expected, result) +} + +func TestStructToMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStructToMap") + + t.Run("StructToMap", func(_ *testing.T) { + type People struct { + Name string `json:"name"` + age int + } + p := People{ + "test", + 100, + } + pm, _ := StructToMap(p) + var expected = map[string]any{"name": "test"} + assert.Equal(expected, pm) + }) + + t.Run("StructToMapWithJsonAttr", func(_ *testing.T) { + type People struct { + Name string `json:"name,omitempty"` // json tag with attribute + Phone string `json:"phone"` // json tag without attribute + Sex string `json:"-"` // ignore + age int // no tag + } + p := People{ + Phone: "1111", + Sex: "male", + age: 100, + } + pm, _ := StructToMap(p) + var expected = map[string]any{"phone": "1111"} + assert.Equal(expected, pm) + }) +} + +func TestMapToSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapToSlice") + + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result := MapToSlice(aMap, func(key string, value int) string { + return key + ":" + strconv.Itoa(value) + }) + + assert.Equal(3, len(result)) + assert.Equal(true, slice.Contain(result, "a:1")) + assert.Equal(true, slice.Contain(result, "b:2")) + assert.Equal(true, slice.Contain(result, "c:3")) } func TestColorHexToRGB(t *testing.T) { + t.Parallel() + colorHex := "#003366" r, g, b := ColorHexToRGB(colorHex) colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b) @@ -162,6 +281,8 @@ func TestColorHexToRGB(t *testing.T) { } func TestColorRGBToHex(t *testing.T) { + t.Parallel() + r := 0 g := 51 b := 102 @@ -171,3 +292,546 @@ func TestColorRGBToHex(t *testing.T) { assert := internal.NewAssert(t, "TestColorRGBToHex") assert.Equal(expected, colorHex) } + +func TestToPointer(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToPointer") + result := ToPointer(123) + + assert.Equal(*result, 123) +} + +func TestEncodeByte(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEncodeByte") + + byteData, _ := EncodeByte("abc") + expected := []byte{6, 12, 0, 3, 97, 98, 99} + + assert.Equal(expected, byteData) +} + +func TestDecodeByte(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDecodeByte") + + var obj string + byteData := []byte{6, 12, 0, 3, 97, 98, 99} + err := DecodeByte(byteData, &obj) + assert.IsNil(err) + assert.Equal("abc", obj) +} + +func TestDeepClone(t *testing.T) { + t.Parallel() + + // assert := internal.NewAssert(t, "TestDeepClone") + + type Struct struct { + Str string + Int int + Float float64 + Bool bool + Nil interface{} + // unexported string + } + + cases := []interface{}{ + true, + 1, + 0.1, + map[string]int{ + "a": 1, + "b": 2, + }, + &Struct{ + Str: "test", + Int: 1, + Float: 0.1, + Bool: true, + Nil: nil, + // unexported: "can't be cloned", + }, + []interface{}{1, &Struct{Str: "test"}, Struct{Str: "test2"}}, + } + + for i, item := range cases { + cloned := DeepClone(item) + + if &cloned == &item { + t.Fatalf("[TestDeepClone case #%d failed]: equal pointer", i) + } + + if !reflect.DeepEqual(item, cloned) { + t.Fatalf("[TestDeepClone case #%d failed] unequal objects", i) + } + } +} + +func TestCopyProperties(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCopyProperties") + + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + err := CopyProperties(&indicatorVO, indicator) + + assert.IsNil(err) + assert.Equal("001", indicatorVO.Id) + assert.Equal("127.0.0.1", indicatorVO.Ip) + assert.Equal(3, len(indicatorVO.Disk)) +} + +func TestToInterface(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToInterface") + + cases := []reflect.Value{ + reflect.ValueOf("abc"), + reflect.ValueOf(int(0)), reflect.ValueOf(int8(1)), reflect.ValueOf(int16(-1)), reflect.ValueOf(int32(123)), reflect.ValueOf(int64(123)), + reflect.ValueOf(uint(123)), reflect.ValueOf(uint8(123)), reflect.ValueOf(uint16(123)), reflect.ValueOf(uint32(123)), reflect.ValueOf(uint64(123)), + reflect.ValueOf(float64(12.3)), reflect.ValueOf(float32(12.3)), + reflect.ValueOf(true), reflect.ValueOf(false), + } + + expected := []interface{}{ + "abc", + 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, + } + + for i := 0; i < len(cases); i++ { + actual, _ := ToInterface(cases[i]) + assert.Equal(expected[i], actual) + } + + nilVal, ok := ToInterface(reflect.ValueOf(nil)) + assert.EqualValues(nil, nilVal) + assert.Equal(false, ok) +} + +func TestUtf8ToGbk(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestUtf8ToGbk") + + utf8Data := []byte("hello") + gbkData, err := Utf8ToGbk(utf8Data) + + assert.Equal(true, utf8.Valid(utf8Data)) + assert.Equal(true, validator.IsGBK(gbkData)) + assert.IsNil(err) +} + +func TestGbkToUtf8(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestGbkToUtf8") + + gbkData, err := Utf8ToGbk([]byte("hello")) + utf8Data, err := GbkToUtf8(gbkData) + + assert.IsNil(err) + assert.Equal(true, utf8.Valid(utf8Data)) + assert.Equal("hello", string(utf8Data)) +} + +func TestToStdBase64(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToStdBase64") + + r1 := ToStdBase64("abc") + d1, _ := base64.StdEncoding.DecodeString(r1) + assert.Equal("abc", string(d1)) + + r2 := ToStdBase64([]byte("abc")) + d2, _ := base64.StdEncoding.DecodeString(r2) + assert.Equal("abc", string(d2)) + + r3 := ToStdBase64(123) + d3, _ := base64.StdEncoding.DecodeString(r3) + assert.Equal("123", string(d3)) + + r4 := ToStdBase64(11.11) + d4, _ := base64.StdEncoding.DecodeString(r4) + assert.Equal("11.11", string(d4)) + + r5 := ToStdBase64(map[string]any{"name": "duke", "quantity": 1}) + d5, _ := base64.StdEncoding.DecodeString(r5) + assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5)) + + r6 := ToStdBase64([]int64{7, 5, 9, 4, 23}) + d6, _ := base64.StdEncoding.DecodeString(r6) + assert.Equal("[7,5,9,4,23]", string(d6)) + + r7 := ToStdBase64([]string{"7", "5", "9", "4", "23"}) + d7, _ := base64.StdEncoding.DecodeString(r7) + assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7)) + + r8 := ToStdBase64(nil) + d8, _ := base64.StdEncoding.DecodeString(r8) + assert.Equal("", string(d8)) + + ch := make(chan int, 3) + ch <- 1 + ch <- 2 + r9 := ToStdBase64(ch) + d9, _ := base64.StdEncoding.DecodeString(r9) + assert.Equal("", string(d9)) + + r10 := ToStdBase64(io.EOF) + d10, _ := base64.StdEncoding.DecodeString(r10) + assert.Equal("EOF", string(d10)) + + r11 := ToStdBase64(errors.New("test")) + d11, _ := base64.StdEncoding.DecodeString(r11) + assert.Equal("test", string(d11)) + + typedNil := (*int)(nil) + r12 := ToStdBase64(typedNil) + d12, _ := base64.StdEncoding.DecodeString(r12) + assert.Equal("", string(d12)) + + type nilInterface interface { + } + var nI nilInterface = nil + d13, _ := base64.StdEncoding.DecodeString(ToStdBase64(nI)) + assert.Equal("", string(d13)) + + var p unsafe.Pointer + d14, _ := base64.StdEncoding.DecodeString(ToStdBase64(p)) + assert.Equal("", string(d14)) +} + +func TestToUrlBase64(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToUrlBase64") + + r1 := ToUrlBase64("abc") + d1, _ := base64.URLEncoding.DecodeString(r1) + assert.Equal("abc", string(d1)) + + r2 := ToUrlBase64([]byte("abc")) + d2, _ := base64.URLEncoding.DecodeString(r2) + assert.Equal("abc", string(d2)) + + r3 := ToUrlBase64(123) + d3, _ := base64.URLEncoding.DecodeString(r3) + assert.Equal("123", string(d3)) + + r4 := ToUrlBase64(11.11) + d4, _ := base64.URLEncoding.DecodeString(r4) + assert.Equal("11.11", string(d4)) + + r5 := ToUrlBase64(map[string]any{"name": "duke", "quantity": 1}) + d5, _ := base64.URLEncoding.DecodeString(r5) + assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5)) + + r6 := ToUrlBase64([]int64{7, 5, 9, 4, 23}) + d6, _ := base64.URLEncoding.DecodeString(r6) + assert.Equal("[7,5,9,4,23]", string(d6)) + + r7 := ToUrlBase64([]string{"7", "5", "9", "4", "23"}) + d7, _ := base64.URLEncoding.DecodeString(r7) + assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7)) + + r8 := ToUrlBase64(nil) + d8, _ := base64.URLEncoding.DecodeString(r8) + assert.Equal("", string(d8)) + + ch := make(chan int, 3) + ch <- 1 + ch <- 2 + r9 := ToUrlBase64(ch) + d9, _ := base64.URLEncoding.DecodeString(r9) + assert.Equal("", string(d9)) + + r10 := ToUrlBase64(io.EOF) + d10, _ := base64.URLEncoding.DecodeString(r10) + assert.Equal("EOF", string(d10)) + + r11 := ToUrlBase64(errors.New("test")) + d11, _ := base64.URLEncoding.DecodeString(r11) + assert.Equal("test", string(d11)) + + typedNil := (*int)(nil) + r12 := ToUrlBase64(typedNil) + d12, _ := base64.URLEncoding.DecodeString(r12) + assert.Equal("", string(d12)) + + type nilInterface interface { + } + var nI nilInterface = nil + d13, _ := base64.URLEncoding.DecodeString(ToUrlBase64(nI)) + assert.Equal("", string(d13)) + + var p unsafe.Pointer + d14, _ := base64.URLEncoding.DecodeString(ToUrlBase64(p)) + assert.Equal("", string(d14)) + + r15 := ToUrlBase64("4+3/4?=") + d15, _ := base64.URLEncoding.DecodeString(r15) + assert.Equal("4+3/4?=", string(d15)) +} + +func TestToRawStdBase64(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToRawStdBase64") + + r1 := ToRawStdBase64("abc") + d1, _ := base64.RawStdEncoding.DecodeString(r1) + assert.Equal("abc", string(d1)) + + r2 := ToRawStdBase64([]byte("abc")) + d2, _ := base64.RawStdEncoding.DecodeString(r2) + assert.Equal("abc", string(d2)) + + r3 := ToRawStdBase64(123) + d3, _ := base64.RawStdEncoding.DecodeString(r3) + assert.Equal("123", string(d3)) + + r4 := ToRawStdBase64(11.11) + d4, _ := base64.RawStdEncoding.DecodeString(r4) + assert.Equal("11.11", string(d4)) + + r5 := ToRawStdBase64(map[string]any{"name": "duke", "quantity": 1}) + d5, _ := base64.RawStdEncoding.DecodeString(r5) + assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5)) + + r6 := ToRawStdBase64([]int64{7, 5, 9, 4, 23}) + d6, _ := base64.RawStdEncoding.DecodeString(r6) + assert.Equal("[7,5,9,4,23]", string(d6)) + + r7 := ToRawStdBase64([]string{"7", "5", "9", "4", "23"}) + d7, _ := base64.RawStdEncoding.DecodeString(r7) + assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7)) + + r8 := ToRawStdBase64(nil) + d8, _ := base64.RawStdEncoding.DecodeString(r8) + assert.Equal("", string(d8)) + + ch := make(chan int, 3) + ch <- 1 + ch <- 2 + r9 := ToRawStdBase64(ch) + d9, _ := base64.RawStdEncoding.DecodeString(r9) + assert.Equal("", string(d9)) + + r10 := ToRawStdBase64(io.EOF) + d10, _ := base64.RawStdEncoding.DecodeString(r10) + assert.Equal("EOF", string(d10)) + + r11 := ToRawStdBase64(errors.New("test")) + d11, _ := base64.RawStdEncoding.DecodeString(r11) + assert.Equal("test", string(d11)) + + typedNil := (*int)(nil) + r12 := ToRawStdBase64(typedNil) + d12, _ := base64.RawStdEncoding.DecodeString(r12) + assert.Equal("", string(d12)) + + type nilInterface interface { + } + var nI nilInterface = nil + d13, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(nI)) + assert.Equal("", string(d13)) + + var p unsafe.Pointer + d14, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(p)) + assert.Equal("", string(d14)) +} + +func TestToRawUrlBase64(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestToRawUrlBase64") + + r1 := ToRawUrlBase64("abc") + d1, _ := base64.RawURLEncoding.DecodeString(r1) + assert.Equal("abc", string(d1)) + + r2 := ToRawUrlBase64([]byte("abc")) + d2, _ := base64.RawURLEncoding.DecodeString(r2) + assert.Equal("abc", string(d2)) + + r3 := ToRawUrlBase64(123) + d3, _ := base64.RawURLEncoding.DecodeString(r3) + assert.Equal("123", string(d3)) + + r4 := ToRawUrlBase64(11.11) + d4, _ := base64.RawURLEncoding.DecodeString(r4) + assert.Equal("11.11", string(d4)) + + r5 := ToRawUrlBase64(map[string]any{"name": "duke", "quantity": 1}) + d5, _ := base64.RawURLEncoding.DecodeString(r5) + assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5)) + + r6 := ToRawUrlBase64([]int64{7, 5, 9, 4, 23}) + d6, _ := base64.RawURLEncoding.DecodeString(r6) + assert.Equal("[7,5,9,4,23]", string(d6)) + + r7 := ToRawUrlBase64([]string{"7", "5", "9", "4", "23"}) + d7, _ := base64.RawURLEncoding.DecodeString(r7) + assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7)) + + r8 := ToRawUrlBase64(nil) + d8, _ := base64.RawURLEncoding.DecodeString(r8) + assert.Equal("", string(d8)) + + ch := make(chan int, 3) + ch <- 1 + ch <- 2 + r9 := ToRawUrlBase64(ch) + d9, _ := base64.RawURLEncoding.DecodeString(r9) + assert.Equal("", string(d9)) + + r10 := ToRawUrlBase64(io.EOF) + d10, _ := base64.RawURLEncoding.DecodeString(r10) + assert.Equal("EOF", string(d10)) + + r11 := ToRawUrlBase64(errors.New("test")) + d11, _ := base64.RawURLEncoding.DecodeString(r11) + assert.Equal("test", string(d11)) + + typedNil := (*int)(nil) + r12 := ToRawUrlBase64(typedNil) + d12, _ := base64.RawURLEncoding.DecodeString(r12) + assert.Equal("", string(d12)) + + type nilInterface interface { + } + var nI nilInterface = nil + d13, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(nI)) + assert.Equal("", string(d13)) + + var p unsafe.Pointer + d14, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(p)) + assert.Equal("", string(d14)) + + r15 := ToRawUrlBase64("4+3/4?=") + 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) + }) + } +} diff --git a/cryptor/aes.go b/cryptor/aes.go deleted file mode 100644 index 58995b00..00000000 --- a/cryptor/aes.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2021 dudaodong@gmail.com. All rights reserved. -// Use of this source code is governed by MIT license - -// Package cryptor implements some util functions to encrypt and decrypt. -// Note: -// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic. -package cryptor - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "io" -) - -// AesEcbEncrypt encrypt data with key use AES ECB algorithm -// len(key) should be 16, 24 or 32 -func AesEcbEncrypt(data, key []byte) []byte { - cipher, _ := aes.NewCipher(generateAesKey(key)) - length := (len(data) + aes.BlockSize) / aes.BlockSize - plain := make([]byte, length*aes.BlockSize) - copy(plain, data) - pad := byte(len(plain) - len(data)) - for i := len(data); i < len(plain); i++ { - plain[i] = pad - } - encrypted := make([]byte, len(plain)) - 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]) - } - - return encrypted -} - -// AesEcbDecrypt decrypt data with key use AES ECB algorithm -// len(key) should be 16, 24 or 32 -func AesEcbDecrypt(encrypted, key []byte) []byte { - cipher, _ := aes.NewCipher(generateAesKey(key)) - 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]) - } - - trim := 0 - if len(decrypted) > 0 { - trim = len(decrypted) - int(decrypted[len(decrypted)-1]) - } - - return decrypted[:trim] -} - -// AesCbcEncrypt encrypt data with key use AES CBC algorithm -// len(key) should be 16, 24 or 32 -func AesCbcEncrypt(data, key []byte) []byte { - // len(key) should be 16, 24 or 32 - block, _ := aes.NewCipher(key) - blockSize := block.BlockSize() - data = pkcs7Padding(data, blockSize) - blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) - - encrypted := make([]byte, len(data)) - blockMode.CryptBlocks(encrypted, data) - return encrypted -} - -// AesCbcDecrypt decrypt data with key use AES CBC algorithm -// len(key) should be 16, 24 or 32 -func AesCbcDecrypt(encrypted, key []byte) []byte { - block, _ := aes.NewCipher(key) - blockSize := block.BlockSize() - blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) - - decrypted := make([]byte, len(encrypted)) - blockMode.CryptBlocks(decrypted, encrypted) - decrypted = pkcs7UnPadding(decrypted) - return decrypted -} - -// AesCtrCrypt encrypt data with key use AES CTR algorithm -// len(key) should be 16, 24 or 32 -func AesCtrCrypt(data, key []byte) []byte { - block, _ := aes.NewCipher(key) - - iv := bytes.Repeat([]byte("1"), block.BlockSize()) - stream := cipher.NewCTR(block, iv) - - dst := make([]byte, len(data)) - stream.XORKeyStream(dst, data) - - return dst -} - -// AesCfbEncrypt encrypt data with key use AES CFB algorithm -// len(key) should be 16, 24 or 32 -func AesCfbEncrypt(data, key []byte) []byte { - block, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - - encrypted := make([]byte, aes.BlockSize+len(data)) - iv := encrypted[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - panic(err) - } - - stream := cipher.NewCFBEncrypter(block, iv) - stream.XORKeyStream(encrypted[aes.BlockSize:], data) - return encrypted -} - -// AesCfbDecrypt decrypt data with key use AES CFB algorithm -// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32 -func AesCfbDecrypt(encrypted, key []byte) []byte { - block, _ := aes.NewCipher(key) - if len(encrypted) < aes.BlockSize { - panic("encrypted data is too short") - } - iv := encrypted[:aes.BlockSize] - encrypted = encrypted[aes.BlockSize:] - - stream := cipher.NewCFBDecrypter(block, iv) - stream.XORKeyStream(encrypted, encrypted) - return encrypted -} - -// AesOfbEncrypt encrypt data with key use AES OFB algorithm -// len(key) should be 16, 24 or 32 -func AesOfbEncrypt(data, key []byte) []byte { - block, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - data = pkcs7Padding(data, aes.BlockSize) - encrypted := make([]byte, aes.BlockSize+len(data)) - iv := encrypted[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - panic(err) - } - - stream := cipher.NewOFB(block, iv) - stream.XORKeyStream(encrypted[aes.BlockSize:], data) - return encrypted -} - -// AesOfbDecrypt decrypt data with key use AES OFB algorithm -// len(key) should be 16, 24 or 32 -func AesOfbDecrypt(data, key []byte) []byte { - block, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - - iv := data[:aes.BlockSize] - data = data[aes.BlockSize:] - if len(data)%aes.BlockSize != 0 { - return nil - } - - decrypted := make([]byte, len(data)) - mode := cipher.NewOFB(block, iv) - mode.XORKeyStream(decrypted, data) - - decrypted = pkcs7UnPadding(decrypted) - return decrypted -} diff --git a/cryptor/aes_test.go b/cryptor/aes_test.go deleted file mode 100644 index 928d63e5..00000000 --- a/cryptor/aes_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cryptor - -import ( - "testing" - - "github.com/duke-git/lancet/internal" -) - -func TestAesEcbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefghijklmnop" - - aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key)) - aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestAesEcbEncrypt") - assert.Equal(data, string(aesEcbDecrypt)) -} - -func TestAesCbcEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefghijklmnop" - - aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key)) - aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestAesCbcEncrypt") - assert.Equal(data, string(aesCbcDecrypt)) -} - -func TestAesCtrCrypt(t *testing.T) { - data := "hello world" - key := "abcdefghijklmnop" - - aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key)) - aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestAesCtrCrypt") - assert.Equal(data, string(aesCtrDeCrypt)) -} - -func TestAesCfbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefghijklmnop" - - aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key)) - aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestAesCfbEncrypt") - assert.Equal(data, string(aesCfbDecrypt)) -} - -func TestAesOfbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefghijklmnop" - - aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key)) - aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestAesOfbEncrypt") - assert.Equal(data, string(aesOfbDecrypt)) -} diff --git a/cryptor/basic.go b/cryptor/basic.go index c4df12d7..7818e81e 100644 --- a/cryptor/basic.go +++ b/cryptor/basic.go @@ -6,7 +6,6 @@ package cryptor import ( - "bufio" "crypto/hmac" "crypto/md5" "crypto/sha1" @@ -19,101 +18,194 @@ import ( "os" ) -// Base64StdEncode encode string with base64 encoding +// Base64StdEncode encode string with base64 encoding. +// Play: https://go.dev/play/p/VOaUyQUreoK func Base64StdEncode(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) } -// Base64StdDecode decode a base64 encoded string +// Base64StdDecode decode a base64 encoded string. +// Play: https://go.dev/play/p/RWQylnJVgIe func Base64StdDecode(s string) string { b, _ := base64.StdEncoding.DecodeString(s) return string(b) } -// Md5String return the md5 value of string +// Md5String return the md5 value of string. +// Play: https://go.dev/play/p/1bLcVetbTOI func Md5String(s string) string { h := md5.New() h.Write([]byte(s)) return hex.EncodeToString(h.Sum(nil)) } -// Md5File return the md5 value of file +// Md5StringWithBase64 return the md5 value of string with base64. +// Play: https://go.dev/play/p/Lx4gH7Vdr5_y +func Md5StringWithBase64(s string) string { + h := md5.New() + h.Write([]byte(s)) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +// Md5Byte return the md5 string of byte slice. +// Play: https://go.dev/play/p/suraalH8lyC +func Md5Byte(data []byte) string { + h := md5.New() + h.Write(data) + return hex.EncodeToString(h.Sum(nil)) +} + +// Md5ByteWithBase64 return the md5 string of byte slice with base64. +// Play: https://go.dev/play/p/Tcb-Z7LN2ax +func Md5ByteWithBase64(data []byte) string { + h := md5.New() + h.Write(data) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +// Md5File return the md5 value of file. func Md5File(filename string) (string, error) { - if fileInfo, err := os.Stat(filename); err != nil { + file, err := os.Open(filename) + if err != nil { return "", err - } else if fileInfo.IsDir() { - return "", nil } + defer file.Close() - file, err := os.Open(filename) + stat, err := file.Stat() if err != nil { return "", err } - defer file.Close() + if stat.IsDir() { + return "", nil + } hash := md5.New() + buf := make([]byte, 65536) // 64KB - chunkSize := 65536 - for buf, reader := make([]byte, chunkSize), bufio.NewReader(file); ; { - n, err := reader.Read(buf) - if err != nil { - if err == io.EOF { - break - } + for { + n, err := file.Read(buf) + if err != nil && err != io.EOF { return "", err } - hash.Write(buf[:n]) + if n > 0 { + hash.Write(buf[:n]) + } + if err == io.EOF { + break + } } - checksum := fmt.Sprintf("%x", hash.Sum(nil)) - return checksum, nil + return fmt.Sprintf("%x", hash.Sum(nil)), nil } -// HmacMd5 return the hmac hash of string use md5 -func HmacMd5(data, key string) string { +// HmacMd5 return the hmac hash of string use md5. +// Play: https://go.dev/play/p/uef0q1fz53I +func HmacMd5(str, key string) string { h := hmac.New(md5.New, []byte(key)) - h.Write([]byte(data)) + h.Write([]byte(str)) return hex.EncodeToString(h.Sum([]byte(""))) } -// HmacSha1 return the hmac hash of string use sha1 -func HmacSha1(data, key string) string { - h := hmac.New(sha1.New, []byte(key)) +// HmacMd5WithBase64 return the hmac hash of string use md5 with base64. +// https://go.dev/play/p/UY0ng2AefFC +func HmacMd5WithBase64(data, key string) string { + h := hmac.New(md5.New, []byte(key)) h.Write([]byte(data)) + return base64.StdEncoding.EncodeToString(h.Sum([]byte(""))) +} + +// HmacSha1 return the hmac hash of string use sha1. +// Play: https://go.dev/play/p/1UI4oQ4WXKM +func HmacSha1(str, key string) string { + h := hmac.New(sha1.New, []byte(key)) + h.Write([]byte(str)) return hex.EncodeToString(h.Sum([]byte(""))) } -// HmacSha256 return the hmac hash of string use sha256 -func HmacSha256(data, key string) string { +// HmacSha1WithBase64 return the hmac hash of string use sha1 with base64. +// Play: https://go.dev/play/p/47JmmGrnF7B +func HmacSha1WithBase64(str, key string) string { + h := hmac.New(sha1.New, []byte(key)) + h.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(h.Sum([]byte(""))) +} + +// HmacSha256 return the hmac hash of string use sha256. +// Play: https://go.dev/play/p/HhpwXxFhhC0 +func HmacSha256(str, key string) string { h := hmac.New(sha256.New, []byte(key)) - h.Write([]byte(data)) + h.Write([]byte(str)) return hex.EncodeToString(h.Sum([]byte(""))) } -// HmacSha512 return the hmac hash of string use sha512 -func HmacSha512(data, key string) string { +// HmacSha256WithBase64 return the hmac hash of string use sha256 with base64. +// Play: https://go.dev/play/p/EKbkUvPTLwO +func HmacSha256WithBase64(str, key string) string { + h := hmac.New(sha256.New, []byte(key)) + h.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(h.Sum([]byte(""))) +} + +// HmacSha512 return the hmac hash of string use sha512. +// Play: https://go.dev/play/p/59Od6m4A0Ud +func HmacSha512(str, key string) string { h := hmac.New(sha512.New, []byte(key)) - h.Write([]byte(data)) + h.Write([]byte(str)) return hex.EncodeToString(h.Sum([]byte(""))) } -// Sha1 return the sha1 value (SHA-1 hash algorithm) of string -func Sha1(data string) string { +// HmacSha512WithBase64 return the hmac hash of string use sha512 with base64. +// Play: https://go.dev/play/p/c6dSe3E2ydU +func HmacSha512WithBase64(str, key string) string { + h := hmac.New(sha512.New, []byte(key)) + h.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(h.Sum([]byte(""))) +} + +// Sha1 return the sha1 value (SHA-1 hash algorithm) of string. +// Play: https://go.dev/play/p/_m_uoD1deMT +func Sha1(str string) string { sha1 := sha1.New() - sha1.Write([]byte(data)) + sha1.Write([]byte(str)) return hex.EncodeToString(sha1.Sum([]byte(""))) } -// Sha256 return the sha256 value (SHA256 hash algorithm) of string -func Sha256(data string) string { +// Sha1WithBase64 return the sha1 value (SHA-1 hash algorithm) of base64 string. +// Play: https://go.dev/play/p/fSyx-Gl2l2- +func Sha1WithBase64(str string) string { + sha1 := sha1.New() + sha1.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(sha1.Sum([]byte(""))) +} + +// Sha256 return the sha256 value (SHA256 hash algorithm) of string. +// Play: https://go.dev/play/p/tU9tfBMIAr1 +func Sha256(str string) string { sha256 := sha256.New() - sha256.Write([]byte(data)) + sha256.Write([]byte(str)) return hex.EncodeToString(sha256.Sum([]byte(""))) } -// Sha512 return the sha512 value (SHA512 hash algorithm) of string -func Sha512(data string) string { +// Sha256WithBase64 return the sha256 value (SHA256 hash algorithm) of base64 string. +// Play: https://go.dev/play/p/85IXJHIal1k +func Sha256WithBase64(str string) string { + sha256 := sha256.New() + sha256.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(sha256.Sum([]byte(""))) +} + +// Sha512 return the sha512 value (SHA512 hash algorithm) of string. +// Play: https://go.dev/play/p/3WsvLYZxsHa +func Sha512(str string) string { sha512 := sha512.New() - sha512.Write([]byte(data)) + sha512.Write([]byte(str)) return hex.EncodeToString(sha512.Sum([]byte(""))) } + +// Sha512WithBase64 return the sha512 value (SHA512 hash algorithm) of base64 string. +// Play: https://go.dev/play/p/q_fY2rA-k5I +func Sha512WithBase64(str string) string { + sha512 := sha512.New() + sha512.Write([]byte(str)) + return base64.StdEncoding.EncodeToString(sha512.Sum([]byte(""))) +} diff --git a/cryptor/basic_test.go b/cryptor/basic_test.go index 90e5f92d..075d8afc 100644 --- a/cryptor/basic_test.go +++ b/cryptor/basic_test.go @@ -3,25 +3,55 @@ package cryptor import ( "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestBase64StdEncode(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBase64StdEncode") assert.Equal("aGVsbG8gd29ybGQ=", Base64StdEncode("hello world")) } func TestBase64StdDecode(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBase64StdDecode") assert.Equal("hello world", Base64StdDecode("aGVsbG8gd29ybGQ=")) } func TestMd5String(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestMd5String") assert.Equal("5d41402abc4b2a76b9719d911017c592", Md5String("hello")) } +func TestMd5StringWithBase64(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMd5StringWithBase64") + assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5StringWithBase64("hello")) +} + +func TestMd5Byte(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMd5Byte") + data := []byte{'a'} + assert.Equal("0cc175b9c0f1b6a831c399e269772661", Md5Byte(data)) +} + +func TestMd5ByteWithBase64(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMd5ByteWithBase64") + assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5ByteWithBase64([]byte("hello"))) +} + func TestMd5File(t *testing.T) { + t.Parallel() + fileMd5, err := Md5File("./basic.go") assert := internal.NewAssert(t, "TestMd5File") assert.IsNotNil(fileMd5) @@ -29,11 +59,22 @@ func TestMd5File(t *testing.T) { } func TestHmacMd5(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestHmacMd5") assert.Equal("5f4c9faaff0a1ad3007d9ddc06abe36d", HmacMd5("hello world", "12345")) } +func TestHmacMd5WithBase64(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHmacMd5WithBase64") + assert.Equal("6DQwbquJLYclJdSRinpjmg==", HmacMd5WithBase64("hello", "12345")) +} + func TestHmacSha1(t *testing.T) { + t.Parallel() + s := "hello world" key := "12345" hmacSha1 := HmacSha1(s, key) @@ -43,17 +84,45 @@ func TestHmacSha1(t *testing.T) { assert.Equal(expected, hmacSha1) } +func TestHmacSha1WithBase64(t *testing.T) { + t.Parallel() + + s := "hello" + key := "12345" + hmacSha1 := HmacSha1WithBase64(s, key) + expected := "XGqdsMzLkuNu0DI/0Jt/k23prOA=" + + assert := internal.NewAssert(t, "TestHmacSha1") + assert.Equal(expected, hmacSha1) +} + func TestHmacSha256(t *testing.T) { - s := "hello world" + t.Parallel() + + str := "hello world" key := "12345" - hmacSha256 := HmacSha256(s, key) + hmacSha256 := HmacSha256(str, key) expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8" assert := internal.NewAssert(t, "TestHmacSha256") assert.Equal(expected, hmacSha256) } +func TestHmacSha256WithBase64(t *testing.T) { + t.Parallel() + + str := "hello" + key := "12345" + hms := HmacSha256WithBase64(str, key) + expected := "MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU=" + + assert := internal.NewAssert(t, "TestHmacSha256WithBase64") + assert.Equal(expected, hms) +} + func TestHmacSha512(t *testing.T) { + t.Parallel() + s := "hello world" key := "12345" hmacSha512 := HmacSha512(s, key) @@ -63,7 +132,21 @@ func TestHmacSha512(t *testing.T) { assert.Equal(expected, hmacSha512) } +func TestHmacSha512WithBase64(t *testing.T) { + t.Parallel() + + str := "hello" + key := "12345" + hms := HmacSha512WithBase64(str, key) + expected := "3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==" + + assert := internal.NewAssert(t, "TestHmacSha512WithBase64") + assert.Equal(expected, hms) +} + func TestSha1(t *testing.T) { + t.Parallel() + s := "hello world" sha1 := Sha1(s) expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" @@ -72,7 +155,19 @@ func TestSha1(t *testing.T) { assert.Equal(expected, sha1) } +func TestSha1WithBase64(t *testing.T) { + t.Parallel() + + str := Sha1WithBase64("hello") + expected := "qvTGHdzF6KLavt4PO0gs2a6pQ00=" + + assert := internal.NewAssert(t, "TestSha1WithBase64") + assert.Equal(expected, str) +} + func TestSha256(t *testing.T) { + t.Parallel() + s := "hello world" sha256 := Sha256(s) expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" @@ -81,7 +176,19 @@ func TestSha256(t *testing.T) { assert.Equal(expected, sha256) } +func TestSha256WithBase64(t *testing.T) { + t.Parallel() + + str := Sha256WithBase64("hello") + expected := "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=" + + assert := internal.NewAssert(t, "TestSha256WithBase64") + assert.Equal(expected, str) +} + func TestSha512(t *testing.T) { + t.Parallel() + s := "hello world" sha512 := Sha512(s) expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f" @@ -89,3 +196,13 @@ func TestSha512(t *testing.T) { assert := internal.NewAssert(t, "TestSha512") assert.Equal(expected, sha512) } + +func TestSha512WithBase64(t *testing.T) { + t.Parallel() + + str := Sha512WithBase64("hello") + expected := "m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==" + + assert := internal.NewAssert(t, "TestSha512WithBase64") + assert.Equal(expected, str) +} diff --git a/cryptor/crypto.go b/cryptor/crypto.go new file mode 100644 index 00000000..8e4fbf84 --- /dev/null +++ b/cryptor/crypto.go @@ -0,0 +1,859 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package cryptor implements some util functions to encrypt and decrypt. +// Note: +// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic. +package cryptor + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "io" + "os" +) + +// 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 { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + blockSize := aes.BlockSize + dataLen := len(data) + padding := blockSize - (dataLen % blockSize) + paddedLen := dataLen + padding + + paddedData := make([]byte, paddedLen) + copy(paddedData, data) + + for i := dataLen; i < paddedLen; i++ { + paddedData[i] = byte(padding) + } + + cipher, err := aes.NewCipher(generateAesKey(key, len(key))) + if err != nil { + panic("aes: failed to create cipher: " + err.Error()) + } + + encrypted := make([]byte, paddedLen) + for bs := 0; bs < paddedLen; bs += blockSize { + cipher.Encrypt(encrypted[bs:], paddedData[bs:]) + } + + return encrypted +} + +// AesEcbDecrypt decrypt data with key use AES ECB algorithm +// len(key) should be 16, 24 or 32. +// Play: https://go.dev/play/p/jT5irszHx-j +func AesEcbDecrypt(encrypted, key []byte) []byte { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + 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 i := 0; i < len(encrypted); i += blockSize { + cipher.Decrypt(decrypted[i:], encrypted[i:]) + } + + 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[: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 { + 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()) + } + + 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("aes: failed to generate IV: " + err.Error()) + } + + encrypted := make([]byte, len(padded)) + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(encrypted, padded) + + 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 { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + 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] + 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(decrypted, ciphertext) + + 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 { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + block, _ := aes.NewCipher(key) + + iv := bytes.Repeat([]byte("1"), block.BlockSize()) + stream := cipher.NewCTR(block, iv) + + dst := make([]byte, len(data)) + stream.XORKeyStream(dst, data) + + return dst +} + +// AesCtrEncrypt encrypt data with key use AES CTR algorithm +// len(key) should be 16, 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("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()) + } + + stream := cipher.NewCTR(block, iv) + ciphertext := make([]byte, len(data)) + stream.XORKeyStream(ciphertext, data) + + 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 { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + if len(encrypted) < aes.BlockSize { + panic("aes: encrypted data too short") + } + + iv := 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) + + 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 { + 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.NewOFB(block, iv) + stream.XORKeyStream(ciphertext, data) + + 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 { + if !isAesKeyLengthValid(len(key)) { + panic("aes: invalid key length (must be 16, 24, or 32 bytes)") + } + + if len(data) < aes.BlockSize { + panic("aes: encrypted data too short") + } + + iv := data[:aes.BlockSize] + ciphertext := data[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.NewOFB(block, iv) + stream.XORKeyStream(plaintext, ciphertext) + + 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("aes: failed to create cipher: " + err.Error()) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + panic("aes: failed to create GCM: " + err.Error()) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + panic("aes: failed to generate nonce: " + err.Error()) + } + + ciphertext := gcm.Seal(nil, nonce, data, nil) + + 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("aes: failed to create cipher: " + err.Error()) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + panic("aes: failed to create GCM: " + err.Error()) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + panic("aes: ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + panic("aes: decryption failed: " + err.Error()) + } + + return plaintext +} + +// DesEcbEncrypt encrypt data with key use DES ECB algorithm +// len(key) should be 8. +// Play: https://go.dev/play/p/8qivmPeZy4P +func DesEcbEncrypt(data, key []byte) []byte { + cipher, err := des.NewCipher(generateDesKey(key)) + if err != nil { + panic("des: failed to create cipher: " + err.Error()) + } + + blockSize := cipher.BlockSize() + padded := pkcs5Padding(data, blockSize) + encrypted := make([]byte, len(padded)) + + for i := 0; i < len(padded); i += blockSize { + cipher.Encrypt(encrypted[i:], padded[i:]) + } + + return encrypted +} + +// DesEcbDecrypt decrypt data with key use DES ECB algorithm +// len(key) should be 8. +// Play: https://go.dev/play/p/8qivmPeZy4P +func DesEcbDecrypt(encrypted, key []byte) []byte { + 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 i := 0; i < len(encrypted); i += blockSize { + cipher.Decrypt(decrypted[i:], encrypted[i:]) + } + + // 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 { + 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() + data = pkcs7Padding(data, blockSize) + + encrypted := make([]byte, blockSize+len(data)) + iv := encrypted[:blockSize] + + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic("des: failed to generate IV: " + err.Error()) + } + + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(encrypted[blockSize:], data) + + return encrypted +} + +// DesCbcDecrypt decrypt data with key use DES CBC algorithm +// len(key) should be 8. +// Play: https://go.dev/play/p/4cC4QvWfe3_1 +func DesCbcDecrypt(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 || len(encrypted)%blockSize != 0 { + panic("des: invalid encrypted data length") + } + + iv := encrypted[:blockSize] + ciphertext := encrypted[blockSize:] + + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(ciphertext, ciphertext) + + 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 { + panic("key length shoud be 8") + } + + block, _ := des.NewCipher(key) + + iv := bytes.Repeat([]byte("1"), block.BlockSize()) + stream := cipher.NewCTR(block, iv) + + dst := make([]byte, len(data)) + stream.XORKeyStream(dst, data) + + return dst +} + +// DesCtrEncrypt encrypt data with key use DES CTR algorithm +// len(key) should 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("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)) + + copy(encrypted[:des.BlockSize], iv) + + stream := cipher.NewCFBEncrypter(block, iv) + stream.XORKeyStream(encrypted[des.BlockSize:], data) + + return encrypted +} + +// DesCfbDecrypt decrypt data with key use DES CFB algorithm +// 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 { + 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()) + } + + if len(encrypted) < des.BlockSize { + panic("des: encrypted data too short") + } + + iv := encrypted[:des.BlockSize] + ciphertext := encrypted[des.BlockSize:] + + stream := cipher.NewCFBDecrypter(block, iv) + stream.XORKeyStream(ciphertext, ciphertext) + + 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 { + 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()) + } + + data = pkcs7Padding(data, des.BlockSize) + + 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)) + copy(encrypted[:des.BlockSize], iv) + + stream := cipher.NewOFB(block, iv) + stream.XORKeyStream(encrypted[des.BlockSize:], data) + + return encrypted +} + +// DesOfbDecrypt decrypt data with key use DES OFB algorithm +// len(key) should be 8. +// Play: https://go.dev/play/p/74KmNadjN1J +func DesOfbDecrypt(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()) + } + + if len(data) < des.BlockSize { + panic("des: encrypted data too short") + } + + iv := data[:des.BlockSize] + ciphertext := data[des.BlockSize:] + + stream := cipher.NewOFB(block, iv) + decrypted := make([]byte, len(ciphertext)) + stream.XORKeyStream(decrypted, ciphertext) + + decrypted = pkcs7UnPadding(decrypted) + + return decrypted +} + +// GenerateRsaKey create rsa private and public pemo file. +// Play: https://go.dev/play/p/zutRHrDqs0X +func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error { + // private key + privateKey, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + return err + } + + derText := x509.MarshalPKCS1PrivateKey(privateKey) + + block := pem.Block{ + Type: "rsa private key", + Bytes: derText, + } + + file, err := os.Create(priKeyFile) + if err != nil { + panic(err) + } + err = pem.Encode(file, &block) + if err != nil { + return err + } + + file.Close() + + // public key + publicKey := privateKey.PublicKey + + derpText, err := x509.MarshalPKIXPublicKey(&publicKey) + if err != nil { + return err + } + + block = pem.Block{ + Type: "rsa public key", + Bytes: derpText, + } + + file, err = os.Create(pubKeyFile) + if err != nil { + return err + } + + err = pem.Encode(file, &block) + if err != nil { + return err + } + + file.Close() + + return nil +} + +// RsaEncrypt encrypt data with ras algorithm. +// Play: https://go.dev/play/p/7_zo6mrx-eX +func RsaEncrypt(data []byte, pubKeyFileName string) []byte { + file, err := os.Open(pubKeyFileName) + if err != nil { + panic(err) + } + fileInfo, err := file.Stat() + if err != nil { + panic(err) + } + defer file.Close() + buf := make([]byte, fileInfo.Size()) + + _, err = file.Read(buf) + if err != nil { + panic(err) + } + + block, _ := pem.Decode(buf) + + pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + panic(err) + } + pubKey := pubInterface.(*rsa.PublicKey) + + cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data) + if err != nil { + panic(err) + } + + return cipherText +} + +// RsaDecrypt decrypt data with ras algorithm. +// Play: https://go.dev/play/p/7_zo6mrx-eX +func RsaDecrypt(data []byte, privateKeyFileName string) []byte { + file, err := os.Open(privateKeyFileName) + if err != nil { + panic(err) + } + fileInfo, err := file.Stat() + if err != nil { + panic(err) + } + buf := make([]byte, fileInfo.Size()) + defer file.Close() + + _, err = file.Read(buf) + if err != nil { + panic(err) + } + + block, _ := pem.Decode(buf) + + priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } + + plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data) + if err != nil { + panic(err) + } + + return plainText +} + +// GenerateRsaKeyPair create rsa private and public key. +// Play: https://go.dev/play/p/sSVmkfENKMz +func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) { + privateKey, _ := rsa.GenerateKey(rand.Reader, keySize) + return privateKey, &privateKey.PublicKey +} + +// RsaEncryptOAEP encrypts the given data with RSA-OAEP. +// Play: https://go.dev/play/p/sSVmkfENKMz +func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) { + encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, data, label) + if err != nil { + return nil, err + } + + return encryptedBytes, nil +} + +// RsaDecryptOAEP decrypts the data with RSA-OAEP. +// Play: https://go.dev/play/p/sSVmkfENKMz +func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) { + decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &key, ciphertext, label) + if err != nil { + return nil, err + } + + return decryptedBytes, nil +} + +// RsaSign signs the data with RSA. +// 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 { + return nil, err + } + + hashed, err := hashData(hash, data) + if err != nil { + return nil, err + } + + return rsa.SignPKCS1v15(rand.Reader, privateKey, hash, hashed) +} + +// RsaVerifySign verifies the signature of the data with RSA. +// 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 { + return err + } + + hashed, err := hashData(hash, data) + if err != nil { + return err + } + + return rsa.VerifyPKCS1v15(publicKey, hash, hashed, signature) +} diff --git a/cryptor/crypto_example_test.go b/cryptor/crypto_example_test.go new file mode 100644 index 00000000..e87e47db --- /dev/null +++ b/cryptor/crypto_example_test.go @@ -0,0 +1,624 @@ +package cryptor + +import ( + "crypto" + "fmt" +) + +func ExampleAesEcbEncrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesEcbDecrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesCbcEncrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesCbcEncrypt([]byte(data), []byte(key)) + + decrypted := AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesCbcDecrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesCbcEncrypt([]byte(data), []byte(key)) + + decrypted := AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesCtrCrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesCtrCrypt([]byte(data), []byte(key)) + decrypted := AesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // 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" + + encrypted := AesCfbEncrypt([]byte(data), []byte(key)) + + decrypted := AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesCfbDecrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesCfbEncrypt([]byte(data), []byte(key)) + + decrypted := AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesOfbEncrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesOfbEncrypt([]byte(data), []byte(key)) + + decrypted := AesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesOfbDecrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesOfbEncrypt([]byte(data), []byte(key)) + + decrypted := AesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesGcmEncrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesGcmEncrypt([]byte(data), []byte(key)) + + decrypted := AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleAesGcmDecrypt() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := AesGcmEncrypt([]byte(data), []byte(key)) + + decrypted := AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesEcbEncrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesEcbDecrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesCbcEncrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesCbcEncrypt([]byte(data), []byte(key)) + + decrypted := DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesCbcDecrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesCbcEncrypt([]byte(data), []byte(key)) + + decrypted := DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesCtrCrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesCtrCrypt([]byte(data), []byte(key)) + decrypted := DesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // 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" + + encrypted := DesCfbEncrypt([]byte(data), []byte(key)) + + decrypted := DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesCfbDecrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesCfbEncrypt([]byte(data), []byte(key)) + + decrypted := DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesOfbEncrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesOfbEncrypt([]byte(data), []byte(key)) + + decrypted := DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleDesOfbDecrypt() { + data := "hello" + key := "abcdefgh" + + encrypted := DesOfbEncrypt([]byte(data), []byte(key)) + + decrypted := DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleGenerateRsaKey() { + // Create ras private and public pem file + err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem") + if err != nil { + return + } + + fmt.Println("foo") + + // Output: + // foo +} + +func ExampleRsaEncrypt() { + // Create ras private and public pem file + err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := RsaEncrypt(data, "rsa_public_example.pem") + decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleRsaDecrypt() { + // Create ras private and public pem file + err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := RsaEncrypt(data, "rsa_public_example.pem") + decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} + +func ExampleBase64StdEncode() { + base64Str := Base64StdEncode("hello") + + fmt.Println(base64Str) + + // Output: + // aGVsbG8= +} + +func ExampleBase64StdDecode() { + str := Base64StdDecode("aGVsbG8=") + + fmt.Println(str) + + // Output: + // hello +} + +func ExampleHmacMd5() { + str := "hello" + key := "12345" + + hms := HmacMd5(str, key) + fmt.Println(hms) + + // Output: + // e834306eab892d872525d4918a7a639a +} + +func ExampleHmacMd5WithBase64() { + str := "hello" + key := "12345" + + hms := HmacMd5WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 6DQwbquJLYclJdSRinpjmg== +} + +func ExampleHmacSha1() { + str := "hello" + key := "12345" + + hms := HmacSha1(str, key) + fmt.Println(hms) + + // Output: + // 5c6a9db0cccb92e36ed0323fd09b7f936de9ace0 +} + +func ExampleHmacSha1WithBase64() { + str := "hello" + key := "12345" + + hms := HmacSha1WithBase64(str, key) + fmt.Println(hms) + + // Output: + // XGqdsMzLkuNu0DI/0Jt/k23prOA= +} + +func ExampleHmacSha256() { + str := "hello" + key := "12345" + + hms := HmacSha256(str, key) + fmt.Println(hms) + + // Output: + // 315bb93c4e989862ba09cb62e05d73a5f376cb36f0d786edab0c320d059fde75 +} + +func ExampleHmacSha256WithBase64() { + str := "hello" + key := "12345" + + hms := HmacSha256WithBase64(str, key) + fmt.Println(hms) + + // Output: + // MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU= +} + +func ExampleHmacSha512() { + str := "hello" + key := "12345" + + hms := HmacSha512(str, key) + fmt.Println(hms) + + // Output: + // dd8f1290a9dd23d354e2526d9a2e9ce8cffffdd37cb320800d1c6c13d2efc363288376a196c5458daf53f8e1aa6b45a6d856303d5c0a2064bff9785861d48cfc +} + +func ExampleHmacSha512WithBase64() { + str := "hello" + key := "12345" + + hms := HmacSha512WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A== +} + +func ExampleMd5String() { + md5Str := Md5String("hello") + fmt.Println(md5Str) + + // Output: + // 5d41402abc4b2a76b9719d911017c592 +} + +func ExampleMd5StringWithBase64() { + md5Str := Md5StringWithBase64("hello") + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} + +func ExampleMd5Byte() { + md5Str := Md5Byte([]byte{'a'}) + fmt.Println(md5Str) + + // Output: + // 0cc175b9c0f1b6a831c399e269772661 +} + +func ExampleMd5ByteWithBase64() { + md5Str := Md5ByteWithBase64([]byte("hello")) + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} + +func ExampleSha1() { + result := Sha1("hello") + fmt.Println(result) + + // Output: + // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d +} + +func ExampleSha1WithBase64() { + result := Sha1WithBase64("hello") + fmt.Println(result) + + // Output: + // qvTGHdzF6KLavt4PO0gs2a6pQ00= +} + +func ExampleSha256() { + result := Sha256("hello") + fmt.Println(result) + + // Output: + // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 +} + +func ExampleSha256WithBase64() { + result := Sha256WithBase64("hello") + fmt.Println(result) + + // Output: + // LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= +} + +func ExampleSha512() { + result := Sha512("hello") + fmt.Println(result) + + // Output: + // 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 +} + +func ExampleSha512WithBase64() { + result := Sha512WithBase64("hello") + fmt.Println(result) + + // Output: + // m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw== +} + +func ExampleRsaEncryptOAEP() { + pri, pub := GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := RsaEncryptOAEP(data, label, *pub) + if err != nil { + return + } + + decrypted, err := RsaDecryptOAEP([]byte(encrypted), label, *pri) + if err != nil { + return + } + + fmt.Println(string(decrypted)) + + // Output: + // hello world +} + +func ExampleRsaSign() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private_example.pem" + publicKey := "./rsa_public_example.pem" + + signature, err := RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } + + fmt.Println("ok") + + // Output: + // ok +} + +func ExampleRsaVerifySign() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private_example.pem" + publicKey := "./rsa_public_example.pem" + + signature, err := RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } + + fmt.Println("ok") + + // Output: + // ok +} diff --git a/cryptor/crypto_internal.go b/cryptor/crypto_internal.go index 6005346e..e458386e 100644 --- a/cryptor/crypto_internal.go +++ b/cryptor/crypto_internal.go @@ -1,12 +1,23 @@ package cryptor -import "bytes" +import ( + "bytes" + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "crypto/x509" + "encoding/pem" + "errors" + "os" + "strings" +) -func generateAesKey(key []byte) []byte { - genKey := make([]byte, 16) +func generateAesKey(key []byte, size int) []byte { + genKey := make([]byte, size) copy(genKey, key) - for i := 16; i < len(key); { - for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 { + for i := size; i < len(key); { + for j := 0; j < size && i < len(key); j, i = j+1, i+1 { genKey[j] ^= key[i] } } @@ -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 +} diff --git a/cryptor/crypto_test.go b/cryptor/crypto_test.go new file mode 100644 index 00000000..c44fa8b0 --- /dev/null +++ b/cryptor/crypto_test.go @@ -0,0 +1,234 @@ +package cryptor + +import ( + "crypto" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestAesEcbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key)) + aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestAesEcbCrypt") + assert.Equal(data, string(aesEcbDecrypt)) +} + +func TestAesCbcCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key)) + aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestAesCbcCrypt") + assert.Equal(data, string(aesCbcDecrypt)) +} + +func TestAesCtrCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + aesCtrCrypt := AesCtrEncrypt([]byte(data), []byte(key)) + aesCtrDeCrypt := AesCtrDecrypt(aesCtrCrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestAesCtrCrypt") + assert.Equal(data, string(aesCtrDeCrypt)) +} + +func TestAesCfbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key)) + aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestAesCfbCrypt") + assert.Equal(data, string(aesCfbDecrypt)) +} + +func TestAesOfbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key)) + aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestAesOfbEncrypt") + assert.Equal(data, string(aesOfbDecrypt)) +} + +func TestDesEcbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefgh" + + desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key)) + desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestDesEcbEncrypt") + assert.Equal(data, string(desEcbDecrypt)) +} + +func TestDesCbcCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefgh" + + desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key)) + desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestDesCbcEncrypt") + assert.Equal(data, string(desCbcDecrypt)) +} + +func TestDesCtrCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefgh" + + desCtrCrypt := DesCtrEncrypt([]byte(data), []byte(key)) + desCtrDeCrypt := DesCtrDecrypt(desCtrCrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestDesCtrCrypt") + assert.Equal(data, string(desCtrDeCrypt)) +} + +func TestDesCfbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefgh" + + desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key)) + desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestDesCfbEncrypt") + assert.Equal(data, string(desCfbDecrypt)) +} + +func TestDesOfbCrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefgh" + + desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key)) + desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key)) + + assert := internal.NewAssert(t, "TestDesOfbEncrypt") + assert.Equal(data, string(desOfbDecrypt)) +} + +func TestRsaEncrypt(t *testing.T) { + t.Parallel() + + err := GenerateRsaKey(4096, "./rsa_private_example.pem", "./rsa_public_example.pem") + if err != nil { + t.FailNow() + } + data := []byte("hello world") + encrypted := RsaEncrypt(data, "./rsa_public_example.pem") + decrypted := RsaDecrypt(encrypted, "./rsa_private_example.pem") + + assert := internal.NewAssert(t, "TestRsaEncrypt") + assert.Equal(string(data), string(decrypted)) +} + +func TestRsaEncryptOAEP(t *testing.T) { + assert := internal.NewAssert(t, "TestRsaEncrypt") + t.Parallel() + + pri, pub := GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := RsaEncryptOAEP(data, label, *pub) + assert.IsNil(err) + + decrypted, err := RsaDecryptOAEP([]byte(encrypted), label, *pri) + + assert.IsNil(err) + assert.Equal("hello world", string(decrypted)) +} + +func TestAesGcmEncrypt(t *testing.T) { + t.Parallel() + + data := "hello world" + key := "abcdefghijklmnop" + + encrypted := AesGcmEncrypt([]byte(data), []byte(key)) + decrypted := AesGcmDecrypt(encrypted, []byte(key)) + + assert := internal.NewAssert(t, "TestAesGcmEncrypt") + assert.Equal(data, string(decrypted)) +} + +func TestRsaSignAndVerify(t *testing.T) { + t.Parallel() + + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + t.Run("RSA Sign and Verify", func(t *testing.T) { + privateKey := "./rsa_private_example.pem" + publicKey := "./rsa_public_example.pem" + + signature, err := RsaSign(hash, data, privateKey) + if err != nil { + t.Fatalf("RsaSign failed: %v", err) + } + + err = RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + t.Fatalf("RsaVerifySign failed: %v", err) + } + }) + + t.Run("RSA Sign and Verify Invalid Signature", func(t *testing.T) { + publicKey := "./rsa_public_example.pem" + + invalidSig := []byte("InvalidSignature") + + err := RsaVerifySign(hash, data, invalidSig, publicKey) + if err == nil { + t.Fatalf("RsaVerifySign failed: %v", err) + } + }) + + t.Run("RSA Sign and Verify With Different Hash", func(t *testing.T) { + publicKey := "./rsa_public_example.pem" + privateKey := "./rsa_private_example.pem" + hashSign := crypto.SHA256 + hashVerify := crypto.SHA512 + + signature, err := RsaSign(hashSign, data, privateKey) + if err != nil { + t.Fatalf("RsaSign failed: %v", err) + } + + err = RsaVerifySign(hashVerify, data, signature, publicKey) + if err == nil { + t.Fatalf("RsaVerifySign failed: %v", err) + } + }) +} diff --git a/cryptor/des.go b/cryptor/des.go deleted file mode 100644 index 3c238b47..00000000 --- a/cryptor/des.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2021 dudaodong@gmail.com. All rights reserved. -// Use of this source code is governed by MIT license - -// Package cryptor implements some util functions to encrypt and decrypt. -package cryptor - -import ( - "bytes" - "crypto/cipher" - "crypto/des" - "crypto/rand" - "io" -) - -// DesEcbEncrypt encrypt data with key use DES ECB algorithm -// len(key) should be 8 -func DesEcbEncrypt(data, key []byte) []byte { - cipher, _ := des.NewCipher(generateDesKey(key)) - 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 - } - - encrypted := make([]byte, len(plain)) - 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]) - } - - return encrypted -} - -// DesEcbDecrypt decrypt data with key use DES ECB algorithm -// len(key) should be 8 -func DesEcbDecrypt(encrypted, key []byte) []byte { - cipher, _ := des.NewCipher(generateDesKey(key)) - 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]) - } - - trim := 0 - if len(decrypted) > 0 { - trim = len(decrypted) - int(decrypted[len(decrypted)-1]) - } - - return decrypted[:trim] -} - -// DesCbcEncrypt encrypt data with key use DES CBC algorithm -// len(key) should be 8 -func DesCbcEncrypt(data, key []byte) []byte { - block, _ := des.NewCipher(key) - blockSize := block.BlockSize() - data = pkcs7Padding(data, blockSize) - blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) - - encrypted := make([]byte, len(data)) - blockMode.CryptBlocks(encrypted, data) - - return encrypted -} - -// DesCbcDecrypt decrypt data with key use DES CBC algorithm -// len(key) should be 8 -func DesCbcDecrypt(encrypted, key []byte) []byte { - block, _ := des.NewCipher(key) - blockSize := block.BlockSize() - blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) - - decrypted := make([]byte, len(encrypted)) - blockMode.CryptBlocks(decrypted, encrypted) - decrypted = pkcs7UnPadding(decrypted) - - return decrypted -} - -// DesCtrCrypt encrypt data with key use DES CTR algorithm -// len(key) should be 8 -func DesCtrCrypt(data, key []byte) []byte { - block, _ := des.NewCipher(key) - - iv := bytes.Repeat([]byte("1"), block.BlockSize()) - stream := cipher.NewCTR(block, iv) - - dst := make([]byte, len(data)) - stream.XORKeyStream(dst, data) - - return dst -} - -// DesCfbEncrypt encrypt data with key use DES CFB algorithm -// len(key) should be 8 -func DesCfbEncrypt(data, key []byte) []byte { - block, err := des.NewCipher(key) - if err != nil { - panic(err) - } - - encrypted := make([]byte, des.BlockSize+len(data)) - iv := encrypted[:des.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - panic(err) - } - - stream := cipher.NewCFBEncrypter(block, iv) - stream.XORKeyStream(encrypted[des.BlockSize:], data) - - return encrypted -} - -// DesCfbDecrypt decrypt data with key use DES CFB algorithm -// len(encrypted) should be great than 16, len(key) should be 8 -func DesCfbDecrypt(encrypted, key []byte) []byte { - block, _ := des.NewCipher(key) - if len(encrypted) < des.BlockSize { - panic("encrypted data is too short") - } - iv := encrypted[:des.BlockSize] - encrypted = encrypted[des.BlockSize:] - - stream := cipher.NewCFBDecrypter(block, iv) - stream.XORKeyStream(encrypted, encrypted) - - return encrypted -} - -// DesOfbEncrypt encrypt data with key use DES OFB algorithm -// len(key) should be 16, 24 or 32 -func DesOfbEncrypt(data, key []byte) []byte { - block, err := des.NewCipher(key) - if err != nil { - panic(err) - } - data = pkcs7Padding(data, des.BlockSize) - encrypted := make([]byte, des.BlockSize+len(data)) - iv := encrypted[:des.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - panic(err) - } - - stream := cipher.NewOFB(block, iv) - stream.XORKeyStream(encrypted[des.BlockSize:], data) - - return encrypted -} - -// DesOfbDecrypt decrypt data with key use DES OFB algorithm -// len(key) should be 8 -func DesOfbDecrypt(data, key []byte) []byte { - block, err := des.NewCipher(key) - if err != nil { - panic(err) - } - - iv := data[:des.BlockSize] - data = data[des.BlockSize:] - if len(data)%des.BlockSize != 0 { - return nil - } - - decrypted := make([]byte, len(data)) - mode := cipher.NewOFB(block, iv) - mode.XORKeyStream(decrypted, data) - - decrypted = pkcs7UnPadding(decrypted) - - return decrypted -} diff --git a/cryptor/des_test.go b/cryptor/des_test.go deleted file mode 100644 index 229fd410..00000000 --- a/cryptor/des_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cryptor - -import ( - "testing" - - "github.com/duke-git/lancet/internal" -) - -func TestDesEcbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefgh" - - desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key)) - desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestDesEcbEncrypt") - assert.Equal(data, string(desEcbDecrypt)) -} - -func TestDesCbcEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefgh" - - desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key)) - desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestDesCbcEncrypt") - assert.Equal(data, string(desCbcDecrypt)) -} - -func TestDesCtrCrypt(t *testing.T) { - data := "hello world" - key := "abcdefgh" - - desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key)) - desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestDesCtrCrypt") - assert.Equal(data, string(desCtrDeCrypt)) -} - -func TestDesCfbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefgh" - - desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key)) - desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestDesCfbEncrypt") - assert.Equal(data, string(desCfbDecrypt)) -} - -func TestDesOfbEncrypt(t *testing.T) { - data := "hello world" - key := "abcdefgh" - - desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key)) - desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key)) - - assert := internal.NewAssert(t, "TestDesOfbEncrypt") - assert.Equal(data, string(desOfbDecrypt)) -} diff --git a/cryptor/rsa.go b/cryptor/rsa.go deleted file mode 100644 index 4b8440ab..00000000 --- a/cryptor/rsa.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2021 dudaodong@gmail.com. All rights reserved. -// Use of this source code is governed by MIT license - -// Package cryptor implements some util functions to encrypt and decrypt. -package cryptor - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "os" -) - -// GenerateRsaKey make a rsa private key, and return key file name -// Generated key file is `rsa_private.pem` and `rsa_public.pem` in current path -func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error { - // private key - privateKey, err := rsa.GenerateKey(rand.Reader, keySize) - if err != nil { - return err - } - - derText := x509.MarshalPKCS1PrivateKey(privateKey) - - block := pem.Block{ - Type: "rsa private key", - Bytes: derText, - } - - //file,err := os.Create("rsa_private.pem") - file, err := os.Create(priKeyFile) - if err != nil { - panic(err) - } - pem.Encode(file, &block) - file.Close() - - // public key - publicKey := privateKey.PublicKey - - derpText, err := x509.MarshalPKIXPublicKey(&publicKey) - if err != nil { - return err - } - - block = pem.Block{ - Type: "rsa public key", - Bytes: derpText, - } - - //file,err = os.Create("rsa_public.pem") - file, err = os.Create(pubKeyFile) - if err != nil { - return err - } - pem.Encode(file, &block) - file.Close() - - return nil -} - -// RsaEncrypt encrypt data with ras algorithm -func RsaEncrypt(data []byte, pubKeyFileName string) []byte { - file, err := os.Open(pubKeyFileName) - if err != nil { - panic(err) - } - fileInfo, err := file.Stat() - if err != nil { - panic(err) - } - defer file.Close() - buf := make([]byte, fileInfo.Size()) - file.Read(buf) - - block, _ := pem.Decode(buf) - - pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - panic(err) - } - pubKey := pubInterface.(*rsa.PublicKey) - - cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data) - if err != nil { - panic(err) - } - return cipherText -} - -// RsaDecrypt decrypt data with ras algorithm -func RsaDecrypt(data []byte, privateKeyFileName string) []byte { - file, err := os.Open(privateKeyFileName) - if err != nil { - panic(err) - } - fileInfo, err := file.Stat() - if err != nil { - panic(err) - } - buf := make([]byte, fileInfo.Size()) - defer file.Close() - file.Read(buf) - - block, _ := pem.Decode(buf) - - priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - panic(err) - } - - plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data) - if err != nil { - panic(err) - } - return plainText -} diff --git a/cryptor/rsa_private.pem b/cryptor/rsa_private.pem new file mode 100644 index 00000000..da4ad650 --- /dev/null +++ b/cryptor/rsa_private.pem @@ -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----- diff --git a/cryptor/rsa_private_example.pem b/cryptor/rsa_private_example.pem new file mode 100644 index 00000000..408169ab --- /dev/null +++ b/cryptor/rsa_private_example.pem @@ -0,0 +1,51 @@ +-----BEGIN rsa private key----- +MIIJKAIBAAKCAgEAw0anfgtraA2uaZwoLpLBvo1EkfYvDBgeXoMQ4WMKbcw6jU8k +18E+f3WM52I2RssEk6g0X1vIiarHZU5qyxbv2iNoT7EfgizzlXYvx06pM69GNBQr +V46+lNhiOv4eeLZcOHBnxAyizlIKyLuwO+C1cX/6BxuXjX3ogw+6IaBFZN/EOMmT +Wc6sPKrnNCEqgCNiTbPAXb+N8j5Iv8QPs0AwVTB3jC9LP0mRt2GGD0cu+QIZkoGE +hyW9Dd+0tgeIK32uXCF8uDjNpUV1TxCdn5H+lvgddxfiaIJAdY5UaTx48XRIdULh +QrVKXJNyTYWTXbezViYIf1nXOdDI2hsGgKTfNVCocDT1LcT5zebijFHVCWfAAKEP +RLdzPZomkFIa3rQQgChgePzE8Oqsaxuwx8sU09NAo3giSq1OmBBwly7h60Lwtm8+ +XuwmWtEgZoSQy2Hm8UMAJ3guotfYuS8mPQBi55JZpdqVN0D5SdwuWXhHpO4xDodN +YAoMMei1NNgrX2zN3FCS8PVi97wD6cQ8xsWM62nByrzIjmPwTfoCb3qP/928FMJM +g7b3bYBrUnOrpVWZfAIDs4H7DoP8VLL4rMgB5PuWNQ13gtX7y5ZOyLhopiKLnar9 +XiN8GAMANBdKRI2anGEozrfJoelJ5POXZwSatRjbL+nWO3YnBzEunCw1xmMCAwEA +AQKCAgBAYYABP3SW5sPVD+XzjPERiPPNh7P1MdJ5aI7dMFEU6Bt50VkdRRn83d2p +v6iTaIXGxNMXiWQxdzusO9FbyeEkMz5F3+i6e2WHpmKUPGvunV/w9aFgibBt1HV2 +a6fSNpVrCiw758qZaVUi3zZ4V1qa5A2j4EX0IUnSRBIi2ftnCZtg+Zx6JHiGu/Xk +KvcfLgtQAO5wOiJrdnt3tgVTHNuSipsvfbw6TmAbbKzNRrPG5xlVQxxVjmypMVMc +HJmZdSNSPrwm5JtwXNkTSzAclv6v+XeFdztvJ1pnJ5jO5WAegy8MchNgcfLlWLt7 +sYlngZQ/1+Q/UHh0GFDQD87yBOmNz8KK5n+5gWB5iumdJ4BHTgUOfXpWilwb0JWG +r7ctqCYrbXXTvsIbRl47zGPzEsbs0mSLAuLzZ3IQ60uaYRt322bqzZQNBwJcUXYM +lRb6nc9BVAzqhvUemOlACbYlqXENmQy/Nz14nsNdy4Qrynsfon92dRZ0m+9rJ9Hj +99K4CNPz0FdayC7jTL76b8QEzoF2MGiKIL5yQYXm9Pt9p0g8g9sER7G7UyrMFqtl +tfylkAWRX5hgDCwQQ/Gqefn7xb/kG/4D1FBE5i2yU4tYw5NCzENo8Y3mUhBqiQql +G33sSv2JK/woxRWSbyGLfu1Iq2L8H/q4CdN55xat2iKbpL8omQKCAQEA6qX7V8A0 +uCg0E/Uid6/Xma0dvZ7e5BMKcx0uElSUhUpB6JnEQRlgljRXF/Op8Iae09Gox6bZ +nU5Ow61wtSrnJY+f/2RVs9xYB+SSO0L0yE/XPKsBveH0dH9R4BWmH+KZ3sLaYovs +ZDsApR782Zh+1TthUT2s4vZ0G25f46xsjKpUzQLmgWeC3UEOThtQo/UZzLeprImI +fijMw+5jYUgHSXN80BXO56JzHQJU6SIDmA4BrlD0qPaDyzLVdNhG/nIbYKvf120p +ogWqEYIgVN4KyjLsvVgfxCEF4Ucwov9VCNgsVTlEtYWzAXEXqf2AW1j7Sh4GlVOz +W4UsfiGaSCjM7wKCAQEA1QuDLQ4cf4UEKsdPOnDYrkHwx6JBBHpkyZZcLhlT/S5A +AcvVcEJjeseNfCEexO7SChenlRQEVB8iXO28ZEumWePEz2JK9rq9p7AItk+ba/D9 +qzfvZ/XE+1xs5szTfwr12Of8b9dXxhoW8gKcFPnKOHxvua6SyocmRlnZtaJRFZ7x +RxOZhfWoOUnc+ySYKhKyuipKR4KmyDd2d2ovxptlMFnj2RJzfjUIZiQpKTa8kXf7 +sYaOgFiNC0AFAs9ZLCEX3NYTKpgVbVKNIaKtNj8GIAG2YPnT/VcbQtj9ULyJcvEw +IdzJXn+Cv6ie1nP05P+eo/gtGmm5okXzMQNv0wcFzQKCAQBmDVBWJtMG8P1NXMTj +1wdm3+LacHkyKpHV5O//qud5XQVzO0UepwHZ8eObGC9l27bCGyJTyt5ESyV4dztY +n9MuA9wrQCEB+6gRrrhmq8U4RXkv+pPkWJxv+lvKoL/CiFQxjP9b8s0Z/otWRTbl +ECzBYnT911wUzelLcOKla30+ZGpDS6qixzkkL0IgeELHPDc/UPWrg5lofSgpYsm4 +KpJ4wJCdE48MMRvtlvEE//UeMaFLhgwSXDyPqIkrq1CdI1WC4t2UnPaJb/s6aCTV +pEh/DkzmQKh4LYCYLNUbXv9FvHbzjdezNvXWf7AyD32+vOF1p79nPKL5/96M8OJf +1dbjAoIBABKld02yNnxSwBKebyjGR7C4xMI0SUyDCd868cZ3IQq/yYpetMemh95v +KMr8exzxaiDIATrjDZ3vO6q2hA6jMGQds1QTXkxJ+995YMnUHd5MsWcS9jk7IYp+ +hGmO89PiubHKXCXNyzjjf66e29paIoDfI0g1J1PikE8H/i4Pjtk9mBCIfp9i6N5a +wKSah1bnXA0/NlEb9kz/zbaV7KiNYUXiGDcfjkw1iA6oi5G34Lk6ryTSihZhqbaa +W9XrH/rkypnhgrvvo7B10TRocJCW44pZnATQ2OULgq9PHpy6Y61Tvsq38Ef9EQyF +TaGndH+2f8QKLKhrKHwzcx2PF3J44uECggEBAM0UGu/Aj4tIRmrcuPGHypkcxMY6 +BS2irwiVD9/8Xnx0/r8RSnBuAXEUY8wTrP0GqGm9PZjFCXKyxk3gi6SkahTu6/SF +WecgomVnONI+ivpHRLmRXTTPEv7iu1F+2jgVQyg0mOR5WLE0r25S6NS6IlnnrTSo +QuIJa1wRIfyXrMpYk77YIOny+mYB4FYr25tChgieQGR4m3dlZICPYqOyFh9GORZ8 +k1cVboGtKGYtAemzAh/PyUp716tMz44fnnHPzINUFI3ucybqUwpGiR9s0E3L+GsV +3h7a2v90RdyWcuAPJL0B5FL5NoHhOMYb1rCMu00FyqCKqXCgte2w2psOP60= +-----END rsa private key----- diff --git a/cryptor/rsa_public.pem b/cryptor/rsa_public.pem new file mode 100644 index 00000000..95abd403 --- /dev/null +++ b/cryptor/rsa_public.pem @@ -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----- diff --git a/cryptor/rsa_public_example.pem b/cryptor/rsa_public_example.pem new file mode 100644 index 00000000..a0994612 --- /dev/null +++ b/cryptor/rsa_public_example.pem @@ -0,0 +1,14 @@ +-----BEGIN rsa public key----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw0anfgtraA2uaZwoLpLB +vo1EkfYvDBgeXoMQ4WMKbcw6jU8k18E+f3WM52I2RssEk6g0X1vIiarHZU5qyxbv +2iNoT7EfgizzlXYvx06pM69GNBQrV46+lNhiOv4eeLZcOHBnxAyizlIKyLuwO+C1 +cX/6BxuXjX3ogw+6IaBFZN/EOMmTWc6sPKrnNCEqgCNiTbPAXb+N8j5Iv8QPs0Aw +VTB3jC9LP0mRt2GGD0cu+QIZkoGEhyW9Dd+0tgeIK32uXCF8uDjNpUV1TxCdn5H+ +lvgddxfiaIJAdY5UaTx48XRIdULhQrVKXJNyTYWTXbezViYIf1nXOdDI2hsGgKTf +NVCocDT1LcT5zebijFHVCWfAAKEPRLdzPZomkFIa3rQQgChgePzE8Oqsaxuwx8sU +09NAo3giSq1OmBBwly7h60Lwtm8+XuwmWtEgZoSQy2Hm8UMAJ3guotfYuS8mPQBi +55JZpdqVN0D5SdwuWXhHpO4xDodNYAoMMei1NNgrX2zN3FCS8PVi97wD6cQ8xsWM +62nByrzIjmPwTfoCb3qP/928FMJMg7b3bYBrUnOrpVWZfAIDs4H7DoP8VLL4rMgB +5PuWNQ13gtX7y5ZOyLhopiKLnar9XiN8GAMANBdKRI2anGEozrfJoelJ5POXZwSa +tRjbL+nWO3YnBzEunCw1xmMCAwEAAQ== +-----END rsa public key----- diff --git a/cryptor/rsa_test.go b/cryptor/rsa_test.go deleted file mode 100644 index 1a87c848..00000000 --- a/cryptor/rsa_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package cryptor - -import ( - "testing" - - "github.com/duke-git/lancet/internal" -) - -func TestRsaEncrypt(t *testing.T) { - err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - t.FailNow() - } - data := []byte("hello world") - encrypted := RsaEncrypt(data, "rsa_public.pem") - decrypted := RsaDecrypt(encrypted, "rsa_private.pem") - - assert := internal.NewAssert(t, "TestRsaEncrypt") - assert.Equal(string(data), string(decrypted)) -} diff --git a/datastructure/hashmap/hashmap.go b/datastructure/hashmap/hashmap.go new file mode 100644 index 00000000..b8e7f3dd --- /dev/null +++ b/datastructure/hashmap/hashmap.go @@ -0,0 +1,217 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure implements some data structure. hashmap structure. +package datastructure + +import ( + "fmt" + "hash/fnv" + "reflect" +) + +var defaultMapCapacity uint64 = 1 << 10 + +type mapNode struct { + key any + value any + next *mapNode +} + +// HashMap implements a hash map +type HashMap struct { + capacity uint64 + size uint64 + table []*mapNode +} + +// NewHashMap return a HashMap instance +func NewHashMap() *HashMap { + return &HashMap{ + capacity: defaultMapCapacity, + table: make([]*mapNode, defaultMapCapacity), + } +} + +// NewHashMapWithCapacity return a HashMap instance with given size and capacity +func NewHashMapWithCapacity(size, capacity uint64) *HashMap { + return &HashMap{ + size: size, + capacity: capacity, + table: make([]*mapNode, capacity), + } +} + +// Get return the value of given key in hashmap +func (hm *HashMap) Get(key any) any { + hashValue := hm.hash(key) + node := hm.table[hashValue] + for node != nil { + if reflect.DeepEqual(node.key, key) { + return node.value + } + node = node.next + } + return nil +} + +// GetOrDefault return the value of given key in hashmap, if not found return default value +func (hm *HashMap) GetOrDefault(key any, defaultValue any) any { + value := hm.Get(key) + if value == nil { + return defaultValue + } + return value +} + +// Put new key value in hashmap +func (hm *HashMap) Put(key any, value any) { + hm.putValue(hm.hash(key), key, value) +} + +func (hm *HashMap) putValue(hash uint64, key, value any) { + if hm.capacity == 0 { + hm.capacity = defaultMapCapacity + hm.table = make([]*mapNode, defaultMapCapacity) + } + + node := hm.table[hash] + if node == nil { + hm.table[hash] = newMapNode(key, value) + } else if node.key == key { + hm.table[hash] = newMapNodeWithNext(key, value, node) + } else { + hm.resize() + hm.putValue(hash, value, value) + } + hm.size++ +} + +// Delete item by given key in hashmap +func (hm *HashMap) Delete(key any) { + hash := hm.hash(key) + node := hm.table[hash] + if node == nil { + return + } + + hm.table = append(hm.table[:hash], hm.table[hash+1:]...) + hm.size-- +} + +// Contains checks if given key is in hashmap or not +func (hm *HashMap) Contains(key any) bool { + node := hm.table[hm.hash(key)] + for node != nil { + if reflect.DeepEqual(node.key, key) { + return true + } + node = node.next + } + return false +} + +// Iterate executes iteratee funcation for every key and value pair of hashmap (random order) +func (hm *HashMap) Iterate(iteratee func(key, value any)) { + if hm.size > 0 { + for i := 0; i < len(hm.table); i++ { + item := hm.table[i] + if item != nil { + iteratee(item.key, item.value) + } + } + } +} + +// FilterByValue returns a filtered HashMap. +// If any value is not matching the perdicate function then it returns nil +// otherwise it returns the HashMap with selected values. +func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap { + var filteredHM *HashMap + if hm.size > 0 { + for i := 0; i < len(hm.table); i++ { + item := hm.table[i] + if item != nil && perdicate(item.value) { + if filteredHM == nil { + filteredHM = NewHashMap() + } + filteredHM.Put(item.key, item.value) + } + } + } + return filteredHM +} + +// Keys returns a slice of the hashmap's keys (random order) +func (hm *HashMap) Keys() []any { + keys := make([]any, int(hm.size)) + index := 0 + if hm.size > 0 { + hm.Iterate(func(key, value any) { + keys[index] = key + index++ + }) + } + + return keys +} + +// Values returns a slice of the hashmap's keys (random order) +func (hm *HashMap) Values() []any { + values := make([]any, int(hm.size)) + index := 0 + if hm.size > 0 { + hm.Iterate(func(key, value any) { + values[index] = value + index++ + }) + } + + return values +} + +func (hm *HashMap) resize() { + hm.capacity <<= 1 + + tempTable := hm.table + + hm.table = make([]*mapNode, hm.capacity) + + for i := 0; i < len(tempTable); i++ { + node := tempTable[i] + if node == nil { + continue + } + + hm.table[hm.hash(node.key)] = node + } +} + +// Size returns current size of Hashmap +func (hm *HashMap) Size() uint64 { + return hm.size +} + +func (hm *HashMap) hash(key any) uint64 { + h := fnv.New64a() + _, _ = h.Write([]byte(fmt.Sprintf("%v", key))) + + hashValue := h.Sum64() + + return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16)) +} + +func newMapNode(key, value any) *mapNode { + return &mapNode{ + key: key, + value: value, + } +} + +func newMapNodeWithNext(key, value any, next *mapNode) *mapNode { + return &mapNode{ + key: key, + value: value, + next: next, + } +} diff --git a/datastructure/hashmap/hashmap_test.go b/datastructure/hashmap/hashmap_test.go new file mode 100644 index 00000000..66ebe2bd --- /dev/null +++ b/datastructure/hashmap/hashmap_test.go @@ -0,0 +1,128 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestHashMap_PutAndGet(t *testing.T) { + assert := internal.NewAssert(t, "TestHashMap_PutAndGet") + + hm := NewHashMap() + + hm.Put("abc", 3) + assert.Equal(3, hm.Get("abc")) + assert.IsNil(hm.Get("abcd")) + + hm.Put("abc", 4) + assert.Equal(4, hm.Get("abc")) +} + +func TestHashMap_Resize(t *testing.T) { + assert := internal.NewAssert(t, "TestHashMap_Resize") + + hm := NewHashMapWithCapacity(3, 3) + + for i := 0; i < 20; i++ { + hm.Put(i, 10) + } + + assert.Equal(10, hm.Get(5)) +} + +func TestHashMap_Delete(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_Delete") + + hm := NewHashMap() + + hm.Put("abc", 3) + assert.Equal(3, hm.Get("abc")) + + hm.Delete("abc") + assert.IsNil(hm.Get("abc")) +} + +func TestHashMap_Contains(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_Contains") + + hm := NewHashMap() + assert.Equal(false, hm.Contains("abc")) + + hm.Put("abc", 3) + assert.Equal(true, hm.Contains("abc")) +} + +func TestHashMap_KeysValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_KeysValues") + + hm := NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + keys := hm.Keys() + values := hm.Values() + + assert.Equal(3, len(values)) + assert.Equal(3, len(keys)) +} + +func TestHashMap_Keys(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_Keys") + + hm := NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + keys := hm.Keys() + + assert.Equal(3, len(keys)) +} + +func TestHashMap_GetOrDefault(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_GetOrDefault") + + hm := NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + assert.Equal(1, hm.GetOrDefault("a", 5)) + assert.Equal(5, hm.GetOrDefault("d", 5)) +} + +func TestHashMap_FilterByValue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHashMap_FilterByValue") + + hm := NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + hm.Put("d", 4) + hm.Put("e", 5) + hm.Put("f", 6) + + filteredHM := hm.FilterByValue(func(value any) bool { + return value.(int) == 1 || value.(int) == 3 + }) + + assert.Equal(uint64(2), filteredHM.Size()) +} diff --git a/datastructure/heap/maxheap.go b/datastructure/heap/maxheap.go new file mode 100644 index 00000000..e913c68d --- /dev/null +++ b/datastructure/heap/maxheap.go @@ -0,0 +1,203 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure implements some data structure. MaxHeap is a binary max heap. +package datastructure + +import ( + "fmt" + + "github.com/duke-git/lancet/v2/constraints" +) + +// MaxHeap implements a binary max heap +// type T should implements Compare function in constraints.Comparator interface. +type MaxHeap[T any] struct { + data []T + comparator constraints.Comparator +} + +// NewMaxHeap returns a MaxHeap instance with the given comparator. +func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T] { + return &MaxHeap[T]{ + data: make([]T, 0), + comparator: comparator, + } +} + +// BuildMaxHeap builds a MaxHeap instance with data and given comparator. +func BuildMaxHeap[T any](data []T, comparator constraints.Comparator) *MaxHeap[T] { + heap := &MaxHeap[T]{ + data: make([]T, 0, len(data)), + comparator: comparator, + } + + for _, v := range data { + heap.Push(v) + } + + return heap +} + +// Push value into the heap +func (h *MaxHeap[T]) Push(value T) { + h.data = append(h.data, value) + h.heapifyUp(len(h.data) - 1) +} + +// heapifyUp heapify the data from bottom to top +func (h *MaxHeap[T]) heapifyUp(i int) { + for h.comparator.Compare(h.data[parentIndex(i)], h.data[i]) < 0 { + h.swap(parentIndex(i), i) + i = parentIndex(i) + } +} + +// Pop return the largest value, and remove it from the heap +// if heap is empty, return zero value and fasle +func (h *MaxHeap[T]) Pop() (T, bool) { + var val T + if h.Size() == 0 { + return val, false + } + + val = h.data[0] + l := len(h.data) - 1 + + h.data[0] = h.data[l] + h.data = h.data[:l] + h.heapifyDown(0) + + return val, true +} + +// heapifyDown heapify the data from top to bottom +func (h *MaxHeap[T]) heapifyDown(i int) { + lastIndex := len(h.data) - 1 + l, r := leftChildIndex(i), rightChildIndex(i) + childToCompare := 0 + + for l <= lastIndex { + if l == lastIndex { + childToCompare = l + } else if h.comparator.Compare(h.data[l], h.data[r]) > 0 { + childToCompare = l + } else { + childToCompare = r + } + + if h.comparator.Compare(h.data[i], h.data[childToCompare]) < 0 { + h.swap(i, childToCompare) + i = childToCompare + l, r = leftChildIndex(i), rightChildIndex(i) + } else { + break + } + } +} + +// Peek returns the largest element from the heap without removing it. +// if heap is empty, it returns zero value and false. +func (h *MaxHeap[T]) Peek() (T, bool) { + if h.Size() == 0 { + var val T + return val, false + } + + return h.data[0], true +} + +// Size return the number of elements in the heap +func (h *MaxHeap[T]) Size() int { + return len(h.data) +} + +// Data return data of the heap +func (h *MaxHeap[T]) Data() []T { + return h.data +} + +// PrintStructure print the structure of the heap +func (h *MaxHeap[T]) PrintStructure() { + level := 1 + data := h.data + length := len(h.data) + index := 0 + + list := [][]string{} + temp := []string{} + for index < length { + start := powerTwo(level-1) - 1 + end := start + powerTwo(level-1) - 1 + + temp = append(temp, fmt.Sprintf("%v", data[index])) + index++ + + if index > end || index >= length { + list = append(list, temp) + temp = []string{} + + if index < length { + level++ + } + } + } + + lastNum := powerTwo(level - 1) + lastLen := lastNum + (lastNum - 1) + + heapTree := make([][]string, level) + for i := 0; i < level; i++ { + heapTree[i] = make([]string, lastLen) + for j := 0; j < lastLen; j++ { + heapTree[i][j] = "" + } + } + + for k := 0; k < len(list); k++ { + vals := list[k] + tempLevel := level - k + st := powerTwo(tempLevel-1) - 1 + for _, v := range vals { + heapTree[k][st] = v + gap := powerTwo(tempLevel) + st = st + gap + } + } + + for m := 0; m < level; m++ { + for n := 0; n < lastLen; n++ { + val := heapTree[m][n] + if val == "" { + fmt.Print(" ") + } else { + fmt.Print(val) + } + } + fmt.Println() + } +} + +// parentIndex get parent index of the given index +func parentIndex(i int) int { + return (i - 1) / 2 +} + +// leftChildIndex get left child index of the given index +func leftChildIndex(i int) int { + return 2*i + 1 +} + +// rightChildIndex get right child index of the given index +func rightChildIndex(i int) int { + return 2*i + 2 +} + +// swap two elements in the heap +func (h *MaxHeap[T]) swap(i, j int) { + h.data[i], h.data[j] = h.data[j], h.data[i] +} + +func powerTwo(n int) int { + return 1 << n +} diff --git a/datastructure/heap/maxheap_test.go b/datastructure/heap/maxheap_test.go new file mode 100644 index 00000000..82277112 --- /dev/null +++ b/datastructure/heap/maxheap_test.go @@ -0,0 +1,98 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func TestMaxHeap_BuildMaxHeap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMaxHeap_BuildMaxHeap") + + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + heap := BuildMaxHeap(values, &intComparator{}) + + expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} + assert.Equal(expected, heap.data) + + assert.Equal(12, heap.Size()) +} + +func TestMaxHeap_Push(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMaxHeap_Push") + + heap := NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + heap.Push(v) + } + + expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} + assert.Equal(expected, heap.data) + + assert.Equal(12, heap.Size()) + + heap.PrintStructure() +} + +func TestMaxHeap_Pop(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMaxHeap_Pop") + + heap := NewMaxHeap[int](&intComparator{}) + + _, ok := heap.Pop() + assert.Equal(false, ok) + + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + for _, v := range values { + heap.Push(v) + } + + val, ok := heap.Pop() + assert.Equal(12, val) + assert.Equal(true, ok) + assert.Equal(11, heap.Size()) +} + +func TestMaxHeap_Peek(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMaxHeap_Peek") + + heap := NewMaxHeap[int](&intComparator{}) + + _, ok := heap.Peek() + assert.Equal(false, ok) + + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + for _, v := range values { + heap.Push(v) + } + + val, ok := heap.Peek() + assert.Equal(12, val) + assert.Equal(true, ok) + + assert.Equal(12, heap.Size()) +} diff --git a/datastructure/link/doublylink.go b/datastructure/link/doublylink.go new file mode 100644 index 00000000..a413deb7 --- /dev/null +++ b/datastructure/link/doublylink.go @@ -0,0 +1,242 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink. +package datastructure + +import ( + "fmt" + + "github.com/duke-git/lancet/v2/datastructure" +) + +// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl. +type DoublyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} + +// NewDoublyLink return *DoublyLink instance +func NewDoublyLink[T any]() *DoublyLink[T] { + return &DoublyLink[T]{Head: nil} +} + +// InsertAtHead insert value into doubly linklist at head index +func (dl *DoublyLink[T]) InsertAtHead(value T) { + newNode := datastructure.NewLinkNode(value) + size := dl.Size() + + if size == 0 { + dl.Head = newNode + dl.length++ + return + } + + newNode.Next = dl.Head + newNode.Pre = nil + + dl.Head.Pre = newNode + dl.Head = newNode + + dl.length++ +} + +// InsertAtTail insert value into doubly linklist at tail index +func (dl *DoublyLink[T]) InsertAtTail(value T) { + current := dl.Head + if current == nil { + dl.InsertAtHead(value) + return + } + + for current.Next != nil { + current = current.Next + } + + newNode := datastructure.NewLinkNode(value) + newNode.Next = nil + newNode.Pre = current + current.Next = newNode + + dl.length++ +} + +// InsertAt insert value into doubly linklist at index +// param `index` should between [0, length], if index do not meet the conditions, do nothing +func (dl *DoublyLink[T]) InsertAt(index int, value T) { + size := dl.length + if index < 0 || index > size { + return + } + + if index == 0 { + dl.InsertAtHead(value) + return + } + + if index == size { + dl.InsertAtTail(value) + return + } + + i := 0 + current := dl.Head + + for current != nil { + if i == index-1 { + newNode := datastructure.NewLinkNode(value) + newNode.Next = current.Next + newNode.Pre = current + + current.Next = newNode + dl.length++ + + return + } + i++ + current = current.Next + } +} + +// DeleteAtHead delete value in doubly linklist at head index +func (dl *DoublyLink[T]) DeleteAtHead() { + if dl.Head == nil { + return + } + + current := dl.Head + dl.Head = current.Next + dl.Head.Pre = nil + dl.length-- +} + +// DeleteAtTail delete value in doubly linklist at tail +func (dl *DoublyLink[T]) DeleteAtTail() { + if dl.Head == nil { + return + } + + current := dl.Head + if current.Next == nil { + dl.DeleteAtHead() + } + + for current.Next.Next != nil { + current = current.Next + } + + current.Next = nil + dl.length-- +} + +// DeleteAt delete value in doubly linklist at index +// param `index` should be [0, len(DoublyLink)-1] +func (dl *DoublyLink[T]) DeleteAt(index int) { + if dl.Head == nil { + return + } + + current := dl.Head + if current.Next == nil || index == 0 { + dl.DeleteAtHead() + } + + if index == dl.length-1 { + dl.DeleteAtTail() + } + + if index < 0 || index > dl.length-1 { + return + } + + i := 0 + for current != nil { + if i == index-1 { + current.Next = current.Next.Next + dl.length-- + return + } + i++ + current = current.Next + } +} + +// Reverse the linked list +func (dl *DoublyLink[T]) Reverse() { + current := dl.Head + var temp *datastructure.LinkNode[T] + + for current != nil { + temp = current.Pre + current.Pre = current.Next + current.Next = temp + current = current.Pre + } + + if temp != nil { + dl.Head = temp.Pre + } +} + +// GetMiddleNode return node at middle index of linked list +func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { + if dl.Head == nil { + return nil + } + if dl.Head.Next == nil { + return dl.Head + } + fast := dl.Head + slow := dl.Head + + for fast != nil { + fast = fast.Next + + if fast != nil { + fast = fast.Next + slow = slow.Next + } else { + return slow + } + } + return slow +} + +// Size return the count of doubly linked list +func (dl *DoublyLink[T]) Size() int { + return dl.length +} + +// Values return slice of all doubly linklist node value +func (dl *DoublyLink[T]) Values() []T { + result := make([]T, 0, dl.length) + current := dl.Head + for current != nil { + result = append(result, current.Value) + current = current.Next + } + return result +} + +// Print all nodes info of a linked list +func (dl *DoublyLink[T]) Print() { + current := dl.Head + info := "[ " + for current != nil { + info += fmt.Sprintf("%+v, ", current) + current = current.Next + } + info += " ]" + fmt.Println(info) +} + +// IsEmpty checks if dl is empty or not +func (dl *DoublyLink[T]) IsEmpty() bool { + return dl.length == 0 +} + +// Clear all nodes in doubly linklist +func (dl *DoublyLink[T]) Clear() { + dl.Head = nil + dl.length = 0 +} diff --git a/datastructure/link/doublylink_test.go b/datastructure/link/doublylink_test.go new file mode 100644 index 00000000..e5f7219f --- /dev/null +++ b/datastructure/link/doublylink_test.go @@ -0,0 +1,181 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestDoublyLink_InsertAtFirst(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_InsertAtFirst") + + link := NewDoublyLink[int]() + link.InsertAtHead(1) + link.InsertAtHead(2) + link.InsertAtHead(3) + link.Print() + + expected := []int{3, 2, 1} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_InsertAtTail(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_InsertAtTail") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.Print() + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_InsertAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_InsertAt") + + link := NewDoublyLink[int]() + link.InsertAt(1, 1) //do nothing + link.InsertAt(0, 1) + link.InsertAt(1, 2) + link.InsertAt(2, 4) + link.InsertAt(2, 3) + + expected := []int{1, 2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_DeleteAtHead(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead") + + link := NewDoublyLink[int]() + link.DeleteAtHead() + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtHead() + + expected := []int{2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_DeleteAtTail(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail") + + link := NewDoublyLink[int]() + link.DeleteAtTail() + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtTail() + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_DeleteAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt") + + link := NewDoublyLink[int]() + link.DeleteAt(0) + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + link.InsertAtTail(5) + + link.DeleteAt(0) + assert.Equal([]int{2, 3, 4, 5}, link.Values()) + + link.DeleteAt(3) + assert.Equal([]int{2, 3, 4}, link.Values()) + + link.DeleteAt(1) + assert.Equal(2, link.Size()) + assert.Equal([]int{2, 4}, link.Values()) +} + +func TestDoublyLink_Reverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_Reverse") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.Reverse() + link.Print() + assert.Equal([]int{4, 3, 2, 1}, link.Values()) +} + +func TestDoublyLink_GetMiddleNode(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_GetMiddleNode") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + middle1 := link.GetMiddleNode() + assert.Equal(3, middle1.Value) + + link.InsertAtTail(5) + link.InsertAtTail(6) + link.InsertAtTail(7) + middle2 := link.GetMiddleNode() + assert.Equal(4, middle2.Value) +} + +func TestDoublyLink_Clear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDoublyLink_Clear") + + link := NewDoublyLink[int]() + assert.Equal(true, link.IsEmpty()) + assert.Equal(0, link.Size()) + + link.InsertAtTail(1) + assert.Equal(false, link.IsEmpty()) + assert.Equal(1, link.Size()) + + link.Clear() + assert.Equal(true, link.IsEmpty()) + assert.Equal(0, link.Size()) +} diff --git a/datastructure/link/singlylink.go b/datastructure/link/singlylink.go new file mode 100644 index 00000000..790cdd9e --- /dev/null +++ b/datastructure/link/singlylink.go @@ -0,0 +1,245 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink. +package datastructure + +import ( + "fmt" + "reflect" + + "github.com/duke-git/lancet/v2/datastructure" +) + +// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl. +type SinglyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} + +// NewSinglyLink return *SinglyLink instance +func NewSinglyLink[T any]() *SinglyLink[T] { + return &SinglyLink[T]{Head: nil} +} + +// InsertAtHead insert value into singly linklist at head index +func (sl *SinglyLink[T]) InsertAtHead(value T) { + newNode := datastructure.NewLinkNode(value) + newNode.Next = sl.Head + sl.Head = newNode + sl.length++ +} + +// InsertAtTail insert value into singly linklist at tail index +func (sl *SinglyLink[T]) InsertAtTail(value T) { + current := sl.Head + if current == nil { + sl.InsertAtHead(value) + return + } + + for current.Next != nil { + current = current.Next + } + + newNode := datastructure.NewLinkNode(value) + newNode.Next = nil + current.Next = newNode + + sl.length++ +} + +// InsertAt insert value into singly linklist at index +// param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing +func (sl *SinglyLink[T]) InsertAt(index int, value T) { + size := sl.length + if index < 0 || index > size { + return + } + + if index == 0 { + sl.InsertAtHead(value) + return + } + + if index == size { + sl.InsertAtTail(value) + return + } + + i := 0 + current := sl.Head + + for current != nil { + if i == index-1 { + newNode := datastructure.NewLinkNode(value) + newNode.Next = current.Next + current.Next = newNode + sl.length++ + return + } + i++ + current = current.Next + } +} + +// DeleteAtHead delete value in singly linklist at head index +func (sl *SinglyLink[T]) DeleteAtHead() { + if sl.Head == nil { + return + } + + current := sl.Head + sl.Head = current.Next + sl.length-- +} + +// DeleteAtTail delete value in singly linklist at tail +func (sl *SinglyLink[T]) DeleteAtTail() { + if sl.Head == nil { + return + } + + current := sl.Head + if current.Next == nil { + sl.DeleteAtHead() + } + + for current.Next.Next != nil { + current = current.Next + } + + current.Next = nil + sl.length-- +} + +// DeleteAt delete value in singly linklist at index +// param `index` should be [0, len(SinglyLink)-1] +func (sl *SinglyLink[T]) DeleteAt(index int) { + if sl.Head == nil { + return + } + current := sl.Head + if current.Next == nil || index == 0 { + sl.DeleteAtHead() + } + + if index == sl.length-1 { + sl.DeleteAtTail() + } + + if index < 0 || index > sl.length-1 { + return + } + + i := 0 + for current != nil { + if i == index-1 { + current.Next = current.Next.Next + sl.length-- + return + } + i++ + current = current.Next + } +} + +// DeleteValue delete value in singly linklist +func (sl *SinglyLink[T]) DeleteValue(value T) { + if sl.Head == nil { + return + } + dummyHead := datastructure.NewLinkNode(value) + dummyHead.Next = sl.Head + current := dummyHead + + for current.Next != nil { + if reflect.DeepEqual(current.Next.Value, value) { + current.Next = current.Next.Next + sl.length-- + } else { + current = current.Next + } + } + + sl.Head = dummyHead.Next +} + +// Reverse the linked list +func (sl *SinglyLink[T]) Reverse() { + var pre, next *datastructure.LinkNode[T] + + current := sl.Head + + for current != nil { + next = current.Next + current.Next = pre + pre = current + current = next + } + + sl.Head = pre +} + +// GetMiddleNode return node at middle index of linked list +func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { + if sl.Head == nil { + return nil + } + if sl.Head.Next == nil { + return sl.Head + } + fast := sl.Head + slow := sl.Head + + for fast != nil { + fast = fast.Next + + if fast != nil { + fast = fast.Next + slow = slow.Next + } else { + return slow + } + } + return slow +} + +// Size return the count of singly linked list +func (sl *SinglyLink[T]) Size() int { + return sl.length +} + +// Values return slice of all singly linklist node value +func (sl *SinglyLink[T]) Values() []T { + result := make([]T, 0, sl.length) + current := sl.Head + for current != nil { + result = append(result, current.Value) + current = current.Next + } + return result +} + +// IsEmpty checks if sl is empty or not +func (sl *SinglyLink[T]) IsEmpty() bool { + return sl.length == 0 +} + +// Clear all the node in singly linklist +func (sl *SinglyLink[T]) Clear() { + sl.Head = nil + sl.length = 0 +} + +// Print all nodes info of a linked list +func (sl *SinglyLink[T]) Print() { + current := sl.Head + info := "[ " + for current != nil { + info += fmt.Sprintf("%+v, ", current) + current = current.Next + } + info += " ]" + fmt.Println(info) +} diff --git a/datastructure/link/singlylink_test.go b/datastructure/link/singlylink_test.go new file mode 100644 index 00000000..ff19874d --- /dev/null +++ b/datastructure/link/singlylink_test.go @@ -0,0 +1,198 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestSinglyLink_InsertAtFirst(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_InsertAtFirst") + + link := NewSinglyLink[int]() + link.InsertAtHead(1) + link.InsertAtHead(2) + link.InsertAtHead(3) + + expected := []int{3, 2, 1} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestSinglyLink_InsertAtTail(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_InsertAtTail") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestSinglyLink_InsertAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_InsertAt") + + link := NewSinglyLink[int]() + + link.InsertAt(1, 1) //do nothing + + link.InsertAt(0, 1) + link.InsertAt(1, 2) + link.InsertAt(2, 4) + link.InsertAt(2, 3) + + link.Print() + + expected := []int{1, 2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestSinglyLink_DeleteAtHead(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead") + + link := NewSinglyLink[int]() + + link.DeleteAtHead() + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtHead() + + expected := []int{2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestSinglyLink_DeleteAtTail(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtTail() + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestSinglyLink_DeleteValue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_DeleteValue") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteValue(2) + assert.Equal([]int{1, 3, 4}, link.Values()) + + link.DeleteValue(1) + assert.Equal([]int{3, 4}, link.Values()) +} + +func TestSinglyLink_DeleteAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + link.InsertAtTail(5) + + link.DeleteAt(0) + assert.Equal([]int{2, 3, 4, 5}, link.Values()) + + link.DeleteAt(3) + assert.Equal([]int{2, 3, 4}, link.Values()) + + link.DeleteAt(1) + assert.Equal(2, link.Size()) + assert.Equal([]int{2, 4}, link.Values()) +} + +func TestSinglyLink_Reverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_Reverse") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.Reverse() + assert.Equal([]int{4, 3, 2, 1}, link.Values()) +} + +func TestSinglyLink_GetMiddleNode(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSinglyLink_GetMiddleNode") + + link := NewSinglyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + middle1 := link.GetMiddleNode() + assert.Equal(3, middle1.Value) + + link.InsertAtTail(5) + link.InsertAtTail(6) + link.InsertAtTail(7) + + middle2 := link.GetMiddleNode() + assert.Equal(4, middle2.Value) +} + +func TestSinglyLink_Clear(t *testing.T) { + assert := internal.NewAssert(t, "TestSinglyLink_Clear") + + link := NewSinglyLink[int]() + + assert.Equal(true, link.IsEmpty()) + assert.Equal(0, link.Size()) + + link.InsertAtTail(1) + assert.Equal(false, link.IsEmpty()) + assert.Equal(1, link.Size()) + + link.Clear() + assert.Equal(true, link.IsEmpty()) + assert.Equal(0, link.Size()) +} diff --git a/datastructure/list/copyonwritelist.go b/datastructure/list/copyonwritelist.go new file mode 100644 index 00000000..2878bbc8 --- /dev/null +++ b/datastructure/list/copyonwritelist.go @@ -0,0 +1,379 @@ +package datastructure + +import ( + "reflect" + "sort" + "sync" +) + +type CopyOnWriteList[T any] struct { + data []T + lock sync.Locker +} + +// NewCopyOnWriteList Creates an empty list. +func NewCopyOnWriteList[T any](data []T) *CopyOnWriteList[T] { + return &CopyOnWriteList[T]{data: data, lock: &sync.RWMutex{}} +} + +func (c *CopyOnWriteList[T]) getList() []T { + return c.data +} +func (c *CopyOnWriteList[T]) setList(data []T) { + c.data = data +} + +// Size returns the number of elements in this list. +func (c *CopyOnWriteList[T]) Size() int { + return len(c.getList()) +} + +// IsEmpty returns true if this list contains no elements. +func (c *CopyOnWriteList[T]) IsEmpty() bool { + return c.Size() == 0 +} + +// Contain returns true if this list contains the specified element. +func (c *CopyOnWriteList[T]) Contain(e T) bool { + list := c.getList() + return indexOf(e, list, 0, c.Size()) >= 0 +} + +// ValueOf returns the index of the first occurrence of the specified element in this list, or null if this list does not contain the element. +func (c *CopyOnWriteList[T]) ValueOf(index int) (*T, bool) { + list := c.getList() + if index < 0 || index >= len(c.data) { + return nil, false + } + return get(list, index), true +} + +// IndexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. +func (c *CopyOnWriteList[T]) IndexOf(e T) int { + list := c.getList() + return indexOf(e, list, 0, c.Size()) +} + +// indexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. +// start the start position of the search (inclusive) +// end the end position of the search (exclusive) +func indexOf[T any](o T, e []T, start int, end int) int { + if start >= end { + return -1 + } + for i := start; i < end; i++ { + if reflect.DeepEqual(e[i], o) { + return i + } + } + return -1 +} + +// LastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element. +func (c *CopyOnWriteList[T]) LastIndexOf(e T) int { + list := c.getList() + return lastIndexOf(e, list, 0, c.Size()) +} + +// lastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element. +// start the start position of the search (inclusive) +// end the end position of the search (exclusive) +func lastIndexOf[T any](o T, e []T, start int, end int) int { + if start >= end { + return -1 + } + for i := end - 1; i >= start; i-- { + if reflect.DeepEqual(e[i], o) { + return i + } + } + return -1 +} + +// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the +// functional predicate f(T) bool +// if not found return -1. +func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int { + index := -1 + data := l.getList() + for i := len(data) - 1; i >= 0; i-- { + if f(data[i]) { + index = i + break + } + } + return index +} + +// IndexOfFunc returns the first index satisfying the functional predicate f(v) bool +// if not found return -1. +func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int { + index := -1 + data := l.getList() + for i, v := range data { + if f(v) { + index = i + break + } + } + return index +} + +// get returns the element at the specified position in this list. +func get[T any](o []T, index int) *T { + return &o[index] +} + +// Get returns the element at the specified position in this list. +func (c *CopyOnWriteList[T]) Get(index int) *T { + list := c.getList() + if index < 0 || index >= len(list) { + return nil + } + return get(list, index) +} + +func (c *CopyOnWriteList[T]) set(index int, e T) (oldValue *T) { + lock := c.lock + lock.Lock() + defer lock.Unlock() + list := c.getList() + oldValue = get(list, index) + + if reflect.DeepEqual(oldValue, e) { + c.setList(list) + } else { + newList := make([]T, len(list)) + copy(newList, list) + newList[index] = e + c.setList(newList) + } + return +} + +// Set replaces the element at the specified position in this list with the specified element. +func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool) { + list := c.getList() + if index < 0 || index >= len(list) { + return oldValue, false + } + return c.set(index, e), true +} + +// Add appends the specified element to the end of this list. +func (c *CopyOnWriteList[T]) Add(e T) bool { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + newList := make([]T, len(list)+1) + copy(newList, list) + newList[len(list)] = e + c.setList(newList) + return true +} + +// AddAll appends all the elements in the specified collection to the end of this list +func (c *CopyOnWriteList[T]) AddAll(e []T) bool { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + newList := make([]T, len(list)+len(e)) + copy(newList, list) + copy(newList[len(list):], e) + c.setList(newList) + return true +} + +// AddByIndex inserts the specified element at the specified position in this list. +func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + length := len(list) + if index < 0 || index > length { + return false + } + var newList []T + var numMove = length - index + if numMove == 0 { + newList = make([]T, length+1) + copy(newList, list) + } else { + newList = make([]T, length+1) + copy(newList, list[:index]) + copy(newList[index+1:], list[index:]) + } + newList[index] = e + c.setList(newList) + return true +} + +// delete removes the element at the specified position in this list. +func (c *CopyOnWriteList[T]) delete(index int) *T { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + length := len(list) + + oldValue := get(list, index) + numMove := length - index - 1 + var newList []T + if numMove == 0 { + newList = make([]T, length-1) + copy(newList, list[:index]) + } else { + newList = make([]T, length-1) + copy(newList, list[:index]) + copy(newList[index:], list[index+1:]) + } + + c.setList(newList) + return oldValue +} + +// DeleteAt removes the element at the specified position in this list. +func (c *CopyOnWriteList[T]) DeleteAt(index int) (*T, bool) { + list := c.getList() + if index < 0 || index >= len(list) { + return nil, false + } + return c.delete(index), true +} + +// DeleteBy removes the first occurrence of the specified element from this list, if it is present. +func (c *CopyOnWriteList[T]) DeleteBy(o T) (*T, bool) { + list := c.getList() + index := indexOf(o, list, 0, len(list)) + if index == -1 { + return nil, false + } + return c.delete(index), true +} + +// DeleteRange removes from this list all the elements whose index is between fromIndex, inclusive, and toIndex, exclusive. +// left close and right open +func (c *CopyOnWriteList[T]) DeleteRange(start int, end int) { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + length := len(list) + if start < 0 || end > length || start > end { + return + } + var newList []T + numMove := length - end + if numMove == 0 { + newList = make([]T, length-(end-start)) + copy(newList, list[:start]) + } else { + newList = make([]T, length-(end-start)) + copy(newList, list[:start]) + copy(newList[start:], list[end:]) + } + c.setList(newList) +} + +// DeleteIf removes all the elements of this collection that satisfy the given predicate. +func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + length := len(list) + var newList []T + for i := 0; i < length; i++ { + if !f(list[i]) { + newList = append(newList, list[i]) + } + } + c.setList(newList) +} + +// Equal returns true if the specified object is equal to this list. +func (c *CopyOnWriteList[T]) Equal(other *[]T) bool { + if other == nil { + return false + } + if c.Size() != len(*other) { + return false + } + list := c.getList() + otherList := NewCopyOnWriteList(*other).getList() + for i := 0; i < len(list); i++ { + if !reflect.DeepEqual(list[i], otherList[i]) { + return false + } + } + return true +} + +// Clear removes all the elements from this list. +func (c *CopyOnWriteList[T]) Clear() { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + list = make([]T, 0) + c.setList(list) +} + +// Merge a tow list to one, change the list +func (c *CopyOnWriteList[T]) Merge(other []T) { + lock := c.lock + lock.Lock() + defer lock.Unlock() + + list := c.getList() + list = append(list, other...) + c.setList(list) +} + +// ForEach performs the given action for each element of the Iterable until all elements have been processed +// or the action throws an exception. +func (c *CopyOnWriteList[T]) ForEach(f func(T)) { + list := c.getList() + for i := 0; i < len(list); i++ { + f(list[i]) + } + +} + +// Sort sorts this list according to the order induced by the specified Comparator. +func (c *CopyOnWriteList[T]) Sort(compare func(o1 T, o2 T) bool) { + lock := c.lock + lock.Lock() + list := c.getList() + + sort.Slice(list, func(i, j int) bool { + return compare(list[i], list[j]) + }) + + c.setList(list) +} + +func (c *CopyOnWriteList[T]) SubList(start int, end int) (newList []T) { + lock := c.lock + lock.Lock() + list := c.getList() + length := len(list) + defer lock.Unlock() + if start < 0 || end > length || start > end { + return []T{} + } + newList = make([]T, end-start) + copy(newList, list[start:end]) + c.setList(newList) + return +} diff --git a/datastructure/list/copyonwritelist_test.go b/datastructure/list/copyonwritelist_test.go new file mode 100644 index 00000000..7b90d1d0 --- /dev/null +++ b/datastructure/list/copyonwritelist_test.go @@ -0,0 +1,268 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestCopyOnWriteList_ValueOf(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + + assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf") + of, ok := list.ValueOf(3) + assert.Equal(4, *of) + assert.Equal(true, ok) + + _, ok = list.ValueOf(6) + assert.Equal(false, ok) +} + +func TestCopyOnWriteList_Contain(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_Contains") + assert.Equal(true, list.Contain(3)) +} + +func TestCopyOnWriteList_IsEmpty(t *testing.T) { + list := NewCopyOnWriteList([]int{}) + assert := internal.NewAssert(t, "CopyOnWriteList_IsEmpty") + assert.Equal(true, list.IsEmpty()) +} + +func TestCopyOnWriteList_Size(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_size") + assert.Equal(5, list.Size()) +} + +func TestCopyOnWriteList_GetList(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_GetList") + assert.Equal([]int{1, 2, 3, 4, 5}, list.getList()) +} + +func TestCopyOnWriteList_Get(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_Get") + i := list.Get(2) + assert.Equal(3, *i) +} + +func TestCopyOnWriteList_Set(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_Set") + list.Set(2, 6) + assert.Equal(6, list.getList()[2]) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.Set(0, 6) + assert.Equal(6, list.getList()[0]) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.Set(0, 1) + assert.Equal(1, list.getList()[0]) +} + +func TestCopyOnWriteList_Add(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_Add") + list.Add(6) + assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList()) +} + +func TestCopyOnWriteList_AddAll(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_AddAll") + list.AddAll([]int{6, 7, 8}) + assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8}, list.getList()) +} + +func TestCopyOnWriteList_AddByIndex(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_AddByIndex") + list.AddByIndex(2, 6) + assert.Equal([]int{1, 2, 6, 3, 4, 5}, list.getList()) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.AddByIndex(0, 6) + assert.Equal([]int{6, 1, 2, 3, 4, 5}, list.getList()) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.AddByIndex(5, 6) + assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList()) +} + +func TestCopyOnWriteList_DeleteAt2(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByIndex") + list.DeleteAt(2) + assert.Equal([]int{1, 2, 4, 5}, list.getList()) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.DeleteAt(4) + assert.Equal([]int{1, 2, 3, 4}, list.getList()) +} + +func TestCopyOnWriteList_RemoveByValue(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByValue") + list.DeleteBy(3) + assert.Equal([]int{1, 2, 4, 5}, list.getList()) +} + +func TestCopyOnWriteList_DeleteRange(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + assert := internal.NewAssert(t, "CopyOnWriteList_RemoveRange") + list.DeleteRange(1, 3) + assert.Equal([]int{1, 4, 5}, list.getList()) + + list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5}) + list.DeleteRange(0, 5) + assert.Equal([]int{}, list.getList()) +} + +func TestCopyOnWriteList_LastIndexOf(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3}) + assert := internal.NewAssert(t, "CopyOnWriteList_LastIndexOf") + assert.Equal(5, list.LastIndexOf(3)) +} + +func TestCopyOnWriteList_DeleteAt(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3}) + assert := internal.NewAssert(t, "CopyOnWriteList_DeleteAt") + list.DeleteAt(2) + assert.Equal([]int{1, 2, 4, 5, 3}, list.getList()) +} + +func TestCopyOnWriteList_DeleteBy(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3}) + assert := internal.NewAssert(t, "CopyOnWriteList_DeleteBy") + list.DeleteBy(3) + assert.Equal([]int{1, 2, 4, 5, 3}, list.getList()) +} + +func TestCopyOnWriteList_DeleteIf(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6}) + assert := internal.NewAssert(t, "CopyOnWriteList_DeleteIf") + + list.DeleteIf(func(i int) bool { + return i%2 == 0 + }) + + assert.Equal([]int{1, 3, 5, 3}, list.getList()) +} + +func TestCopyOnWriteList_Equal(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6}) + assert := internal.NewAssert(t, "CopyOnWriteList_Equal") + + assert.Equal(true, list.Equal(&[]int{1, 2, 3, 4, 5, 3, 6})) +} + +func TestCopyOnWriteList_ForEach(t *testing.T) { + testList := make([]int, 0) + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6}) + assert := internal.NewAssert(t, "CopyOnWriteList_ForEach") + + list.ForEach(func(i int) { + testList = append(testList, i) + }) + assert.Equal([]int{1, 2, 3, 4, 5, 3, 6}, testList) + + list.ForEach(func(i int) { + list.Add(i) + }) + assert.Equal([]int{1, 2, 3, 4, 5, 3, 6, 1, 2, 3, 4, 5, 3, 6}, list.getList()) +} + +func TestCopyOnWriteList_Clear(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6}) + assert := internal.NewAssert(t, "CopyOnWriteList_Clear") + + list.Clear() + assert.Equal([]int{}, list.getList()) +} + +func TestCopyOnWriteList_Merge(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9}) + assert := internal.NewAssert(t, "CopyOnWriteList_Merge") + + list.Merge([]int{2, 4, 6, 8, 10}) + assert.Equal([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}, list.getList()) + +} + +func TestCopyOnWriteList_Sort(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + assert := internal.NewAssert(t, "CopyOnWriteList_Sort") + + list.Sort(func(i, j int) bool { + return i < j + }) + assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, list.getList()) +} + +func TestCopyOnWriteList_IndexOf(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf") + + assert.Equal(0, list.IndexOf(1)) + assert.Equal(9, list.IndexOf(10)) + assert.Equal(-1, list.IndexOf(11)) +} + +func TestCopyOnWriteList_SubList(t *testing.T) { + list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + + assert := internal.NewAssert(t, "CopyOnWriteList_SubList") + + list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + subList := list.SubList(1, 3) + assert.Equal([]int{3, 5}, subList) + + list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + subList = list.SubList(1, 1) + assert.Equal([]int{}, subList) + + list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + assert.Equal(10, list.Size()) + subList = list.SubList(1, 10) + assert.Equal([]int{3, 5, 7, 9, 2, 4, 6, 8, 10}, subList) + + list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}) + subList = list.SubList(11, 1) + assert.Equal([]int{}, subList) +} + +func TestCopyOnWriteListIndexOfFunc(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOfFunc") + + list := NewCopyOnWriteList([]int{1, 2, 3}) + i := list.IndexOfFunc(func(a int) bool { return a == 1 }) + assert.Equal(0, i) + + i = list.IndexOfFunc(func(a int) bool { return a == 4 }) + assert.Equal(-1, i) +} + +func TestNewCopyOnWriteListLastIndexOfFunc(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLastIndexOfFunc") + + list := NewCopyOnWriteList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9}) + i := list.LastIndexOfFunc(func(a int) bool { return a == 3 }) + assert.Equal(5, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 10 }) + assert.Equal(-1, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 4 }) + assert.Equal(6, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 1 }) + assert.Equal(0, i) +} diff --git a/datastructure/list/list.go b/datastructure/list/list.go new file mode 100644 index 00000000..78813f78 --- /dev/null +++ b/datastructure/list/list.go @@ -0,0 +1,418 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. list is a linear table, implemented with slice. +package datastructure + +import ( + "reflect" + + "github.com/duke-git/lancet/v2/iterator" +) + +// List is a linear table, implemented with slice. +type List[T any] struct { + data []T +} + +// NewList return a pointer of List. +func NewList[T any](data []T) *List[T] { + return &List[T]{data: data} +} + +// Data return list data. +func (l *List[T]) Data() []T { + return l.data +} + +// ValueOf return the value pointer at index of list data. +func (l *List[T]) ValueOf(index int) (*T, bool) { + if index < 0 || index >= len(l.data) { + return nil, false + } + return &l.data[index], true +} + +// IndexOf returns the index of value. if not found return -1. +func (l *List[T]) IndexOf(value T) int { + index := -1 + data := l.data + for i, v := range data { + if reflect.DeepEqual(v, value) { + index = i + break + } + } + return index +} + +// LastIndexOf returns the index of the last occurrence of the value in this list. +// if not found return -1. +func (l *List[T]) LastIndexOf(value T) int { + index := -1 + data := l.data + for i := len(data) - 1; i >= 0; i-- { + if reflect.DeepEqual(data[i], value) { + index = i + break + } + } + return index +} + +// IndexOfFunc returns the first index satisfying f(v) +// if not found return -1. +func (l *List[T]) IndexOfFunc(f func(T) bool) int { + index := -1 + data := l.data + for i, v := range data { + if f(v) { + index = i + break + } + } + return index +} + +// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying f(data[i]) +// if not found return -1. +func (l *List[T]) LastIndexOfFunc(f func(T) bool) int { + index := -1 + data := l.data + for i := len(data) - 1; i >= 0; i-- { + if f(data[i]) { + index = i + break + } + } + return index +} + +// Contain checks if the value in the list or not. +func (l *List[T]) Contain(value T) bool { + data := l.data + for _, v := range data { + if reflect.DeepEqual(v, value) { + return true + } + } + return false +} + +// Push append value to the list data. +func (l *List[T]) Push(value T) { + l.data = append(l.data, value) +} + +// InsertAtFirst insert value into list at first index. +func (l *List[T]) InsertAtFirst(value T) { + l.InsertAt(0, value) +} + +// InsertAtLast insert value into list at last index. +func (l *List[T]) InsertAtLast(value T) { + l.InsertAt(len(l.data), value) +} + +// InsertAt insert value into list at index. +func (l *List[T]) InsertAt(index int, value T) { + data := l.data + size := len(data) + + if index < 0 || index > size { + return + } + l.data = append(data[:index], append([]T{value}, data[index:]...)...) +} + +// PopFirst delete the first value of list and return it. +func (l *List[T]) PopFirst() (*T, bool) { + if len(l.data) == 0 { + return nil, false + } + + v := l.data[0] + l.DeleteAt(0) + + return &v, true +} + +// PopLast delete the last value of list and return it. +func (l *List[T]) PopLast() (*T, bool) { + size := len(l.data) + if size == 0 { + return nil, false + } + + v := l.data[size-1] + l.DeleteAt(size - 1) + + return &v, true +} + +// DeleteAt delete the value of list at index. +func (l *List[T]) DeleteAt(index int) { + data := l.data + size := len(data) + if index < 0 || index > size-1 { + return + } + if index == size-1 { + data = data[:index] + } else { + data = append(data[:index], data[index+1:]...) + } + l.data = data +} + +// DeleteIf delete all satisfying f(data[i]), returns count of removed elements +func (l *List[T]) DeleteIf(f func(T) bool) int { + data := l.data + size := len(data) + + var c int + for index := 0; index < len(data); index++ { + if !f(data[index]) { + continue + } + if index == size-1 { + data = data[:index] + } else { + data = append(data[:index], data[index+1:]...) + index-- + } + c++ + } + + if c > 0 { + l.data = data + } + return c +} + +// UpdateAt update value of list at index, index shoud between 0 and list size -1 +func (l *List[T]) UpdateAt(index int, value T) { + data := l.data + size := len(data) + + if index < 0 || index >= size { + return + } + l.data = append(data[:index], append([]T{value}, data[index+1:]...)...) +} + +// Equal compare list to other list, use reflect.DeepEqual. +func (l *List[T]) Equal(other *List[T]) bool { + if len(l.data) != len(other.data) { + return false + } + + for i := 0; i < len(l.data); i++ { + if !reflect.DeepEqual(l.data[i], other.data[i]) { + return false + } + } + + return true +} + +// IsEmpty check if the list is empty or not. +func (l *List[T]) IsEmpty() bool { + return len(l.data) == 0 +} + +// Clear the data of list. +func (l *List[T]) Clear() { + l.data = make([]T, 0) +} + +// Clone return a copy of list. +func (l *List[T]) Clone() *List[T] { + cl := NewList(make([]T, len(l.data))) + copy(cl.data, l.data) + + return cl +} + +// Merge two list, return new list, don't change original list. +func (l *List[T]) Merge(other *List[T]) *List[T] { + l1, l2 := len(l.data), len(other.data) + ml := NewList(make([]T, l1+l2)) + + data := append([]T{}, append(l.data, other.data...)...) + ml.data = data + + return ml +} + +// Size return number of list data items. +func (l *List[T]) Size() int { + return len(l.data) +} + +// Cap return cap of the inner data. +func (l *List[T]) Cap() int { + return cap(l.data) +} + +// Swap the value of index i and j in list. +func (l *List[T]) Swap(i, j int) { + size := len(l.data) + if i < 0 || i >= size || j < 0 || j >= size { + return + } + l.data[i], l.data[j] = l.data[j], l.data[i] +} + +// Reverse the item order of list. +func (l *List[T]) Reverse() { + for i, j := 0, len(l.data)-1; i < j; i, j = i+1, j-1 { + l.data[i], l.data[j] = l.data[j], l.data[i] + } +} + +// Unique delete duplicate items in list. +func (l *List[T]) Unique() { + data := l.data + size := len(data) + + uniqueData := make([]T, 0) + for i := 0; i < size; i++ { + value := data[i] + skip := true + for _, v := range uniqueData { + if reflect.DeepEqual(value, v) { + skip = false + break + } + } + if skip { + uniqueData = append(uniqueData, value) + } + } + + l.data = uniqueData +} + +// Union creates a new list contain all element in list l and other, delete duplicate element. +func (l *List[T]) Union(other *List[T]) *List[T] { + result := NewList([]T{}) + + result.data = append(result.data, l.data...) + result.data = append(result.data, other.data...) + result.Unique() + + return result +} + +// Intersection creates a new list whose element both be contained in list l and other. +func (l *List[T]) Intersection(other *List[T]) *List[T] { + result := NewList(make([]T, 0)) + + for _, v := range l.data { + if other.Contain(v) { + result.data = append(result.data, v) + } + } + + return result +} + +// Difference returns the difference between two collections. +// return a list whose element in the original list, not in the given list. +func (l *List[T]) Difference(other *List[T]) *List[T] { + result := NewList(make([]T, 0)) + + intersectList := l.Intersection(other) + + for _, v := range l.data { + if !intersectList.Contain(v) { + result.data = append(result.data, v) + } + } + + return result +} + +// SymmetricDifference oppoiste operation of intersection function. +func (l *List[T]) SymmetricDifference(other *List[T]) *List[T] { + result := NewList(make([]T, 0)) + + intersectList := l.Intersection(other) + + for _, v := range l.data { + if !intersectList.Contain(v) { + result.data = append(result.data, v) + } + } + + for _, v := range other.data { + if !intersectList.Contain(v) { + result.data = append(result.data, v) + } + } + + return result +} + +// SubList returns a sub list of the original list between the specified fromIndex, inclusive, and toIndex, exclusive. +func (l *List[T]) SubList(fromIndex, toIndex int) *List[T] { + data := l.data[fromIndex:toIndex] + subList := make([]T, len(data)) + copy(subList, data) + return NewList(subList) +} + +// ForEach performs the given action for each element of the list. +func (l *List[T]) ForEach(consumer func(T)) { + for _, it := range l.data { + consumer(it) + } +} + +// RetainAll retains only the elements in this list that are contained in the given list. +func (l *List[T]) RetainAll(list *List[T]) bool { + return l.batchRemove(list, true) +} + +// DeleteAll removes from this list all of its elements that are contained in the given list. +func (l *List[T]) DeleteAll(list *List[T]) bool { + return l.batchRemove(list, false) +} + +func (l *List[T]) batchRemove(list *List[T], complement bool) bool { + var ( + w = 0 + data = l.data + size = len(data) + ) + + for i := 0; i < size; i++ { + if list.Contain(data[i]) == complement { + data[w] = data[i] + w++ + } + } + + if w != size { + l.data = data[:w] + return true + } + return false +} + +// Iterator returns an iterator over the elements in this list in proper sequence. +func (l *List[T]) Iterator() iterator.Iterator[T] { + return iterator.FromSlice(l.data) +} + +// ListToMap convert a list to a map based on iteratee function. +func ListToMap[T any, K comparable, V any](list *List[T], iteratee func(T) (K, V)) map[K]V { + result := make(map[K]V, list.Size()) + for _, item := range list.data { + k, v := iteratee(item) + result[k] = v + } + + return result +} diff --git a/datastructure/list/list_test.go b/datastructure/list/list_test.go new file mode 100644 index 00000000..d553558b --- /dev/null +++ b/datastructure/list/list_test.go @@ -0,0 +1,531 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestListData(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestListData") + + list := NewList([]int{1, 2, 3}) + assert.Equal([]int{1, 2, 3}, list.Data()) +} + +func TestValueOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestValueOf") + + list := NewList([]int{1, 2, 3}) + v, ok := list.ValueOf(0) + assert.Equal(1, *v) + assert.Equal(true, ok) + + _, ok = list.ValueOf(3) + assert.Equal(false, ok) +} + +func TestIndexOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOf") + + list := NewList([]int{1, 2, 3}) + i := list.IndexOf(1) + assert.Equal(0, i) + + i = list.IndexOf(4) + assert.Equal(-1, i) +} + +func TestIndexOfFunc(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOf") + + list := NewList([]int{1, 2, 3}) + i := list.IndexOfFunc(func(a int) bool { return a == 1 }) + assert.Equal(0, i) + + i = list.IndexOfFunc(func(a int) bool { return a == 4 }) + assert.Equal(-1, i) +} + +func TestLastIndexOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOf") + + list := NewList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9}) + i := list.LastIndexOf(3) + assert.Equal(5, i) + + i = list.LastIndexOf(10) + assert.Equal(-1, i) + + i = list.LastIndexOf(4) + assert.Equal(6, i) + + i = list.LastIndexOf(1) + assert.Equal(0, i) +} + +func TestLastIndexOfFunc(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOf") + + list := NewList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9}) + i := list.LastIndexOfFunc(func(a int) bool { return a == 3 }) + assert.Equal(5, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 10 }) + assert.Equal(-1, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 4 }) + assert.Equal(6, i) + + i = list.LastIndexOfFunc(func(a int) bool { return a == 1 }) + assert.Equal(0, i) +} + +func TestContain(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestContain") + + list := NewList([]int{1, 2, 3}) + assert.Equal(true, list.Contain(1)) + assert.Equal(false, list.Contain(0)) +} + +func TestPush(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPush") + + list := NewList([]int{1, 2, 3}) + list.Push(4) + + assert.Equal([]int{1, 2, 3, 4}, list.Data()) +} + +func TestInsertAtFirst(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestInsertAtFirst") + + list := NewList([]int{1, 2, 3}) + list.InsertAtFirst(0) + + assert.Equal([]int{0, 1, 2, 3}, list.Data()) +} + +func TestInsertAtLast(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestInsertAtLast") + + list := NewList([]int{1, 2, 3}) + list.InsertAtLast(4) + + assert.Equal([]int{1, 2, 3, 4}, list.Data()) +} + +func TestInsertAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestInsertAt") + + list := NewList([]int{1, 2, 3}) + + list.InsertAt(-1, 0) + assert.Equal([]int{1, 2, 3}, list.Data()) + + list.InsertAt(4, 0) + assert.Equal([]int{1, 2, 3}, list.Data()) + + list.InsertAt(0, 0) + assert.Equal([]int{0, 1, 2, 3}, list.Data()) + + list.InsertAt(4, 4) + assert.Equal([]int{0, 1, 2, 3, 4}, list.Data()) +} + +func TestPopFirst(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPopFirst") + + list := NewList([]int{1, 2, 3}) + v, ok := list.PopFirst() + assert.Equal(1, *v) + assert.Equal(true, ok) + assert.Equal([]int{2, 3}, list.Data()) + + list2 := NewList([]int{}) + _, ok = list2.PopFirst() + assert.Equal(false, ok) + assert.Equal([]int{}, list2.Data()) +} + +func TestPopLast(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPopLast") + + list := NewList([]int{1, 2, 3}) + v, ok := list.PopLast() + assert.Equal(3, *v) + assert.Equal(true, ok) + assert.Equal([]int{1, 2}, list.Data()) + + list2 := NewList([]int{}) + _, ok = list2.PopLast() + assert.Equal(false, ok) + assert.Equal([]int{}, list2.Data()) +} + +func TestDeleteAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDeleteAt") + + list := NewList([]int{1, 2, 3, 4}) + + list.DeleteAt(-1) + assert.Equal([]int{1, 2, 3, 4}, list.Data()) + + list.DeleteAt(4) + assert.Equal([]int{1, 2, 3, 4}, list.Data()) + + list.DeleteAt(0) + assert.Equal([]int{2, 3, 4}, list.Data()) + + list.DeleteAt(2) + assert.Equal([]int{2, 3}, list.Data()) +} + +func TestUpdateAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUpdateAt") + + list := NewList([]int{1, 2, 3, 4}) + + list.UpdateAt(-1, 0) + assert.Equal([]int{1, 2, 3, 4}, list.Data()) + + list.UpdateAt(4, 0) + assert.Equal([]int{1, 2, 3, 4}, list.Data()) + + list.UpdateAt(0, 5) + assert.Equal([]int{5, 2, 3, 4}, list.Data()) + + list.UpdateAt(3, 1) + assert.Equal([]int{5, 2, 3, 1}, list.Data()) +} + +func TestEqual(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEqual") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + list3 := NewList([]int{1, 2, 3}) + + assert.Equal(true, list1.Equal(list2)) + assert.Equal(false, list1.Equal(list3)) +} + +func TestIsEmpty(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsEmpty") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{}) + + assert.Equal(false, list1.IsEmpty()) + assert.Equal(true, list2.IsEmpty()) +} + +func TestIsClear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsClear") + + list1 := NewList([]int{1, 2, 3, 4}) + list1.Clear() + empty := NewList([]int{}) + + assert.Equal(empty, list1) +} + +func TestClone(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestClone") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := list1.Clone() + + assert.Equal(true, list1.Equal(list2)) +} + +func TestMerge(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMerge") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{4, 5, 6}) + expected := NewList([]int{1, 2, 3, 4, 4, 5, 6}) + + list3 := list1.Merge(list2) + assert.Equal(true, expected.Equal(list3)) +} + +func TestSize(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSize") + + list := NewList([]int{1, 2, 3, 4}) + empty := NewList([]int{}) + + assert.Equal(4, list.Size()) + assert.Equal(0, empty.Size()) +} + +func TestCap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCap") + + data := make([]int, 0, 100) + list := NewList(data) + assert.Equal(100, list.Cap()) + + data = make([]int, 0) + list = NewList(data) + assert.Equal(0, list.Cap()) +} + +func TestSwap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSwap") + + list := NewList([]int{1, 2, 3, 4}) + expected := NewList([]int{4, 2, 3, 1}) + + list.Swap(0, 3) + + assert.Equal(true, expected.Equal(list)) +} + +func TestReverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReverse") + + list := NewList([]int{1, 2, 3, 4}) + expected := NewList([]int{4, 3, 2, 1}) + + list.Reverse() + + assert.Equal(true, expected.Equal(list)) +} + +func TestUnique(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnique") + + list := NewList([]int{1, 2, 2, 3, 4}) + expected := NewList([]int{1, 2, 3, 4}) + + list.Unique() + + assert.Equal(true, expected.Equal(list)) +} + +func TestUnion(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnion") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{4, 5, 6}) + expected := NewList([]int{1, 2, 3, 4, 5, 6}) + + list3 := list1.Union(list2) + assert.Equal(true, expected.Equal(list3)) +} + +func TestIntersection(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIntersection") + + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{4, 5, 6}) + expected := NewList([]int{4}) + + list3 := list1.Intersection(list2) + assert.Equal(true, expected.Equal(list3)) +} + +func TestDifference(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDifference") + + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + expected := NewList([]int{3}) + + list3 := list1.Difference(list2) + assert.Equal(true, expected.Equal(list3)) +} + +func TestSymmetricDifference(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSymmetricDifference") + + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + expected := NewList([]int{3, 4}) + + list3 := list1.SymmetricDifference(list2) + assert.Equal(true, expected.Equal(list3)) +} + +func TestSubSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSubSlice") + + list := NewList([]int{1, 2, 3, 4, 5, 8}) + subList := list.SubList(2, 5) + + assert.Equal([]int{3, 4, 5}, subList.Data()) +} + +func BenchmarkSubSlice(b *testing.B) { + list := NewList([]int{1, 2, 3, 4, 5, 8}) + for n := 0; n < b.N; n++ { + list.SubList(2, 5) + } +} + +func TestDeleteIf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDeleteIf") + + list := NewList([]int{1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1}) + + count := list.DeleteIf(func(a int) bool { return a == 1 }) + assert.Equal([]int{2, 3, 4}, list.Data()) + assert.Equal(12, count) + + count = list.DeleteIf(func(a int) bool { return a == 5 }) + assert.Equal([]int{2, 3, 4}, list.Data()) + assert.Equal(0, count) +} + +func TestForEach(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestForEach") + + list := NewList([]int{1, 2, 3, 4}) + rs := make([]int, 0) + list.ForEach(func(i int) { + rs = append(rs, i) + }) + + assert.Equal([]int{1, 2, 3, 4}, rs) +} + +func TestRetainAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetainAll") + + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + retain := NewList([]int{1, 2}) + retain1 := NewList([]int{2, 3}) + retain2 := NewList([]int{1, 2, 5}) + + list.RetainAll(retain) + list1.RetainAll(retain1) + list2.RetainAll(retain2) + + assert.Equal([]int{1, 2}, list.Data()) + assert.Equal([]int{2, 3}, list1.Data()) + assert.Equal([]int{1, 2}, list2.Data()) +} + +func TestDeleteAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDeleteAll") + + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + del := NewList([]int{1}) + del1 := NewList([]int{2, 3}) + del2 := NewList([]int{1, 2, 5}) + + list.DeleteAll(del) + list1.DeleteAll(del1) + list2.DeleteAll(del2) + assert.Equal([]int{2, 3, 4}, list.Data()) + assert.Equal([]int{1, 4}, list1.Data()) + assert.Equal([]int{3, 4}, list2.Data()) +} + +func TestIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIterator") + + list := NewList([]int{1, 2, 3, 4}) + iterator := list.Iterator() + + rs := make([]int, 0) + for iterator.HasNext() { + item, _ := iterator.Next() + rs = append(rs, item) + } + + assert.Equal([]int{1, 2, 3, 4}, rs) +} + +func TestListToMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "ListToMap") + + list := NewList([]int{1, 2, 3, 4}) + result := ListToMap(list, func(n int) (int, bool) { + return n, n > 1 + }) + + expected := map[int]bool{1: false, 2: true, 3: true, 4: true} + assert.Equal(expected, result) +} diff --git a/datastructure/node.go b/datastructure/node.go new file mode 100644 index 00000000..ed24a833 --- /dev/null +++ b/datastructure/node.go @@ -0,0 +1,51 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure implements some data structure. +package datastructure + +// LinkNode is a linkedlist node, which have a Value and Pre points to previous node, Next points to a next node of the link. +type LinkNode[T any] struct { + Value T + Pre *LinkNode[T] + Next *LinkNode[T] +} + +// NewLinkNode return a LinkNode pointer +func NewLinkNode[T any](value T) *LinkNode[T] { + return &LinkNode[T]{value, nil, nil} +} + +// StackNode is a node in stack, which have a Value and Next pointer points to next node in the stack. +type StackNode[T any] struct { + Value T + Next *StackNode[T] +} + +// NewStackNode return a StackNode pointer +func NewStackNode[T any](value T) *StackNode[T] { + return &StackNode[T]{value, nil} +} + +// QueueNode is a node in a queue, which have a Value and Next pointer points to next node in the queue. +type QueueNode[T any] struct { + Value T + Next *QueueNode[T] +} + +// NewQueueNode return a QueueNode pointer +func NewQueueNode[T any](value T) *QueueNode[T] { + return &QueueNode[T]{value, nil} +} + +// TreeNode is node of tree +type TreeNode[T any] struct { + Value T + Left *TreeNode[T] + Right *TreeNode[T] +} + +// NewTreeNode return a TreeNode pointer +func NewTreeNode[T any](val T) *TreeNode[T] { + return &TreeNode[T]{val, nil, nil} +} diff --git a/datastructure/optional/optional.go b/datastructure/optional/optional.go new file mode 100644 index 00000000..663c6604 --- /dev/null +++ b/datastructure/optional/optional.go @@ -0,0 +1,108 @@ +package optional + +import ( + "sync" +) + +// Optional is a type that may or may not contain a non-nil value. +type Optional[T any] struct { + value *T + mu *sync.RWMutex +} + +// Default returns an default Optional instance. +func Default[T any]() Optional[T] { + return Optional[T]{mu: &sync.RWMutex{}} +} + +// Of returns an Optional with a non-nil value. +func Of[T any](value T) Optional[T] { + return Optional[T]{value: &value, mu: &sync.RWMutex{}} +} + +// FromNillable returns an Optional for a given value, which may be nil. +func FromNillable[T any](value *T) Optional[T] { + if value == nil { + return Default[T]() + } + return Optional[T]{value: value, mu: &sync.RWMutex{}} +} + +// IsNotNil checks if there is a value present. +func (o Optional[T]) IsNotNil() bool { + o.mu.RLock() + defer o.mu.RUnlock() + + return o.value != nil +} + +// IsNil checks if the Optional is nil. +func (o Optional[T]) IsNil() bool { + return !o.IsNotNil() +} + +// IfNotNil performs the given action with the value if a value is not nil. +func (o Optional[T]) IfNotNil(action func(value T)) { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value != nil { + action(*o.value) + } +} + +// IfNotNilOrElse performs the action with the value if present, otherwise performs the fallback action. +func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value != nil { + action(*o.value) + } else { + fallbackAction() + } +} + +// Unwarp returns the value if not nil, otherwise panics. +func (o Optional[T]) Unwarp() T { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value == nil { + panic("Optional.Get: no value present") + } + return *o.value +} + +// OrElse returns the value if is not nil, otherwise returns other. +func (o Optional[T]) OrElse(other T) T { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value != nil { + return *o.value + } + return other +} + +// OrElseGet returns the value if is not nil, otherwise invokes action and returns the result. +func (o Optional[T]) OrElseGet(action func() T) T { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value != nil { + return *o.value + } + return action() +} + +// OrElseTrigger returns the value if present, otherwise returns an error. +func (o Optional[T]) OrElseTrigger(errorHandler func() error) (T, error) { + o.mu.RLock() + defer o.mu.RUnlock() + + if o.value == nil { + return *new(T), errorHandler() + } + return *o.value, nil +} diff --git a/datastructure/optional/optional_test.go b/datastructure/optional/optional_test.go new file mode 100644 index 00000000..461b4e36 --- /dev/null +++ b/datastructure/optional/optional_test.go @@ -0,0 +1,151 @@ +package optional + +import ( + "errors" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestDefault(t *testing.T) { + assert := internal.NewAssert(t, "TestEmpty") + opt := Default[int]() + + assert.ShouldBeTrue(opt.IsNil()) +} + +func TestOf(t *testing.T) { + assert := internal.NewAssert(t, "TestOf") + value := 42 + opt := Of(value) + + assert.ShouldBeTrue(opt.IsNotNil()) + assert.Equal(opt.Unwarp(), value) +} + +func TestFromNillable(t *testing.T) { + assert := internal.NewAssert(t, "TestOfNullable") + var value *int = nil + opt := FromNillable(value) + + assert.ShouldBeFalse(opt.IsNotNil()) + + value = new(int) + *value = 42 + opt = FromNillable(value) + + assert.ShouldBeTrue(opt.IsNotNil()) +} + +func TestOrElse(t *testing.T) { + assert := internal.NewAssert(t, "TestOrElse") + optDefault := Default[int]() + defaultValue := 100 + + val := optDefault.OrElse(defaultValue) + assert.Equal(val, defaultValue) + + optWithValue := Of(42) + val = optWithValue.OrElse(defaultValue) + assert.Equal(val, 42) +} + +func TestOrElseGetHappyPath(t *testing.T) { + assert := internal.NewAssert(t, "TestOrElseGetHappyPath") + optWithValue := Of(42) + action := func() int { return 100 } + + val := optWithValue.OrElseGet(action) + assert.Equal(val, 42) +} + +func TestOrElseGet(t *testing.T) { + assert := internal.NewAssert(t, "TestOrElseGet") + optDefault := Default[int]() + action := func() int { return 100 } + + val := optDefault.OrElseGet(action) + assert.Equal(val, action()) +} + +func TestOrElseTrigger(t *testing.T) { + assert := internal.NewAssert(t, "OrElseTrigger") + optDefault := Default[int]() + _, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") }) + + assert.Equal(err.Error(), "no value") + + optWithValue := Of(42) + val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") }) + + assert.IsNil(err) + assert.Equal(val, 42) +} + +func TestIfNotNil(t *testing.T) { + assert := internal.NewAssert(t, "IfNotNil") + called := false + action := func(value int) { called = true } + + optDefault := Default[int]() + optDefault.IfNotNil(action) + + assert.ShouldBeFalse(called) + + called = false // Reset for next test + optWithValue := Of(42) + optWithValue.IfNotNil(action) + + assert.ShouldBeTrue(called) +} + +func TestIfNotNilOrElse(t *testing.T) { + assert := internal.NewAssert(t, "TestIfNotNilOrElse") + + // Test when value is present + calledWithValue := false + valueAction := func(value int) { calledWithValue = true } + fallbackAction := func() { t.Errorf("Empty action should not be called when value is present") } + + optWithValue := Of(42) + optWithValue.IfNotNilOrElse(valueAction, fallbackAction) + + assert.ShouldBeTrue(calledWithValue) + + // Test when value is not present + calledWithEmpty := false + valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") } + fallbackAction = func() { calledWithEmpty = true } + + optDefault := Default[int]() + optDefault.IfNotNilOrElse(valueAction, fallbackAction) + + assert.ShouldBeTrue(calledWithEmpty) +} + +func TestGetWithPanicStandard(t *testing.T) { + assert := internal.NewAssert(t, "TestGetWithPanicStandard") + + // Test when value is present + optWithValue := Of(42) + func() { + defer func() { + r := recover() + assert.IsNil(r) + }() + val := optWithValue.Unwarp() + if val != 42 { + t.Errorf("Expected Unwarp to return 42, got %v", val) + } + }() + + // Test when value is not present + optDefault := Default[int]() + func() { + defer func() { + r := recover() + assert.IsNotNil(r) + }() + _ = optDefault.Unwarp() + }() +} diff --git a/datastructure/queue/arrayqueue.go b/datastructure/queue/arrayqueue.go new file mode 100644 index 00000000..4532894c --- /dev/null +++ b/datastructure/queue/arrayqueue.go @@ -0,0 +1,155 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. +// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue. +package datastructure + +import ( + "fmt" + "reflect" +) + +// ArrayQueue implements queue with slice +type ArrayQueue[T any] struct { + data []T + head int + tail int + capacity int + size int +} + +func NewArrayQueue[T any](capacity int) *ArrayQueue[T] { + return &ArrayQueue[T]{ + data: make([]T, 0, capacity), + head: 0, + tail: 0, + capacity: capacity, + size: 0, + } +} + +// Data return slice of queue data +func (q *ArrayQueue[T]) Data() []T { + items := make([]T, 0, q.tail-q.head) + for i := q.head; i < q.tail; i++ { + items = append(items, q.data[i]) + } + return items +} + +// Size return number of elements in queue +func (q *ArrayQueue[T]) Size() int { + return q.size +} + +// IsEmpty checks if queue is empty or not +func (q *ArrayQueue[T]) IsEmpty() bool { + return q.size == 0 +} + +// IsFull checks if queue is full or not +func (q *ArrayQueue[T]) IsFull() bool { + return q.size == q.capacity +} + +// Front return front value of queue +func (q *ArrayQueue[T]) Front() T { + return q.data[q.head] +} + +// Back return back value of queue +func (q *ArrayQueue[T]) Back() T { + return q.data[q.tail-1] +} + +// EnQueue put element into queue +func (q *ArrayQueue[T]) Enqueue(item T) bool { + if q.tail < q.capacity { + q.data = append(q.data, item) + // q.tail++ + q.data[q.tail] = item + } else { + //upgrade + if q.head > 0 { + for i := 0; i < q.tail-q.head; i++ { + q.data[i] = q.data[i+q.head] + } + q.tail -= q.head + q.head = 0 + } else { + if q.capacity < 65536 { + if q.capacity == 0 { + q.capacity = 1 + } + q.capacity *= 2 + } else { + q.capacity += 2 ^ 16 + } + + tmp := make([]T, q.capacity, q.capacity) + copy(tmp, q.data) + q.data = tmp + } + + q.data[q.tail] = item + } + + q.tail++ + q.size++ + + return true +} + +// DeQueue remove head element of queue and return it, if queue is empty, return nil and error +func (q *ArrayQueue[T]) Dequeue() (T, bool) { + var item T + if q.size == 0 { + return item, false + } + + item = q.data[q.head] + q.head++ + + if q.head >= 1024 || q.head*2 > q.tail { + q.capacity -= q.head + q.tail -= q.head + tmp := make([]T, q.capacity, q.capacity) + copy(tmp, q.data[q.head:]) + q.data = tmp + q.head = 0 + } + + q.size-- + return item, true +} + +// Clear the queue data +func (q *ArrayQueue[T]) Clear() { + capacity := q.capacity + q.data = make([]T, 0, capacity) + q.head = 0 + q.tail = 0 + q.size = 0 + q.capacity = capacity +} + +// Contain checks if the value is in queue or not +func (q *ArrayQueue[T]) Contain(value T) bool { + for _, v := range q.data { + if reflect.DeepEqual(v, value) { + return true + } + } + return false +} + +// Print queue data +func (q *ArrayQueue[T]) Print() { + info := "[" + for i := q.head; i < q.tail; i++ { + info += fmt.Sprintf("%+v, ", q.data[i]) + } + info += "]" + fmt.Println(info) +} diff --git a/datastructure/queue/arrayqueue_test.go b/datastructure/queue/arrayqueue_test.go new file mode 100644 index 00000000..b4f990e0 --- /dev/null +++ b/datastructure/queue/arrayqueue_test.go @@ -0,0 +1,119 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestArrayQueue_Enqueue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Enqueue") + + queue := NewArrayQueue[int](2) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + data := queue.Data() + size := queue.Size() + + assert.Equal([]int{1, 2, 3}, data) + assert.Equal(3, size) +} + +func TestArrayQueue_Dequeue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Dequeue") + + queue := NewArrayQueue[int](4) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val, ok := queue.Dequeue() + assert.Equal(true, ok) + + assert.Equal(1, val) + assert.Equal([]int{2, 3}, queue.Data()) +} + +func TestArrayQueue_Front(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Front") + + queue := NewArrayQueue[int](4) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val := queue.Front() + + assert.Equal(1, val) + assert.Equal([]int{1, 2, 3}, queue.Data()) +} + +func TestArrayQueue_Back(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Back") + + queue := NewArrayQueue[int](4) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val := queue.Back() + + assert.Equal(3, val) + assert.Equal([]int{1, 2, 3}, queue.Data()) +} + +func TestArrayQueue_Contain(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Contain") + + queue := NewArrayQueue[int](4) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + assert.Equal(true, queue.Contain(1)) + assert.Equal(false, queue.Contain(4)) +} + +func TestArrayQueue_Clear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_Clear") + + queue := NewArrayQueue[int](4) + + assert.Equal(true, queue.IsEmpty()) + assert.Equal(0, queue.Size()) + + queue.Enqueue(1) + assert.Equal(false, queue.IsEmpty()) + assert.Equal(1, queue.Size()) + + queue.Clear() + assert.Equal(true, queue.IsEmpty()) + assert.Equal(0, queue.Size()) +} + +func TestArrayQueue_IsFull(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayQueue_IsFull") + + queue := NewArrayQueue[int](3) + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + assert.Equal(true, queue.IsFull()) +} diff --git a/datastructure/queue/circularqueue.go b/datastructure/queue/circularqueue.go new file mode 100644 index 00000000..b33ba551 --- /dev/null +++ b/datastructure/queue/circularqueue.go @@ -0,0 +1,123 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. +// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue. +package datastructure + +import ( + "errors" + "fmt" + "reflect" +) + +// CircularQueue implements circular queue with slice, +// last index of CircularQueue don't contain value, so acturl capacity is capacity - 1 +type CircularQueue[T any] struct { + data []T + front int + rear int + capacity int +} + +// NewCircularQueue return a empty CircularQueue pointer +func NewCircularQueue[T any](capacity int) *CircularQueue[T] { + data := make([]T, capacity) + return &CircularQueue[T]{data: data, front: 0, rear: 0, capacity: capacity} +} + +// Data return slice of queue data +func (q *CircularQueue[T]) Data() []T { + data := []T{} + + front := q.front + rear := q.rear + if front <= rear { + return q.data[front:rear] + } + + data = append(data, q.data[front:]...) + data = append(data, q.data[0:rear]...) + + return data +} + +// Size return number of elements in circular queue +func (q *CircularQueue[T]) Size() int { + if q.capacity == 0 { + return 0 + } + return (q.rear - q.front + q.capacity) % q.capacity +} + +// IsEmpty checks if queue is empty or not +func (q *CircularQueue[T]) IsEmpty() bool { + return q.front == q.rear +} + +// IsFull checks if queue is full or not +func (q *CircularQueue[T]) IsFull() bool { + return (q.rear+1)%q.capacity == q.front +} + +// Front return front value of queue +func (q *CircularQueue[T]) Front() T { + return q.data[q.front] +} + +// Back return back value of queue +func (q *CircularQueue[T]) Back() T { + if q.rear-1 >= 0 { + return q.data[q.rear-1] + } + return q.data[q.capacity-1] +} + +// Enqueue put element into queue +func (q *CircularQueue[T]) Enqueue(value T) error { + if q.IsFull() { + return errors.New("queue is full!") + } + + q.data[q.rear] = value + q.rear = (q.rear + 1) % q.capacity + + return nil +} + +// Dequeue remove head element of queue and return it, if queue is empty, return nil and error +func (q *CircularQueue[T]) Dequeue() (*T, error) { + if q.IsEmpty() { + return nil, errors.New("queue is empty") + } + + headItem := q.data[q.front] + var t T + q.data[q.front] = t + q.front = (q.front + 1) % q.capacity + + return &headItem, nil +} + +// Clear the queue data +func (q *CircularQueue[T]) Clear() { + q.data = []T{} + q.front = 0 + q.rear = 0 + q.capacity = 0 +} + +// Contain checks if the value is in queue or not +func (q *CircularQueue[T]) Contain(value T) bool { + for _, v := range q.data { + if reflect.DeepEqual(v, value) { + return true + } + } + return false +} + +// Print queue data +func (q *CircularQueue[T]) Print() { + fmt.Printf("%+v\n", q) +} diff --git a/datastructure/queue/circularqueue_test.go b/datastructure/queue/circularqueue_test.go new file mode 100644 index 00000000..e1d386ae --- /dev/null +++ b/datastructure/queue/circularqueue_test.go @@ -0,0 +1,159 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestCircularQueue_Enqueue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Enqueue") + + queue := NewCircularQueue[int](6) + + err := queue.Enqueue(1) + assert.IsNil(err) + + err = queue.Enqueue(2) + assert.IsNil(err) + + err = queue.Enqueue(3) + assert.IsNil(err) + + err = queue.Enqueue(4) + assert.IsNil(err) + + err = queue.Enqueue(5) + assert.IsNil(err) + + assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data()) + assert.Equal(5, queue.Size()) + + err = queue.Enqueue(6) + assert.IsNotNil(err) +} + +func TestCircularQueue_Dequeue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_DeQueue") + + queue := NewCircularQueue[int](4) + assert.Equal(true, queue.IsEmpty()) + + err := queue.Enqueue(1) + assert.IsNil(err) + + err = queue.Enqueue(2) + assert.IsNil(err) + + err = queue.Enqueue(3) + assert.IsNil(err) + + val, err := queue.Dequeue() + assert.IsNil(err) + + assert.Equal(1, *val) + assert.Equal(false, queue.IsFull()) + + val, _ = queue.Dequeue() + assert.Equal(2, *val) + assert.Equal(false, queue.IsFull()) +} + +func TestCircularQueue_Front(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Front") + + queue := NewCircularQueue[int](6) + + err := queue.Enqueue(1) + assert.IsNil(err) + + err = queue.Enqueue(2) + assert.IsNil(err) + + err = queue.Enqueue(3) + assert.IsNil(err) + + val := queue.Front() + assert.IsNil(err) + assert.Equal(1, val) + assert.Equal(3, queue.Size()) +} + +func TestCircularQueue_Back(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Back") + + queue := NewCircularQueue[int](3) + assert.Equal(true, queue.IsEmpty()) + + err := queue.Enqueue(1) + assert.IsNil(err) + + err = queue.Enqueue(2) + assert.IsNil(err) + + assert.Equal(2, queue.Back()) + + val, _ := queue.Dequeue() + assert.Equal(1, *val) + + err = queue.Enqueue(3) + assert.IsNil(err) + + assert.Equal(3, queue.Back()) +} + +func TestCircularQueue_Contain(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Contain") + + queue := NewCircularQueue[int](2) + err := queue.Enqueue(1) + assert.IsNil(err) + + assert.Equal(true, queue.Contain(1)) + assert.Equal(false, queue.Contain(2)) +} + +func TestCircularQueue_Clear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Clear") + + queue := NewCircularQueue[int](3) + assert.Equal(true, queue.IsEmpty()) + assert.Equal(0, queue.Size()) + + err := queue.Enqueue(1) + assert.IsNil(err) + + assert.Equal(false, queue.IsEmpty()) + assert.Equal(1, queue.Size()) + + queue.Clear() + assert.Equal(true, queue.IsEmpty()) + assert.Equal(0, queue.Size()) +} + +func TestCircularQueue_Data(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCircularQueue_Data") + + queue := NewCircularQueue[int](3) + err := queue.Enqueue(1) + assert.IsNil(err) + + err = queue.Enqueue(2) + assert.IsNil(err) + + assert.Equal([]int{1, 2}, queue.Data()) +} diff --git a/datastructure/queue/linkedqueue.go b/datastructure/queue/linkedqueue.go new file mode 100644 index 00000000..789ca02c --- /dev/null +++ b/datastructure/queue/linkedqueue.go @@ -0,0 +1,122 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. +// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue. +package datastructure + +import ( + "errors" + "fmt" + "reflect" + + "github.com/duke-git/lancet/v2/datastructure" +) + +// LinkedQueue implements queue with link list +type LinkedQueue[T any] struct { + head *datastructure.QueueNode[T] + tail *datastructure.QueueNode[T] + length int +} + +// NewLinkedQueue return a empty LinkedQueue pointer +func NewLinkedQueue[T any]() *LinkedQueue[T] { + return &LinkedQueue[T]{head: nil, tail: nil, length: 0} +} + +// Data return slice of queue data +func (q *LinkedQueue[T]) Data() []T { + res := make([]T, 0, q.length) + current := q.head + + for current != nil { + res = append(res, current.Value) + current = current.Next + } + return res +} + +// Size return length of queue data +func (q *LinkedQueue[T]) Size() int { + return q.length +} + +// IsEmpty checks if queue is empty or not +func (q *LinkedQueue[T]) IsEmpty() bool { + return q.length == 0 +} + +// Enqueue put element into queue +func (q *LinkedQueue[T]) Enqueue(value T) { + newNode := datastructure.NewQueueNode(value) + + if q.IsEmpty() { + q.head = newNode + q.tail = newNode + } else { + q.tail.Next = newNode + q.tail = newNode + } + q.length++ +} + +// Dequeue delete head element of queue then return it, if queue is empty, return nil and error +func (q *LinkedQueue[T]) Dequeue() (*T, error) { + if q.IsEmpty() { + return nil, errors.New("queue is empty") + } + + head := q.head + q.head = q.head.Next + q.length-- + + return &head.Value, nil +} + +// Front return front value of queue +func (q *LinkedQueue[T]) Front() (*T, error) { + if q.IsEmpty() { + return nil, errors.New("queue is empty") + } + return &q.head.Value, nil +} + +// Back return back value of queue +func (q *LinkedQueue[T]) Back() (*T, error) { + if q.IsEmpty() { + return nil, errors.New("queue is empty") + } + return &q.tail.Value, nil +} + +// Clear clear the queue data +func (q *LinkedQueue[T]) Clear() { + q.head = nil + q.tail = nil + q.length = 0 +} + +// Print all nodes info of queue link +func (q *LinkedQueue[T]) Print() { + current := q.head + info := "[ " + for current != nil { + info += fmt.Sprintf("%+v, ", current) + current = current.Next + } + info += " ]" + fmt.Println(info) +} + +// Contain checks if the value is in queue or not +func (q *LinkedQueue[T]) Contain(value T) bool { + current := q.head + for current != nil { + if reflect.DeepEqual(current.Value, value) { + return true + } + current = current.Next + } + return false +} diff --git a/datastructure/queue/linkedqueue_test.go b/datastructure/queue/linkedqueue_test.go new file mode 100644 index 00000000..82136dca --- /dev/null +++ b/datastructure/queue/linkedqueue_test.go @@ -0,0 +1,106 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestLinkedQueue_Enqueue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_Enqueue") + + queue := NewLinkedQueue[int]() + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + assert.Equal([]int{1, 2, 3}, queue.Data()) + assert.Equal(3, queue.Size()) +} + +func TestLinkedQueue_Dequeue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_DeQueue") + + queue := NewLinkedQueue[int]() + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val, _ := queue.Dequeue() + + queue.Print() + + assert.Equal([]int{2, 3}, queue.Data()) + assert.Equal(1, *val) +} + +func TestLinkedQueue_Front(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_Front") + + queue := NewLinkedQueue[int]() + _, err := queue.Front() + assert.IsNotNil(err) + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val, err := queue.Front() + assert.Equal(1, *val) + assert.IsNil(err) +} + +func TestLinkedQueue_Back(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_Back") + + queue := NewLinkedQueue[int]() + _, err := queue.Back() + assert.IsNotNil(err) + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + val, err := queue.Back() + assert.Equal(3, *val) + assert.IsNil(err) +} + +func TestLinkedQueue_Clear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_Back") + + queue := NewLinkedQueue[int]() + assert.Equal(true, queue.IsEmpty()) + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + assert.Equal(false, queue.IsEmpty()) + + queue.Clear() + assert.Equal(true, queue.IsEmpty()) +} + +func TestLinkedQueue_Contain(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedQueue_Contain") + + queue := NewLinkedQueue[int]() + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + assert.Equal(true, queue.Contain(1)) + assert.Equal(false, queue.Contain(4)) +} diff --git a/datastructure/queue/priorityqueue.go b/datastructure/queue/priorityqueue.go new file mode 100644 index 00000000..76a558f9 --- /dev/null +++ b/datastructure/queue/priorityqueue.go @@ -0,0 +1,118 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. +// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue. +package datastructure + +import ( + "errors" + + "github.com/duke-git/lancet/v2/constraints" +) + +// PriorityQueue is a priority queue implemented by binary heap tree +// type T should implements Compare function in constraints.Comparator interface. +type PriorityQueue[T any] struct { + items []T + size int + comparator constraints.Comparator +} + +// NewPriorityQueue return a pointer of PriorityQueue +// param `comparator` is used to compare values in the queue +func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T] { + return &PriorityQueue[T]{ + items: make([]T, capacity+1), + size: 0, + comparator: comparator, + } +} + +// IsEmpty checks if the queue is empty or not +func (q *PriorityQueue[T]) IsEmpty() bool { + return q.size == 0 +} + +// Size get number of items in the queue +func (q *PriorityQueue[T]) Size() int { + return q.size +} + +// IsFull checks if the queue capacity is full or not +func (q *PriorityQueue[T]) IsFull() bool { + return q.size == len(q.items)-1 +} + +// Data return a slice of queue data +func (q *PriorityQueue[T]) Data() []T { + data := make([]T, q.size) + for i := 1; i < q.size+1; i++ { + data[i-1] = q.items[i] + } + + return data +} + +// Enqueue insert value into queue +func (q *PriorityQueue[T]) Enqueue(val T) error { + if q.IsFull() { + return errors.New("queue is already full.") + } + q.size++ + q.items[q.size] = val + q.swim(q.size) + return nil +} + +// Dequeue delete and return max value in queue +func (q *PriorityQueue[T]) Dequeue() (T, bool) { + var val T + if q.IsEmpty() { + return val, false + } + + max := q.items[1] + + q.swap(1, q.size) + q.size-- + q.sink(1) + + //set zero value for rest values of the queue + q.items[q.size+1] = val + + return max, true +} + +// swim when child's key is larger than parent's key, exchange them. +func (q *PriorityQueue[T]) swim(index int) { + for index > 1 && q.comparator.Compare(q.items[index/2], q.items[index]) < 0 { + q.swap(index, index/2) + index = index / 2 + } +} + +// sink when parent's key smaller than child's key, exchange parent's key with larger child's key. +func (q *PriorityQueue[T]) sink(index int) { + + for 2*index <= q.size { + j := 2 * index + + // get larger child node index + if j < q.size && q.comparator.Compare(q.items[j], q.items[j+1]) < 0 { + j++ + } + // if parent larger than child, stop + if !(q.comparator.Compare(q.items[index], q.items[j]) < 0) { + break + } + + q.swap(index, j) + index = j + } +} + +// swap the two values at index i and j +func (q *PriorityQueue[T]) swap(i, j int) { + q.items[i], q.items[j] = q.items[j], q.items[i] +} diff --git a/datastructure/queue/priorityqueue_test.go b/datastructure/queue/priorityqueue_test.go new file mode 100644 index 00000000..43b8815f --- /dev/null +++ b/datastructure/queue/priorityqueue_test.go @@ -0,0 +1,74 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} +func TestPriorityQueue_Enqueue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue") + + comparator := &intComparator{} + pq := NewPriorityQueue[int](3, comparator) + + assert.Equal(true, pq.IsEmpty()) + assert.Equal(false, pq.IsFull()) + + err := pq.Enqueue(1) + assert.IsNil(err) + + err = pq.Enqueue(2) + assert.IsNil(err) + + err = pq.Enqueue(3) + assert.IsNil(err) + + assert.Equal(true, pq.IsFull()) + + queueData := pq.Data() + assert.Equal([]int{3, 1, 2}, queueData) + +} + +func TestPriorityQueue_Dequeue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue") + + comparator := &intComparator{} + pq := NewPriorityQueue[int](3, comparator) + + _, ok := pq.Dequeue() + assert.Equal(false, ok) + + err := pq.Enqueue(1) + assert.IsNil(err) + + err = pq.Enqueue(2) + assert.IsNil(err) + + err = pq.Enqueue(3) + assert.IsNil(err) + + assert.Equal(3, pq.Size()) + + val, ok := pq.Dequeue() + assert.Equal(true, ok) + assert.Equal(3, val) +} diff --git a/datastructure/set/set.go b/datastructure/set/set.go new file mode 100644 index 00000000..3608a14a --- /dev/null +++ b/datastructure/set/set.go @@ -0,0 +1,220 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate. +package datastructure + +import "sort" + +// Set is a data container, like slice, but element of set is not duplicate. +type Set[T comparable] map[T]struct{} + +// New create a instance of set from given values. +func New[T comparable](items ...T) Set[T] { + set := make(Set[T], len(items)) + set.Add(items...) + return set +} + +// FromSlice create a set from given slice. +func FromSlice[T comparable](items []T) Set[T] { + set := make(Set[T], len(items)) + for _, item := range items { + set.Add(item) + } + return set +} + +// Add items to set +func (s Set[T]) Add(items ...T) { + for _, v := range items { + s[v] = struct{}{} + } +} + +// AddIfNotExist checks if item exists in the set, +// it adds the item to set and returns true if it does not exist in the set, +// or else it does nothing and returns false. +func (s Set[T]) AddIfNotExist(item T) bool { + if !s.Contain(item) { + if _, ok := s[item]; !ok { + s[item] = struct{}{} + return true + } + } + return false +} + +// AddIfNotExistBy checks if item exists in the set and pass the `checker` function +// it adds the item to set and returns true if it does not exists in the set and +// function `checker` returns true, or else it does nothing and returns false. +func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool { + if !s.Contain(item) { + if checker(item) { + if _, ok := s[item]; !ok { + s[item] = struct{}{} + return true + } + } + } + return false +} + +// Contain checks if set contains item or not +func (s Set[T]) Contain(item T) bool { + _, ok := s[item] + return ok +} + +// ContainAll checks if set contains other set +func (s Set[T]) ContainAll(other Set[T]) bool { + for k := range other { + _, ok := s[k] + if !ok { + return false + } + } + return true +} + +// Clone return a copy of set +func (s Set[T]) Clone() Set[T] { + set := FromSlice(s.ToSlice()) + return set +} + +// Delete item of set +func (s Set[T]) Delete(items ...T) { + for _, v := range items { + delete(s, v) + } +} + +// Equal checks if two set has same elements or not +func (s Set[T]) Equal(other Set[T]) bool { + if s.Size() != other.Size() { + return false + } + + return s.ContainAll(other) && other.ContainAll(s) +} + +// Iterate call function by every element of set +func (s Set[T]) Iterate(fn func(item T)) { + for v := range s { + fn(v) + } +} + +// IsEmpty checks the set is empty or not +func (s Set[T]) IsEmpty() bool { + return len(s) == 0 +} + +// Size get the number of elements in set +func (s Set[T]) Size() int { + return len(s) +} + +// Values return all values of set +// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead. +// +// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set. +func (s Set[T]) Values() []T { + return s.ToSlice() +} + +// Union creates a new set contain all element of set s and other +func (s Set[T]) Union(other Set[T]) Set[T] { + set := s.Clone() + set.Add(other.Values()...) + return set +} + +// Intersection creates a new set whose element both be contained in set s and other +func (s Set[T]) Intersection(other Set[T]) Set[T] { + set := New[T]() + s.Iterate(func(value T) { + if other.Contain(value) { + set.Add(value) + } + }) + + return set +} + +// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets +func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] { + set := New[T]() + s.Iterate(func(value T) { + if !other.Contain(value) { + set.Add(value) + } + }) + + other.Iterate(func(value T) { + if !s.Contain(value) { + set.Add(value) + } + }) + + return set +} + +// Minus creates a set of whose element in origin set but not in compared set +func (s Set[T]) Minus(comparedSet Set[T]) Set[T] { + set := New[T]() + + s.Iterate(func(value T) { + if !comparedSet.Contain(value) { + set.Add(value) + } + }) + + return set +} + +// EachWithBreak iterates over elements of a set and invokes function for each element, +// when iteratee return false, will break the for each loop. +func (s Set[T]) EachWithBreak(iteratee func(item T) bool) { + for _, v := range s.Values() { + if !iteratee(v) { + break + } + } +} + +// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false. +func (s Set[T]) Pop() (v T, ok bool) { + if len(s) > 0 { + for item := range s { + v = item + delete(s, item) + return v, true + } + } + + return v, false +} + +// ToSlice returns a slice containing all values of the set. +func (s Set[T]) ToSlice() []T { + if s.IsEmpty() { + return []T{} + } + result := make([]T, 0, s.Size()) + s.Iterate(func(value T) { + result = append(result, value) + }) + + return result +} + +// ToSortedSlice returns a sorted slice containing all values of the set. +func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T { + result := s.ToSlice() + sort.Slice(result, func(i, j int) bool { + return less(result[i], result[j]) + }) + return result +} diff --git a/datastructure/set/set_test.go b/datastructure/set/set_test.go new file mode 100644 index 00000000..91432376 --- /dev/null +++ b/datastructure/set/set_test.go @@ -0,0 +1,335 @@ +package datastructure + +import ( + "reflect" + "sort" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestSet_FromSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_FromSlice") + + s1 := FromSlice([]int{1, 2, 2, 3}) + assert.Equal(3, s1.Size()) + assert.Equal(true, s1.Contain(1)) + assert.Equal(true, s1.Contain(2)) + assert.Equal(true, s1.Contain(3)) + + s2 := FromSlice([]int{}) + assert.Equal(0, s2.Size()) +} + +func TestSet_Add(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Add") + + set := New[int]() + set.Add(1, 2, 3) + + cmpSet := New(1, 2, 3) + + assert.Equal(true, set.Equal(cmpSet)) +} + +func TestSet_AddIfNotExist(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_AddIfNotExist") + + set := New[int]() + set.Add(1, 2, 3) + + assert.Equal(false, set.AddIfNotExist(1)) + assert.Equal(true, set.AddIfNotExist(4)) + assert.Equal(New(1, 2, 3, 4), set) +} + +func TestSet_AddIfNotExistBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy") + + set := New[int]() + set.Add(1, 2) + + ok := set.AddIfNotExistBy(3, func(val int) bool { + return val%2 != 0 + }) + + notOk := set.AddIfNotExistBy(4, func(val int) bool { + return val%2 != 0 + }) + + assert.Equal(true, ok) + assert.Equal(false, notOk) + + assert.Equal(true, set.Contain(3)) + assert.Equal(false, set.Contain(4)) +} + +func TestSet_Contain(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Contain") + + set := New[int]() + set.Add(1, 2, 3) + + assert.Equal(true, set.Contain(1)) + assert.Equal(false, set.Contain(4)) +} + +func TestSet_ContainAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_ContainAll") + + set1 := New(1, 2, 3) + set2 := New(1, 2) + set3 := New(1, 2, 3, 4) + + assert.Equal(true, set1.ContainAll(set2)) + assert.Equal(false, set1.ContainAll(set3)) +} + +func TestSet_Clone(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Clone") + + set1 := New(1, 2, 3) + set2 := set1.Clone() + + assert.Equal(true, set1.Size() == set2.Size()) + assert.Equal(true, set1.ContainAll(set2)) +} + +func TestSet_Delete(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Delete") + + set := New[int]() + set.Add(1, 2, 3) + set.Delete(3) + + assert.Equal(true, set.Equal(New(1, 2))) +} + +func TestSet_Equal(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Equal") + + set1 := New(1, 2, 3) + set2 := New(1, 2, 3) + set3 := New(1, 2, 3, 4) + + assert.Equal(true, set1.Equal(set2)) + assert.Equal(false, set1.Equal(set3)) +} + +func TestSet_Iterate(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Iterate") + + set := New(1, 2, 3) + arr := []int{} + set.Iterate(func(value int) { + arr = append(arr, value) + }) + + assert.Equal(3, len(arr)) +} + +func TestSet_IsEmpty(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_IsEmpty") + + set := New[int]() + assert.Equal(true, set.IsEmpty()) +} + +func TestSet_Size(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Size") + + set := New(1, 2, 3) + assert.Equal(3, set.Size()) +} + +func TestSet_Values(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Values") + + set := New(1, 2, 3) + values := set.Values() + + assert.Equal(3, len(values)) +} + +func TestSet_Union(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Union") + + set1 := New(1, 2, 3) + set2 := New(2, 3, 4, 5) + + unionSet := set1.Union(set2) + + assert.Equal(New(1, 2, 3, 4, 5), unionSet) +} + +func TestSet_Intersection(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Intersection") + + set1 := New(1, 2, 3) + set2 := New(2, 3, 4, 5) + intersectionSet := set1.Intersection(set2) + + assert.Equal(New(2, 3), intersectionSet) +} + +func TestSet_SymmetricDifference(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_SymmetricDifference") + + set1 := New(1, 2, 3) + set2 := New(2, 3, 4, 5) + + assert.Equal(New(1, 4, 5), set1.SymmetricDifference(set2)) +} + +func TestSet_Minus(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_Minus") + + set1 := New(1, 2, 3) + set2 := New(2, 3, 4, 5) + set3 := New(2, 3) + + assert.Equal(New(1), set1.Minus(set2)) + assert.Equal(New(4, 5), set2.Minus(set3)) +} + +func TestEachWithBreak(t *testing.T) { + // s := New(1, 2, 3, 4, 5) + + // var sum int + + // s.EachWithBreak(func(n int) bool { + // if n > 3 { + // return false + // } + // sum += n + // return true + // }) + + // assert := internal.NewAssert(t, "TestEachWithBreak") + // assert.Equal(6, sum) +} + +func TestPop(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSet_Pop") + + s := New[int]() + + val, ok := s.Pop() + assert.Equal(0, val) + assert.Equal(false, ok) + + s = New(1, 2, 3, 4, 5) + sl := s.ToSlice() + + val, ok = s.Pop() + assert.Equal(false, s.Contain(val)) + assert.Equal(true, ok) + assert.Equal(len(sl)-1, s.Size()) + + var found bool + + for _, v := range sl { + if v == val { + found = true + } + } + + assert.Equal(true, found) +} + +func TestSet_ToSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_ToSlice") + + set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1}) + set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0}) + set3 := New[string]() + + slice1 := set1.ToSlice() + slice2 := set2.ToSlice() + slice3 := set3.ToSlice() + + sort.Ints(slice1) + sort.Float64s(slice2) + + assert.Equal(5, len(slice1)) + assert.Equal(4, len(slice2)) + assert.Equal(0, len(slice3)) + + assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7})) + assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25})) + assert.Equal("[]string", reflect.TypeOf(slice3).String()) +} + +func TestSet_ToSortedSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSet_ToSortedSlice") + + set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1}) + set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0}) + + type Person struct { + Name string + Age int + } + set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}}) + + slice1 := set1.ToSortedSlice(func(v1, v2 int) bool { + return v1 < v2 + }) + slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool { + return v2 < v1 + }) + slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool { + return v1.Age < v2.Age + }) + + assert.Equal(5, len(slice1)) + assert.Equal(4, len(slice2)) + assert.Equal(3, len(slice3)) + + assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7})) + assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65})) + assert.Equal(true, reflect.DeepEqual(slice3, []Person{ + {"Jerry", 18}, + {"Tom", 20}, + {"Spike", 25}, + })) +} diff --git a/datastructure/stack/arraystack.go b/datastructure/stack/arraystack.go new file mode 100644 index 00000000..b0cbe88a --- /dev/null +++ b/datastructure/stack/arraystack.go @@ -0,0 +1,66 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack. +package datastructure + +import "errors" + +// ArrayStack implements stack with slice +type ArrayStack[T any] struct { + data []T + length int +} + +// NewArrayStack return a empty ArrayStack pointer +func NewArrayStack[T any]() *ArrayStack[T] { + return &ArrayStack[T]{data: []T{}, length: 0} +} + +// Data return stack data +func (s *ArrayStack[T]) Data() []T { + return s.data +} + +// Size return length of stack data +func (s *ArrayStack[T]) Size() int { + return s.length +} + +// IsEmpty checks if stack is empty or not +func (s *ArrayStack[T]) IsEmpty() bool { + return s.length == 0 +} + +// Push element into stack +func (s *ArrayStack[T]) Push(value T) { + s.data = append([]T{value}, s.data...) + s.length++ +} + +// Pop delete the top element of stack then return it, if stack is empty, return nil and error +func (s *ArrayStack[T]) Pop() (*T, error) { + if s.IsEmpty() { + return nil, errors.New("stack is empty") + } + + topItem := s.data[0] + s.data = s.data[1:] + s.length-- + + return &topItem, nil +} + +// Peak return the top element of stack +func (s *ArrayStack[T]) Peak() (*T, error) { + if s.IsEmpty() { + return nil, errors.New("stack is empty") + } + return &s.data[0], nil +} + +// Clear the stack data +func (s *ArrayStack[T]) Clear() { + s.data = []T{} + s.length = 0 +} diff --git a/datastructure/stack/arraystack_test.go b/datastructure/stack/arraystack_test.go new file mode 100644 index 00000000..4bf6543d --- /dev/null +++ b/datastructure/stack/arraystack_test.go @@ -0,0 +1,82 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestArrayStack_Push(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayStack_Push") + + stack := NewArrayStack[int]() + stack.Push(1) + stack.Push(2) + stack.Push(3) + + values := stack.Data() + length := stack.Size() + + assert.Equal([]int{3, 2, 1}, values) + assert.Equal(3, length) +} + +func TestArrayStack_Pop(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayStack_Pop") + + stack := NewArrayStack[int]() + _, err := stack.Pop() + assert.IsNotNil(err) + + stack.Push(1) + stack.Push(2) + stack.Push(3) + + topItem, err := stack.Pop() + assert.IsNil(err) + assert.Equal(3, *topItem) + + assert.Equal([]int{2, 1}, stack.Data()) +} + +func TestArrayStack_Peak(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayStack_Peak") + + stack := NewArrayStack[int]() + _, err := stack.Peak() + assert.IsNotNil(err) + + stack.Push(1) + stack.Push(2) + stack.Push(3) + + topItem, err := stack.Peak() + assert.IsNil(err) + assert.Equal(3, *topItem) + + assert.Equal([]int{3, 2, 1}, stack.Data()) +} + +func TestArrayStack_Clear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestArrayStack_Clear") + + stack := NewArrayStack[int]() + assert.Equal(true, stack.IsEmpty()) + assert.Equal(0, stack.Size()) + + stack.Push(1) + assert.Equal(false, stack.IsEmpty()) + assert.Equal(1, stack.Size()) + + stack.Clear() + assert.Equal(true, stack.IsEmpty()) + assert.Equal(0, stack.Size()) +} diff --git a/datastructure/stack/linkedstack.go b/datastructure/stack/linkedstack.go new file mode 100644 index 00000000..02d9a921 --- /dev/null +++ b/datastructure/stack/linkedstack.go @@ -0,0 +1,98 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack. +package datastructure + +import ( + "errors" + "fmt" + + "github.com/duke-git/lancet/v2/datastructure" +) + +// LinkedStack implements stack with link list +type LinkedStack[T any] struct { + top *datastructure.StackNode[T] + length int +} + +// NewLinkedStack return a empty LinkedStack pointer +func NewLinkedStack[T any]() *LinkedStack[T] { + return &LinkedStack[T]{top: nil, length: 0} +} + +// Data return stack data +func (s *LinkedStack[T]) Data() []T { + res := make([]T, 0, s.length) + current := s.top + + for current != nil { + res = append(res, current.Value) + current = current.Next + } + return res +} + +// Size return length of stack data +func (s *LinkedStack[T]) Size() int { + return s.length +} + +// IsEmpty checks if stack is empty or not +func (s *LinkedStack[T]) IsEmpty() bool { + return s.length == 0 +} + +// Push element into stack +func (s *LinkedStack[T]) Push(value T) { + newNode := datastructure.NewStackNode(value) + top := s.top + if top == nil { + s.top = newNode + } else { + newNode.Next = top + s.top = newNode + } + + s.length++ +} + +// Pop delete the top element of stack then return it, if stack is empty, return nil and error +func (s *LinkedStack[T]) Pop() (*T, error) { + if s.IsEmpty() { + return nil, errors.New("stack is empty") + } + + top := s.top + s.top = s.top.Next + s.length-- + + return &top.Value, nil +} + +// Peak return the top element of stack then return it +func (s *LinkedStack[T]) Peak() (*T, error) { + if s.IsEmpty() { + return nil, errors.New("stack is empty") + } + return &s.top.Value, nil +} + +// Clear clear the stack data +func (s *LinkedStack[T]) Clear() { + s.top = nil + s.length = 0 +} + +// Print all nodes info of stack link +func (s *LinkedStack[T]) Print() { + current := s.top + info := "[ " + for current != nil { + info += fmt.Sprintf("%+v, ", current) + current = current.Next + } + info += " ]" + fmt.Println(info) +} diff --git a/datastructure/stack/linkedstack_test.go b/datastructure/stack/linkedstack_test.go new file mode 100644 index 00000000..c80b3c90 --- /dev/null +++ b/datastructure/stack/linkedstack_test.go @@ -0,0 +1,83 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestLinkedStack_Push(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedStack_Push") + + stack := NewLinkedStack[int]() + stack.Push(1) + stack.Push(2) + stack.Push(3) + + values := stack.Data() + size := stack.Size() + + assert.Equal([]int{3, 2, 1}, values) + assert.Equal(3, size) +} + +func TestLinkedStack_Pop(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedStack_Pop") + + stack := NewLinkedStack[int]() + _, err := stack.Pop() + assert.IsNotNil(err) + + stack.Push(1) + stack.Push(2) + stack.Push(3) + + topItem, err := stack.Pop() + assert.IsNil(err) + assert.Equal(3, *topItem) + + stack.Print() + assert.Equal([]int{2, 1}, stack.Data()) +} + +func TestLinkedStack_Peak(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedStack_Peak") + + stack := NewLinkedStack[int]() + _, err := stack.Peak() + assert.IsNotNil(err) + + stack.Push(1) + stack.Push(2) + stack.Push(3) + + topItem, err := stack.Peak() + assert.IsNil(err) + assert.Equal(3, *topItem) + + assert.Equal([]int{3, 2, 1}, stack.Data()) +} + +func TestLinkedStack_Empty(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLinkedStack_Empty") + + stack := NewLinkedStack[int]() + assert.Equal(true, stack.IsEmpty()) + assert.Equal(0, stack.Size()) + + stack.Push(1) + assert.Equal(false, stack.IsEmpty()) + assert.Equal(1, stack.Size()) + + stack.Clear() + assert.Equal(true, stack.IsEmpty()) + assert.Equal(0, stack.Size()) +} diff --git a/datastructure/tree/bstree.go b/datastructure/tree/bstree.go new file mode 100644 index 00000000..fd63011d --- /dev/null +++ b/datastructure/tree/bstree.go @@ -0,0 +1,112 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package datastructure contains some data structure. BSTree is binary search tree. +package datastructure + +import ( + "math" + + "github.com/duke-git/lancet/v2/constraints" + "github.com/duke-git/lancet/v2/datastructure" +) + +// BSTree is a binary search tree data structure in which each node has at most two children, +// which are referred to as the left child and the right child. +// In BSTree: leftNode < rootNode < rightNode +// type T should implements Compare function in constraints.Comparator interface. +type BSTree[T any] struct { + root *datastructure.TreeNode[T] + comparator constraints.Comparator +} + +// NewBSTree create a BSTree pointer +// param `comparator` is used to compare values in the tree +func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T] { + root := datastructure.NewTreeNode(rootData) + return &BSTree[T]{root, comparator} +} + +// InsertNode insert data into BSTree +func (t *BSTree[T]) Insert(data T) { + root := t.root + newNode := datastructure.NewTreeNode(data) + if root == nil { + t.root = newNode + } else { + insertTreeNode(root, newNode, t.comparator) + } +} + +// DeletetNode delete data into BSTree +func (t *BSTree[T]) Delete(data T) { + deleteTreeNode(t.root, data, t.comparator) +} + +// NodeLevel get node level in BSTree +func (t *BSTree[T]) NodeLevel(node *datastructure.TreeNode[T]) int { + if node == nil { + return 0 + } + left := float64(t.NodeLevel(node.Left)) + right := float64(t.NodeLevel(node.Right)) + + return int(math.Max(left, right)) + 1 +} + +// PreOrderTraverse traverse tree node in pre order +func (t *BSTree[T]) PreOrderTraverse() []T { + return preOrderTraverse(t.root) +} + +// PostOrderTraverse traverse tree node in post order +func (t *BSTree[T]) PostOrderTraverse() []T { + return postOrderTraverse(t.root) +} + +// InOrderTraverse traverse tree node in mid order +func (t *BSTree[T]) InOrderTraverse() []T { + return inOrderTraverse(t.root) +} + +// LevelOrderTraverse traverse tree node in level order +func (t *BSTree[T]) LevelOrderTraverse() []T { + traversal := make([]T, 0) + levelOrderTraverse(t.root, &traversal) + return traversal +} + +// Depth returns the calculated depth of a binary saerch tree +func (t *BSTree[T]) Depth() int { + return calculateDepth(t.root, 0) +} + +// IsSubTree checks if the tree `t` has `subTree` or not +func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool { + return hasSubTree(t.root, subTree.root, t.comparator) +} + +func hasSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], + comparator constraints.Comparator) bool { + result := false + + if superTreeRoot != nil && subTreeRoot != nil { + if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) == 0 { + result = isSubTree(superTreeRoot, subTreeRoot, comparator) + } + if !result { + result = hasSubTree(superTreeRoot.Left, subTreeRoot, comparator) + } + if !result { + result = hasSubTree(superTreeRoot.Right, subTreeRoot, comparator) + } + } + return result +} + +// Print the bstree structure +func (t *BSTree[T]) Print() { + maxLevel := t.NodeLevel(t.root) + nodes := []*datastructure.TreeNode[T]{t.root} + printTreeNodes(nodes, 1, maxLevel) +} diff --git a/datastructure/tree/bstree_test.go b/datastructure/tree/bstree_test.go new file mode 100644 index 00000000..60716613 --- /dev/null +++ b/datastructure/tree/bstree_test.go @@ -0,0 +1,140 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func TestBSTree_PreOrderTraverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_PreOrderTraverse") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + acturl := bstree.PreOrderTraverse() + assert.Equal([]int{6, 5, 2, 4, 7}, acturl) +} + +func TestBSTree_PostOrderTraverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_PostOrderTraverse") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + acturl := bstree.PostOrderTraverse() + assert.Equal([]int{5, 2, 4, 7, 6}, acturl) +} + +func TestBSTree_InOrderTraverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_InOrderTraverse") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + acturl := bstree.InOrderTraverse() + assert.Equal([]int{2, 4, 5, 6, 7}, acturl) +} + +func TestBSTree_LevelOrderTraverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_LevelOrderTraverse") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + acturl := bstree.LevelOrderTraverse() + assert.Equal([]int{6, 5, 7, 2, 4}, acturl) +} + +func TestBSTree_Delete(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_Delete") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + bstree.Delete(4) + + acturl1 := bstree.InOrderTraverse() + assert.Equal([]int{2, 5, 6, 7}, acturl1) +} + +func TestBSTree_Depth(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_Depth") + + bstree := NewBSTree(6, &intComparator{}) + + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + assert.Equal(bstree.Depth(), 4) +} + +func TestBSTree_IsSubTree(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBSTree_IsSubTree") + + superTree := NewBSTree(8, &intComparator{}) + superTree.Insert(4) + superTree.Insert(5) + superTree.Insert(6) + superTree.Insert(9) + superTree.Insert(4) + + superTree.Print() + + subTree := NewBSTree(5, &intComparator{}) + subTree.Insert(4) + subTree.Insert(6) + + assert.Equal(true, superTree.HasSubTree(subTree)) + assert.Equal(false, subTree.HasSubTree(superTree)) +} diff --git a/datastructure/tree/tree_internal.go b/datastructure/tree/tree_internal.go new file mode 100644 index 00000000..424d34db --- /dev/null +++ b/datastructure/tree/tree_internal.go @@ -0,0 +1,238 @@ +package datastructure + +import ( + "fmt" + "math" + + "github.com/duke-git/lancet/v2/constraints" + "github.com/duke-git/lancet/v2/datastructure" +) + +func preOrderTraverse[T any](node *datastructure.TreeNode[T]) []T { + data := []T{} + if node != nil { + data = append(data, node.Value) + data = append(data, preOrderTraverse(node.Left)...) + data = append(data, preOrderTraverse(node.Right)...) + } + return data +} + +func postOrderTraverse[T any](node *datastructure.TreeNode[T]) []T { + data := []T{} + if node != nil { + data = append(data, preOrderTraverse(node.Left)...) + data = append(data, preOrderTraverse(node.Right)...) + data = append(data, node.Value) + } + return data +} + +func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T { + data := []T{} + if node != nil { + data = append(data, inOrderTraverse(node.Left)...) + data = append(data, node.Value) + data = append(data, inOrderTraverse(node.Right)...) + } + return data +} + +// func preOrderPrint[T any](node *datastructure.TreeNode[T]) { +// if node == nil { +// return +// } + +// fmt.Printf("%v, ", node.Value) +// preOrderPrint(node.Left) +// preOrderPrint(node.Right) +// } + +// func postOrderPrint[T any](node *datastructure.TreeNode[T]) { +// if node == nil { +// return +// } + +// postOrderPrint(node.Left) +// postOrderPrint(node.Right) +// fmt.Printf("%v, ", node.Value) +// } + +// func inOrderPrint[T any](node *datastructure.TreeNode[T]) { +// if node == nil { +// return +// } + +// inOrderPrint(node.Left) +// fmt.Printf("%v, ", node.Value) +// inOrderPrint(node.Right) +// } + +func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) { + var q []*datastructure.TreeNode[T] // queue + var n *datastructure.TreeNode[T] // temp node + + q = append(q, root) + + for len(q) != 0 { + n, q = q[0], q[1:] + *traversal = append(*traversal, n.Value) + if n.Left != nil { + q = append(q, n.Left) + } + if n.Right != nil { + q = append(q, n.Right) + } + } +} + +func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator constraints.Comparator) { + if comparator.Compare(newNode.Value, rootNode.Value) == -1 { + if rootNode.Left == nil { + rootNode.Left = newNode + } else { + insertTreeNode(rootNode.Left, newNode, comparator) + } + } else { + if rootNode.Right == nil { + rootNode.Right = newNode + } else { + insertTreeNode(rootNode.Right, newNode, comparator) + } + } +} + +// todo, delete root node failed +func deleteTreeNode[T any](node *datastructure.TreeNode[T], data T, comparator constraints.Comparator) *datastructure.TreeNode[T] { + if node == nil { + return nil + } + if comparator.Compare(data, node.Value) == -1 { + node.Left = deleteTreeNode(node.Left, data, comparator) + } else if comparator.Compare(data, node.Value) == 1 { + node.Right = deleteTreeNode(node.Right, data, comparator) + } else { + if node.Left == nil { + node = node.Right + } else if node.Right == nil { + node = node.Left + } else { + l := node.Right + d := inOrderSuccessor(l) + d.Left = node.Left + return node.Right + } + } + + return node +} + +func inOrderSuccessor[T any](root *datastructure.TreeNode[T]) *datastructure.TreeNode[T] { + cur := root + for cur.Left != nil { + cur = cur.Left + } + return cur +} + +func printTreeNodes[T any](nodes []*datastructure.TreeNode[T], level, maxLevel int) { + if len(nodes) == 0 || isAllNil(nodes) { + return + } + + floor := maxLevel - level + endgeLines := int(math.Pow(float64(2), (math.Max(float64(floor)-1, 0)))) + firstSpaces := int(math.Pow(float64(2), float64(floor))) - 1 + betweenSpaces := int(math.Pow(float64(2), float64(floor)+1)) - 1 + + printSpaces(firstSpaces) + + newNodes := make([]*datastructure.TreeNode[T], 0, len(nodes)*2) + for _, node := range nodes { + if node != nil { + fmt.Printf("%v", node.Value) + newNodes = append(newNodes, node.Left) + newNodes = append(newNodes, node.Right) + } else { + newNodes = append(newNodes, nil) + newNodes = append(newNodes, nil) + printSpaces(1) + } + + printSpaces(betweenSpaces) + } + + fmt.Println("") + + for i := 1; i <= endgeLines; i++ { + for j := 0; j < len(nodes); j++ { + printSpaces(firstSpaces - i) + if nodes[j] == nil { + printSpaces(endgeLines + endgeLines + i + 1) + continue + } + + if nodes[j].Left != nil { + fmt.Print("/") + } else { + printSpaces(1) + } + + printSpaces(i + i - 1) + + if nodes[j].Right != nil { + fmt.Print("\\") + } else { + printSpaces(1) + } + printSpaces(endgeLines + endgeLines - 1) + } + fmt.Println("") + } + + printTreeNodes(newNodes, level+1, maxLevel) +} + +// printSpaces +func printSpaces(n int) { + for i := 0; i < n; i++ { + fmt.Print(" ") + } +} + +func isAllNil[T any](nodes []*datastructure.TreeNode[T]) bool { + for _, v := range nodes { + if v != nil { + return false + } + } + return true +} + +func calculateDepth[T any](node *datastructure.TreeNode[T], depth int) int { + if node == nil { + return depth + } + return max(calculateDepth(node.Left, depth+1), calculateDepth(node.Right, depth+1)) +} + +func isSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], comparator constraints.Comparator) bool { + if subTreeRoot == nil { + return true + } + if superTreeRoot == nil { + return false + } + if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) != 0 { + return false + } + result := isSubTree(superTreeRoot.Left, subTreeRoot.Left, comparator) && isSubTree(superTreeRoot.Right, subTreeRoot.Right, comparator) + return result +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/datetime/conversion.go b/datetime/conversion.go new file mode 100644 index 00000000..4dc80e8b --- /dev/null +++ b/datetime/conversion.go @@ -0,0 +1,68 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license. + +package datetime + +import "time" + +type theTime struct { + unix int64 +} + +// NewUnixNow return unix timestamp of current time. +// Play: https://go.dev/play/p/U4PPx-9D0oz +func NewUnixNow() *theTime { + return &theTime{unix: time.Now().Unix()} +} + +// NewUnix return unix timestamp of specified time. +// Play: https://go.dev/play/p/psoSuh_kLRt +func NewUnix(unix int64) *theTime { + return &theTime{unix: unix} +} + +// NewFormat return unix timestamp of specified time string, t should be "yyyy-mm-dd hh:mm:ss". +// Play: https://go.dev/play/p/VkW08ZOaXPZ +func NewFormat(t string) (*theTime, error) { + timeLayout := "2006-01-02 15:04:05" + loc := time.FixedZone("CST", 8*3600) + tt, err := time.ParseInLocation(timeLayout, t, loc) + if err != nil { + return nil, err + } + return &theTime{unix: tt.Unix()}, nil +} + +// NewISO8601 return unix timestamp of specified iso8601 time string. +// Play: https://go.dev/play/p/mkhOHQkdeA2 +func NewISO8601(iso8601 string) (*theTime, error) { + t, err := time.ParseInLocation(time.RFC3339, iso8601, time.UTC) + if err != nil { + return nil, err + } + return &theTime{unix: t.Unix()}, nil +} + +// ToUnix return unix timestamp. +// Play: https://go.dev/play/p/_LUiwAdocjy +func (t *theTime) ToUnix() int64 { + return t.unix +} + +// ToFormat return the time string 'yyyy-mm-dd hh:mm:ss' of unix time. +// Play: https://go.dev/play/p/VkW08ZOaXPZ +func (t *theTime) ToFormat() string { + return time.Unix(t.unix, 0).Format("2006-01-02 15:04:05") +} + +// ToFormatForTpl return the time string which format is specified tpl. +// Play: https://go.dev/play/p/nyXxXcQJ8L5 +func (t *theTime) ToFormatForTpl(tpl string) string { + return time.Unix(t.unix, 0).Format(tpl) +} + +// ToFormatForTpl return iso8601 time string. +// Play: https://go.dev/play/p/mkhOHQkdeA2 +func (t *theTime) ToIso8601() string { + return time.Unix(t.unix, 0).Format(time.RFC3339) +} diff --git a/datetime/conversion_test.go b/datetime/conversion_test.go new file mode 100644 index 00000000..b1d05370 --- /dev/null +++ b/datetime/conversion_test.go @@ -0,0 +1,55 @@ +package datetime + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestToUnix(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToUnix") + + tm1 := NewUnixNow() + unixTimestamp := tm1.ToUnix() + tm2 := NewUnix(unixTimestamp) + + assert.Equal(tm1, tm2) +} + +func TestToFormat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToFormat") + + tm, err := NewFormat("2022-03-18 17:04:05") + t.Log("TestToFormat", tm.ToFormat()) + assert.IsNil(err) +} + +func TestToFormatForTpl(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToFormatForTpl") + + _, err := NewFormat("2022/03/18 17:04:05") + assert.IsNotNil(err) + + tm, err := NewFormat("2022-03-18 17:04:05") + t.Log("TestToFormatForTpl", tm.ToFormatForTpl("2006/01/02 15:04:05")) + assert.IsNil(err) +} + +func TestToIso8601(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToIso8601") + + _, err := NewISO8601("2022-03-18 17:04:05") + assert.IsNotNil(err) + + tm, err := NewISO8601("2006-01-02T15:04:05.999Z") + t.Log("TestToIso8601", tm.ToIso8601()) + assert.IsNil(err) +} diff --git a/datetime/datetime.go b/datetime/datetime.go index cf35bf85..b4b687a7 100644 --- a/datetime/datetime.go +++ b/datetime/datetime.go @@ -3,29 +3,35 @@ // Package datetime implements some functions to format date and time. // Note: -// 1. `format` param in FormatTimeToStr function should be as flow: -//"yyyy-mm-dd hh:mm:ss" -//"yyyy-mm-dd hh:mm" -//"yyyy-mm-dd hh" -//"yyyy-mm-dd" -//"yyyy-mm" -//"mm-dd" -//"dd-mm-yy hh:mm:ss" -//"yyyy/mm/dd hh:mm:ss" -//"yyyy/mm/dd hh:mm" -//"yyyy/mm/dd hh" -//"yyyy/mm/dd" -//"yyyy/mm" -//"mm/dd" -//"dd/mm/yy hh:mm:ss" -//"yyyy" -//"mm" -//"hh:mm:ss" -//"mm:ss" +// 1. `format` param in FormatTimeToStr function should be as flow (case no sensitive): +// "yyyy-mm-dd hh:mm:ss" +// "yyyy-mm-dd hh:mm" +// "yyyy-mm-dd hh" +// "yyyy-mm-dd" +// "yyyy-mm" +// "mm-dd" +// "dd-mm-yy hh:mm:ss" +// "yyyy/mm/dd hh:mm:ss" +// "yyyy/mm/dd hh:mm" +// "yyyy/mm/dd hh" +// "yyyy/mm/dd" +// "yyyy/mm" +// "mm/dd" +// "dd/mm/yy hh:mm:ss" +// "yyyymmdd" +// "mmddyy" +// "yyyy" +// "yy" +// "mm" +// "hh:mm:ss" +// "hh:mm" +// "mm:ss" package datetime import ( "fmt" + "runtime" + "strings" "time" ) @@ -35,7 +41,7 @@ func init() { timeFormat = map[string]string{ "yyyy-mm-dd hh:mm:ss": "2006-01-02 15:04:05", "yyyy-mm-dd hh:mm": "2006-01-02 15:04", - "yyyy-mm-dd hh": "2006-01-02 15:04", + "yyyy-mm-dd hh": "2006-01-02 15", "yyyy-mm-dd": "2006-01-02", "yyyy-mm": "2006-01", "mm-dd": "01-02", @@ -47,136 +53,499 @@ func init() { "yyyy/mm": "2006/01", "mm/dd": "01/02", "dd/mm/yy hh:mm:ss": "02/01/06 15:04:05", + "yyyymmdd": "20060102", + "mmddyy": "010206", "yyyy": "2006", + "yy": "06", "mm": "01", "hh:mm:ss": "15:04:05", + "hh:mm": "15:04", "mm:ss": "04:05", } } -// AddMinute add or sub minute to the time -func AddMinute(t time.Time, minute int64) time.Time { - return t.Add(time.Minute * time.Duration(minute)) +// AddMinute add or sub minutes to the time. +// Play: https://go.dev/play/p/nT1heB1KUUK +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 -func AddHour(t time.Time, hour int64) time.Time { - return t.Add(time.Hour * time.Duration(hour)) +// AddHour add or sub hours to the time. +// Play: https://go.dev/play/p/rcMjd7OCsi5 +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 -func AddDay(t time.Time, day int64) time.Time { - return t.Add(24 * time.Hour * time.Duration(day)) +// AddDay add or sub days to the time. +// Play: https://go.dev/play/p/dIGbs_uTdFa +func AddDay(t time.Time, days int64) time.Time { + return t.Add(24 * time.Hour * time.Duration(days)) } -// GetNowDate return format yyyy-mm-dd of current date +// 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.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. +// Play: https://go.dev/play/p/PvfkPpcpBBf func GetNowDate() string { return time.Now().Format("2006-01-02") } -// GetNowTime return format hh-mm-ss of current time +// GetNowTime return format hh-mm-ss of current time. +// Play: https://go.dev/play/p/l7BNxCkTmJS func GetNowTime() string { return time.Now().Format("15:04:05") } -// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime +// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime. +// Play: https://go.dev/play/p/pI4AqngD0al func GetNowDateTime() string { return time.Now().Format("2006-01-02 15:04:05") } -// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00) +// GetTodayStartTime return the start time of today, format: yyyy-mm-dd 00:00:00. +// Play: https://go.dev/play/p/84siyYF7t99 +func GetTodayStartTime() string { + return time.Now().Format("2006-01-02") + " 00:00:00" +} + +// GetTodayEndTime return the end time of today, format: yyyy-mm-dd 23:59:59. +// Play: https://go.dev/play/p/jjrLnfoqgn3 +func GetTodayEndTime() string { + return time.Now().Format("2006-01-02") + " 23:59:59" +} + +// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00). +// Play: https://go.dev/play/p/QmL2oIaGE3q func GetZeroHourTimestamp() int64 { ts := time.Now().Format("2006-01-02") t, _ := time.Parse("2006-01-02", ts) return t.UTC().Unix() - 8*3600 } -// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59) +// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59). +// Play: https://go.dev/play/p/UolysR3MYP1 func GetNightTimestamp() int64 { return GetZeroHourTimestamp() + 86400 - 1 } -// FormatTimeToStr convert time to string -func FormatTimeToStr(t time.Time, format string) string { - return t.Format(timeFormat[format]) +// FormatTimeToStr convert time to string. +// Play: https://go.dev/play/p/_Ia7M8H_OvE +func FormatTimeToStr(t time.Time, format string, timezone ...string) string { + tf, ok := timeFormat[strings.ToLower(format)] + if !ok { + return "" + } + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return "" + } + return t.In(loc).Format(tf) + } + return t.Format(tf) } -// FormatStrToTime convert string to time -func FormatStrToTime(str, format string) (time.Time, error) { - v, ok := timeFormat[format] +// FormatStrToTime convert string to time. +// Play: https://go.dev/play/p/1h9FwdU8ql4 +func FormatStrToTime(str, format string, timezone ...string) (time.Time, error) { + tf, ok := timeFormat[strings.ToLower(format)] if !ok { - return time.Time{}, fmt.Errorf("format %s not found", format) + return time.Time{}, fmt.Errorf("format %s not support", format) } - return time.Parse(v, str) + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return time.Time{}, err + } + + return time.ParseInLocation(tf, str, loc) + } + + return time.Parse(tf, str) } -// BeginOfMinute return beginning minute time of day +// BeginOfMinute return beginning minute time of day. +// Play: https://go.dev/play/p/ieOLVJ9CiFT func BeginOfMinute(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, t.Hour(), t.Minute(), 0, 0, t.Location()) } -// EndOfMinute return end minute time of day +// EndOfMinute return end minute time of day. +// Play: https://go.dev/play/p/yrL5wGzPj4z func EndOfMinute(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, t.Hour(), t.Minute(), 59, int(time.Second-time.Nanosecond), t.Location()) } -// BeginOfHour return beginning hour time of day +// BeginOfHour return beginning hour time of day. +// Play: https://go.dev/play/p/GhdGFnDWpYs func BeginOfHour(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, t.Hour(), 0, 0, 0, t.Location()) } -// EndOfHour return end hour time of day +// EndOfHour return end hour time of day. +// Play: https://go.dev/play/p/6ce3j_6cVqN func EndOfHour(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, t.Hour(), 59, 59, int(time.Second-time.Nanosecond), t.Location()) } -// BeginOfDay return beginning hour time of day +// BeginOfDay return beginning hour time of day. +// Play: https://go.dev/play/p/94m_UT6cWs9 func BeginOfDay(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) } -// EndOfDay return end time of day +// EndOfDay return end time of day. +// Play: https://go.dev/play/p/eMBOvmq5Ih1 func EndOfDay(t time.Time) time.Time { y, m, d := t.Date() return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) } -// BeginOfWeek return beginning week, week begin from Sunday -func BeginOfWeek(t time.Time) time.Time { - y, m, d := t.AddDate(0, 0, 0-int(BeginOfDay(t).Weekday())).Date() - return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) +// BeginOfWeek return beginning week, default week begin from Sunday. +// 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) + } + return beginOfWeek } -// EndOfWeek return end week time, week end with Saturday -func EndOfWeek(t time.Time) time.Time { - y, m, d := BeginOfWeek(t).AddDate(0, 0, 7).Add(-time.Nanosecond).Date() - return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) +// EndOfWeek return end week time, default week end with Saturday. +// 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) + } + return endWithWeek } -// BeginOfMonth return beginning of month +// BeginOfMonth return beginning of month. +// Play: https://go.dev/play/p/bWXVFsmmzwL func BeginOfMonth(t time.Time) time.Time { y, m, _ := t.Date() return time.Date(y, m, 1, 0, 0, 0, 0, t.Location()) } -// EndOfMonth return end of month +// EndOfMonth return end of month. +// Play: https://go.dev/play/p/_GWh10B3Nqi func EndOfMonth(t time.Time) time.Time { return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond) } -// BeginOfYear return beginning of year +// BeginOfYear return the date time at the begin of year. +// Play: https://go.dev/play/p/i326DSwLnV8 func BeginOfYear(t time.Time) time.Time { y, _, _ := t.Date() return time.Date(y, time.January, 1, 0, 0, 0, 0, t.Location()) } -// EndOfYear return end of year +// EndOfYear return the date time at the end of year. +// Play: https://go.dev/play/p/G01cKlMCvNm func EndOfYear(t time.Time) time.Time { return BeginOfYear(t).AddDate(1, 0, 0).Add(-time.Nanosecond) } + +// IsLeapYear check if param year is leap year or not. +// Play: https://go.dev/play/p/xS1eS2ejGew +func IsLeapYear(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} + +// BetweenSeconds returns the number of seconds between two times. +// Play: https://go.dev/play/p/n3YDRyfyXJu +func BetweenSeconds(t1 time.Time, t2 time.Time) int64 { + index := t2.Unix() - t1.Unix() + return index +} + +// DayOfYear returns which day of the year the parameter date `t` is. +// Play: https://go.dev/play/p/0hjqhTwFNlH +func DayOfYear(t time.Time) int { + y, m, d := t.Date() + firstDay := time.Date(y, 1, 1, 0, 0, 0, 0, t.Location()) + nowDate := time.Date(y, m, d, 0, 0, 0, 0, t.Location()) + + return int(nowDate.Sub(firstDay).Hours() / 24) +} + +// IsWeekend checks if passed time is weekend or not. +// Play: https://go.dev/play/p/cupRM5aZOIY +// Deprecated Use '== Weekday' instead +func IsWeekend(t time.Time) bool { + return time.Saturday == t.Weekday() || time.Sunday == t.Weekday() +} + +// NowDateOrTime return current datetime with specific format and timezone. +// Play: https://go.dev/play/p/EZ-begEjtT0 +func NowDateOrTime(format string, timezone ...string) string { + tf, ok := timeFormat[strings.ToLower(format)] + if !ok { + return "" + } + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return "" + } + + return time.Now().In(loc).Format(tf) + } + + return time.Now().Format(tf) +} + +// Timestamp return current second timestamp. +// Play: https://go.dev/play/p/iU5b7Vvjx6x +func Timestamp(timezone ...string) int64 { + t := time.Now() + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return 0 + } + + t = t.In(loc) + } + + return t.Unix() +} + +// TimestampMilli return current mill second timestamp. +// Play: https://go.dev/play/p/4gvEusOTu1T +func TimestampMilli(timezone ...string) int64 { + t := time.Now() + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return 0 + } + t = t.In(loc) + } + + return int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond) +} + +// TimestampMicro return current micro second timestamp. +// Play: https://go.dev/play/p/2maANglKHQE +func TimestampMicro(timezone ...string) int64 { + t := time.Now() + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return 0 + } + t = t.In(loc) + } + + return int64(time.Nanosecond) * t.UnixNano() / int64(time.Microsecond) +} + +// TimestampNano return current nano second timestamp. +// Play: https://go.dev/play/p/A9Oq_COrcCF +func TimestampNano(timezone ...string) int64 { + t := time.Now() + + if timezone != nil && timezone[0] != "" { + loc, err := time.LoadLocation(timezone[0]) + if err != nil { + return 0 + } + t = t.In(loc) + } + + return t.UnixNano() +} + +// TrackFuncTime track the time of function execution. +// call it at top of the func like `defer TrackFuncTime(time.Now())()` +// Play: https://go.dev/play/p/QBSEdfXHPTp +func TrackFuncTime(pre time.Time) func() { + callerName := getCallerName() + return func() { + elapsed := time.Since(pre) + fmt.Printf("Function %s execution time:\t %v", callerName, elapsed) + } +} + +func getCallerName() string { + pc, _, _, ok := runtime.Caller(2) + if !ok { + return "Unknown" + } + fn := runtime.FuncForPC(pc) + if fn == nil { + return "Unknown" + } + + fullName := fn.Name() + if lastDot := strings.LastIndex(fullName, "."); lastDot != -1 { + return fullName[lastDot+1:] + } + + return fullName +} + +// DaysBetween returns the number of days between two times. +// Play: https://go.dev/play/p/qD6qGb3TbOy +func DaysBetween(start, end time.Time) int { + duration := end.Sub(start) + days := int(duration.Hours() / 24) + + return days +} + +// GenerateDatetimesBetween returns a slice of strings between two times. +// layout: the format of the datetime string +// interval: the interval between two datetimes +// Play: https://go.dev/play/p/6kHBpAxD9ZC +func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) { + var result []string + + if start.After(end) { + start, end = end, start + } + + duration, err := time.ParseDuration(interval) + if err != nil { + return nil, err + } + + for current := start; !current.After(end); current = current.Add(duration) { + result = append(result, current.Format(layout)) + } + + return result, nil +} + +// Min returns the earliest time among the given times. +// Play: https://go.dev/play/p/MCIDvHNOGGb +func Min(t1 time.Time, times ...time.Time) time.Time { + minTime := t1 + + for _, t := range times { + if t.Before(minTime) { + minTime = t + } + } + + return minTime +} + +// Max returns the latest time among the given times. +// Play: https://go.dev/play/p/9m6JMk1LB7- +func Max(t1 time.Time, times ...time.Time) time.Time { + maxTime := t1 + + for _, t := range times { + if t.After(maxTime) { + maxTime = t + } + } + + return maxTime +} + +// MaxMin returns the latest and earliest time among the given times. +// 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 + + for _, t := range times { + if t.Before(minTime) { + minTime = t + } + + if t.After(maxTime) { + maxTime = t + } + } + + return maxTime, minTime +} diff --git a/datetime/datetime_example_test.go b/datetime/datetime_example_test.go new file mode 100644 index 00000000..f846f97c --- /dev/null +++ b/datetime/datetime_example_test.go @@ -0,0 +1,538 @@ +package datetime + +import ( + "fmt" + "reflect" + "time" +) + +func ExampleAddDay() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after1Day := AddDay(date, 1) + before1Day := AddDay(date, -1) + + fmt.Println(after1Day.Format("2006-01-02 15:04:05")) + fmt.Println(before1Day.Format("2006-01-02 15:04:05")) + + // Output: + // 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() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Hours := AddHour(date, 2) + before2Hours := AddHour(date, -2) + + fmt.Println(after2Hours.Format("2006-01-02 15:04:05")) + fmt.Println(before2Hours.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 02:00:00 + // 2020-12-31 22:00:00 +} + +func ExampleAddMinute() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Minutes := AddMinute(date, 2) + before2Minutes := AddMinute(date, -2) + + fmt.Println(after2Minutes.Format("2006-01-02 15:04:05")) + fmt.Println(before2Minutes.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 00:02:00 + // 2020-12-31 23:58:00 +} + +func ExampleAddYear() { + date, _ := time.Parse("2006-01-02", "2021-01-01") + + after2Years := AddYear(date, 2) + before2Years := AddYear(date, -2) + + fmt.Println(after2Years.Format("2006-01-02")) + fmt.Println(before2Years.Format("2006-01-02")) + + // Output: + // 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() { + result := GetNowDate() + + expected := time.Now().Format("2006-01-02") + + fmt.Println(result == expected) + + // Output: + // true +} + +func ExampleGetNowTime() { + result := GetNowTime() + + expected := time.Now().Format("15:04:05") + + fmt.Println(result == expected) + + // Output: + // true +} + +func ExampleGetNowDateTime() { + result := GetNowDateTime() + + expected := time.Now().Format("2006-01-02 15:04:05") + + fmt.Println(result == expected) + + // Output: + // true +} + +// func ExampleGetZeroHourTimestamp() { +// ts := GetZeroHourTimestamp() + +// fmt.Println(ts) + +// // Output: +// // 1673107200 +// } + +// func ExampleGetNightTimestamp() { +// ts := GetNightTimestamp() + +// fmt.Println(ts) + +// // Output: +// // 1673193599 +// } + +func ExampleFormatTimeToStr() { + datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") + + result1 := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss") + result2 := FormatTimeToStr(datetime, "yyyy-mm-dd") + result3 := FormatTimeToStr(datetime, "dd-mm-yy hh:mm:ss") + result4 := FormatTimeToStr(datetime, "yyyy-mm-dd hh") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 2021-01-02 16:04:08 + // 2021-01-02 + // 02-01-21 16:04:08 + // 2021-01-02 16 +} + +func ExampleFormatStrToTime() { + result1, _ := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss") + result2, _ := FormatStrToTime("2021-01-02", "yyyy-mm-dd") + result3, _ := FormatStrToTime("02-01-21 16:04:08", "dd-mm-yy hh:mm:ss") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2021-01-02 16:04:08 +0000 UTC + // 2021-01-02 00:00:00 +0000 UTC + // 2021-01-02 16:04:08 +0000 UTC +} + +func ExampleBeginOfMinute() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:00 +0000 UTC +} + +func ExampleEndOfMinute() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := EndOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:59.999999999 +0000 UTC +} + +func ExampleBeginOfHour() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:00:00 +0000 UTC +} + +func ExampleEndOfHour() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := EndOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:59:59.999999999 +0000 UTC +} + +func ExampleBeginOfDay() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 00:00:00 +0000 UTC +} + +func ExampleEndOfDay() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := EndOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 23:59:59.999999999 +0000 UTC +} + +func ExampleBeginOfWeek() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfWeek(input, time.Monday) + + fmt.Println(result) + + // Output: + // 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, time.Sunday) + + fmt.Println(result) + + // Output: + // 2023-01-08 23:59:59.999999999 +0000 UTC +} + +func ExampleBeginOfMonth() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} + +func ExampleEndOfMonth() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := EndOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-31 23:59:59.999999999 +0000 UTC +} + +func ExampleBeginOfYear() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := BeginOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} + +func ExampleEndOfYear() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := EndOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-12-31 23:59:59.999999999 +0000 UTC +} + +func ExampleNewUnix() { + result := NewUnix(1647597438) + + fmt.Println(result) + + // Output: + // &{1647597438} +} + +func ExampleNewUnixNow() { + tm1 := NewUnixNow() + + unixTimestamp := tm1.ToUnix() + + tm2 := NewUnix(unixTimestamp) + + fmt.Println(reflect.DeepEqual(tm1, tm2)) + + // Output: + // true +} + +// func ExampleNewFormat() { +// tm, err := NewFormat("2022-03-18 17:04:05") +// if err != nil { +// return +// } + +// result := tm.ToFormat() + +// fmt.Println(result) + +// // Output: +// // 2022-03-18 17:04:05 +// } + +// func ExampleNewISO8601() { +// tm, err := NewISO8601("2006-01-02T15:04:05.999Z") +// if err != nil { +// return +// } + +// result := tm.ToIso8601() + +// fmt.Println(result) + +// // Output: +// // 2006-01-02T23:04:05+08:00 +// } + +func ExampleIsLeapYear() { + result1 := IsLeapYear(2000) + result2 := IsLeapYear(2001) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleBetweenSeconds() { + today := time.Now() + tomorrow := AddDay(today, 1) + yesterday := AddDay(today, -1) + + result1 := BetweenSeconds(today, tomorrow) + result2 := BetweenSeconds(today, yesterday) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 86400 + // -86400 +} + +func ExampleDayOfYear() { + date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local) + result1 := DayOfYear(date1) + + date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local) + result2 := DayOfYear(date2) + + date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local) + result3 := DayOfYear(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 31 + // 1 + // 0 +} + +func ExampleIsWeekend() { + date1 := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local) + date2 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local) + date3 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local) + + result1 := IsWeekend(date1) + result2 := IsWeekend(date2) + result3 := IsWeekend(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleDaysBetween() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC) + + result := DaysBetween(start, end) + + fmt.Println(result) + + // Output: + // 9 +} + +func ExampleGenerateDatetimesBetween() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC) + + layout := "2006-01-02 15:04:05" + interval := "1h" + + result, err := GenerateDatetimesBetween(start, end, layout, interval) + + fmt.Println(result) + fmt.Println(err) + + // Output: + // [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00] + // +} + +func ExampleMin() { + result := Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(result) + + // Output: + // 2024-09-01 00:00:00 +0000 UTC +} + +func ExampleMax() { + result := Max(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(result) + + // Output: + // 2024-09-02 00:00:00 +0000 UTC +} + +func ExampleMaxMin() { + max, min := MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC)) + + fmt.Println(max) + fmt.Println(min) + + // Output: + // 2024-09-03 00:00:00 +0000 UTC + // 2024-09-01 00:00:00 +0000 UTC +} diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go index 728fe970..ed547a9c 100644 --- a/datetime/datetime_test.go +++ b/datetime/datetime_test.go @@ -4,88 +4,506 @@ import ( "testing" "time" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) +func TestAddYear(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestAddDay") + + 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", + }, + } + + 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", + }, + } + + 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")) + } - before2Minutes := AddMinute(now, -2) - diff2 := before2Minutes.Sub(now) - assert.Equal(float64(-2), diff2.Minutes()) +} + +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) { + t.Parallel() + assert := internal.NewAssert(t, "TestGetNowDate") expected := time.Now().Format("2006-01-02") assert.Equal(expected, GetNowDate()) } -func TestGetNotTime(t *testing.T) { - assert := internal.NewAssert(t, "TestGetNotTime") +func TestGetNowTime(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGetNowTime") expected := time.Now().Format("15:04:05") assert.Equal(expected, GetNowTime()) } func TestGetNowDateTime(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestGetNowDateTime") expected := time.Now().Format("2006-01-02 15:04:05") assert.Equal(expected, GetNowDateTime()) } +func TestGetTodayStartTime(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGetTodayStartTime") + expected := time.Now().Format("2006-01-02") + " 00:00:00" + assert.Equal(expected, GetTodayStartTime()) +} + +func TestGetTodayEndTime(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGetTodayEndTime") + expected := time.Now().Format("2006-01-02") + " 23:59:59" + assert.Equal(expected, GetTodayEndTime()) +} + func TestFormatTimeToStr(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestFormatTimeToStr") datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") cases := []string{ "yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd", "dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss", - "hh:mm:ss", "yyyy/mm"} + "hh:mm:ss", "yyyy/mm", + "yyyy-mm-dd hh", + } expected := []string{ "2021-01-02 16:04:08", "2021-01-02", "02-01-21 16:04:08", "2021/01/02 16:04:08", - "16:04:08", "2021/01"} + "16:04:08", "2021/01", + "2021-01-02 16", + } for i := 0; i < len(cases); i++ { actual := FormatTimeToStr(datetime, cases[i]) assert.Equal(expected[i], actual) - } + + ds := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss", "EST") + t.Log(ds) } func TestFormatStrToTime(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestFormatStrToTime") formats := []string{ @@ -97,22 +515,28 @@ func TestFormatStrToTime(t *testing.T) { "dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss", "yyyy/mm"} - datetimeStr := []string{ + expected := []string{ "2021-01-02 16:04:08", "2021-01-02", "02-01-21 16:04:08", "2021/01/02 16:04:08", "2021/01"} for i := 0; i < len(cases); i++ { - actual, err := FormatStrToTime(datetimeStr[i], cases[i]) + actual, err := FormatStrToTime(expected[i], cases[i]) if err != nil { t.Fatal(err) } - expected, _ := time.Parse(formats[i], datetimeStr[i]) + expected, _ := time.Parse(formats[i], expected[i]) assert.Equal(expected, actual) } + + estTime, err := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss", "EST") + t.Log(estTime) + assert.IsNil(err) } func TestBeginOfMinute(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeginOfMinute") expected := time.Date(2022, 2, 15, 15, 48, 0, 0, time.Local) @@ -123,6 +547,8 @@ func TestBeginOfMinute(t *testing.T) { } func TestEndOfMinute(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEndOfMinute") expected := time.Date(2022, 2, 15, 15, 48, 59, 999999999, time.Local) @@ -133,6 +559,8 @@ func TestEndOfMinute(t *testing.T) { } func TestBeginOfHour(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeginOfHour") expected := time.Date(2022, 2, 15, 15, 0, 0, 0, time.Local) @@ -143,6 +571,8 @@ func TestBeginOfHour(t *testing.T) { } func TestEndOfHour(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEndOfHour") expected := time.Date(2022, 2, 15, 15, 59, 59, 999999999, time.Local) @@ -153,6 +583,8 @@ func TestEndOfHour(t *testing.T) { } func TestBeginOfDay(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeginOfDay") expected := time.Date(2022, 2, 15, 0, 0, 0, 0, time.Local) @@ -163,6 +595,8 @@ func TestBeginOfDay(t *testing.T) { } func TestEndOfDay(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEndOfDay") expected := time.Date(2022, 2, 15, 23, 59, 59, 999999999, time.Local) @@ -173,26 +607,32 @@ func TestEndOfDay(t *testing.T) { } func TestBeginOfWeek(t *testing.T) { + t.Parallel() + 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) } func TestEndOfWeek(t *testing.T) { + t.Parallel() + 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) } func TestBeginOfMonth(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeginOfMonth") expected := time.Date(2022, 2, 1, 0, 0, 0, 0, time.Local) @@ -203,6 +643,8 @@ func TestBeginOfMonth(t *testing.T) { } func TestEndOfMonth(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEndOfMonth") expected := time.Date(2022, 2, 28, 23, 59, 59, 999999999, time.Local) @@ -213,6 +655,8 @@ func TestEndOfMonth(t *testing.T) { } func TestBeginOfYear(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeginOfYear") expected := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local) @@ -223,6 +667,8 @@ func TestBeginOfYear(t *testing.T) { } func TestEndOfYear(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEndOfYear") expected := time.Date(2022, 12, 31, 23, 59, 59, 999999999, time.Local) @@ -231,3 +677,269 @@ func TestEndOfYear(t *testing.T) { assert.Equal(expected, actual) } + +func TestIsLeapYear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEndOfYear") + + result1 := IsLeapYear(2000) + result2 := IsLeapYear(2001) + + assert.Equal(true, result1) + assert.Equal(false, result2) +} + +func TestDayOfYear(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDayOfYear") + date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local) + result1 := DayOfYear(date1) + assert.Equal(31, result1) + + date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local) + result2 := DayOfYear(date2) + assert.Equal(1, result2) + + date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local) + result3 := DayOfYear(date3) + assert.Equal(0, result3) +} + +func TestIsWeekend(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsWeekend") + + date := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local) + result := IsWeekend(date) + assert.Equal(true, result) + + date1 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local) + result1 := IsWeekend(date1) + assert.Equal(true, result1) + + date2 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local) + result2 := IsWeekend(date2) + assert.Equal(false, result2) +} + +func TestNowDateOrTime(t *testing.T) { + t.Parallel() + + formats := []string{ + "yyyy-mm-dd hh:mm:ss", + "yyyy-mm-dd", + "dd-mm-yy hh:mm:ss", + "yyyy/mm/dd hh:mm:ss", + "hh:mm:ss", + "yyyy/mm", + "yyyy-mm-dd hh", + } + + for i := 0; i < len(formats); i++ { + result := NowDateOrTime(formats[i], "UTC") + t.Log(result) + } +} + +func TestTimestamp(t *testing.T) { + t.Parallel() + + ts1 := Timestamp() + t.Log(ts1) + + ts2 := TimestampMilli() + t.Log(ts2) + + ts3 := TimestampMicro() + t.Log(ts3) + + ts4 := TimestampNano() + t.Log(ts4) +} + +func TestTrackFuncTime(t *testing.T) { + defer TrackFuncTime(time.Now())() + + var n int + for i := 0; i < 5000000; i++ { + n++ + } +} + +func TestDaysBetween(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDaysBetween") + + tests := []struct { + start time.Time + end time.Time + expected int + }{ + { + start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC), + expected: 9, + }, + { + start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + expected: -9, + }, + { + start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + expected: 0, + }, + { + start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC), + expected: 365, + }, + { + start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC), + expected: 30, + }, + } + + for _, tt := range tests { + result := DaysBetween(tt.start, tt.end) + assert.Equal(tt.expected, result) + } +} + +func TestGenerateDatetimesBetween(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGenerateDatetimesBetween") + + tests := []struct { + start time.Time + end time.Time + layout string + interval string + expected []string + }{ + { + start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC), + layout: "2006-01-02 15:04:05", + interval: "30m", + expected: []string{ + "2024-09-01 00:00:00", + "2024-09-01 00:30:00", + "2024-09-01 01:00:00", + "2024-09-01 01:30:00", + "2024-09-01 02:00:00", + }, + }, + { + start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + layout: "2006-01-02 15:04:05", + interval: "1h", + expected: []string{"2024-09-01 00:00:00"}, + }, + { + start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), + end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC), + layout: "2006-01-02 15:04:05", + interval: "2h", + expected: []string{ + "2024-09-01 00:00:00", + "2024-09-01 02:00:00", + }, + }, + } + + for _, tt := range tests { + result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval) + + assert.Equal(tt.expected, result) + assert.IsNil(err) + } + + t.Run("Invalid interval", func(t *testing.T) { + _, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid") + if err == nil { + t.Fatal("Expected error, got nil") + } + }) +} + +func TestMin(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMin") + + zeroTime := time.Time{} + now := time.Now() + oneMinuteAgo := now.Add(-time.Minute) + oneMinuteAfter := now.Add(time.Minute) + + assert.Equal(zeroTime, Min(zeroTime, now, oneMinuteAgo, oneMinuteAfter)) + + assert.Equal(zeroTime, Min(now, zeroTime)) + + assert.Equal(oneMinuteAgo, Min(oneMinuteAgo, now, oneMinuteAfter)) +} + +func TestMax(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMax") + + zeroTime := time.Time{} + now := time.Now() + oneMinuteAgo := now.Add(-time.Minute) + oneMinuteAfter := now.Add(time.Minute) + + assert.Equal(oneMinuteAfter, Max(zeroTime, now, oneMinuteAgo, oneMinuteAfter)) + + assert.Equal(now, Max(now, zeroTime)) + + assert.Equal(oneMinuteAfter, Max(oneMinuteAgo, now, oneMinuteAfter)) +} + +func TestMaxMin(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMinMax") + + zeroTime := time.Time{} + now := time.Now() + oneMinuteAgo := now.Add(-time.Minute) + oneMinuteAfter := now.Add(time.Minute) + + max, min := MaxMin(zeroTime, now, oneMinuteAgo, oneMinuteAfter) + assert.Equal(zeroTime, min) + assert.Equal(oneMinuteAfter, max) + + max, min = MaxMin(now, zeroTime) + assert.Equal(zeroTime, min) + assert.Equal(now, max) + + max, min = MaxMin(oneMinuteAgo, now, oneMinuteAfter) + 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) +} diff --git a/docs/.vitepress/common.ts b/docs/.vitepress/common.ts new file mode 100644 index 00000000..01c0cee7 --- /dev/null +++ b/docs/.vitepress/common.ts @@ -0,0 +1,90 @@ +import { defineConfig, HeadConfig } from 'vitepress' + +export const META_IMAGE = '/lancet_logo.png' +export const isProduction = process.env.NETLIFY && process.env.CONTEXT === 'production' + +if (process.env.NETLIFY) { + console.log('Netlify build', process.env.CONTEXT) +} + +const productionHead: HeadConfig[] = [ + [ + 'script', + { + src: 'https://unpkg.com/thesemetrics@latest', + async: '', + type: 'text/javascript', + }, + ], +] + +const rControl = /[\u0000-\u001f]/g +const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g +const rCombining = /[\u0300-\u036F]/g + +/** + * Default slugification function + */ +export const slugify = (str: string): string => + str + .normalize('NFKD') + // Remove accents + .replace(rCombining, '') + // Remove control characters + .replace(rControl, '') + // Replace special characters + .replace(rSpecial, '-') + // ensure it doesn't start with a number + .replace(/^(\d)/, '_$1') + +export const commonConfig = defineConfig({ + title: 'Lancet', + appearance: true, + ignoreDeadLinks: true, + + markdown: { + theme: { + dark: 'dracula-soft', + light: 'vitesse-light', + }, + + attrs: { + leftDelimiter: '%{', + rightDelimiter: '}%', + }, + + anchor: { + slugify, + }, + }, + + head: [ + // ['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }], + ['link', { rel: 'icon', type: 'image/png', href: '/lancet_logo_mini.png' }], + ['meta', { name: 'theme-color', content: '#5f67ee' }], + ['meta', { name: 'og:type', content: 'website' }], + ['meta', { name: 'og:locale', content: 'zh' }], + + ...(isProduction ? productionHead : []), + ], + + themeConfig: { + logo: { src: '/lancet_logo_mini.png', width: 24, height: 24 }, + outline: [2, 3], + + search: { + provider: 'local', + }, + socialLinks: [ + { + icon: 'github', + link: 'https://github.com/duke-git/lancet', + }, + ], + + footer: { + copyright: 'Copyright © 2023-present Duke Du', + message: '京ICP备2023022770号-1', + }, + }, +}) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 00000000..aad6c3d5 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitepress' +import { commonConfig } from './common' +import { zhConfig } from './zh' +import { enConfig } from './en' + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + ...commonConfig, + + locales: { + root: { label: '简体中文', lang: 'zh-CN', link: '/', ...zhConfig }, + en: { label: 'English', lang: 'en-US', link: '/en/', ...enConfig }, + }, +}) diff --git a/docs/.vitepress/en.ts b/docs/.vitepress/en.ts new file mode 100644 index 00000000..4e047c5c --- /dev/null +++ b/docs/.vitepress/en.ts @@ -0,0 +1,140 @@ +import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress' + +export const META_URL = 'https://www.golancet.cn/en/' +export const META_TITLE = 'Lancet' +export const META_DESCRIPTION = 'A powerful util function library of Go' + +export const enConfig: LocaleSpecificConfig = { + description: META_DESCRIPTION, + + head: [ + ['meta', { property: 'og:url', content: META_URL }], + ['meta', { property: 'og:description', content: META_DESCRIPTION }], + ], + + themeConfig: { + editLink: { + pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path', + text: 'Suggest changes to this page', + }, + nav: [ + { + text: 'Home', + link: '/en/', + activeMatch: '^/en/', + }, + { + text: 'Guide', + link: '/en/guide/introduction', + activeMatch: '^/en/guide/', + }, + { text: 'API', link: '/en/api/overview', activeMatch: '^/en/api/' }, + { + text: 'Links', + items: [ + { + text: 'Discussion', + link: 'https://github.com/duke-git/lancet/discussions', + }, + { + text: 'Changelog', + link: 'https://github.com/duke-git/lancet/releases', + }, + { + text: 'Contribution', + link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.md', + }, + ], + }, + ], + + sidebar: { + '/en/guide/': [ + { + text: 'Introduction', + collapsed: false, + items: [ + { + text: 'What is Lancet?', + link: '/en/guide/introduction', + }, + { + text: 'Getting started', + link: '/en/guide/getting_started', + }, + ], + }, + { + text: 'Contribute Code', + collapsed: false, + items: [ + { + text: 'Contribution guide', + link: '/en/guide/contribution_guide', + }, + { + text: 'Contributors', + link: '/en/guide/contributors', + }, + ], + }, + { + text: 'API Reference', + link: '/en/api/overview' + }, + ], + '/en/api/': [ + { + text: 'Overview', + items: [{ text: 'API overview', link: '/en/api/overview' }], + }, + { + text: 'Packages', + collapsed: false, + items: [ + { text: 'algorithm', link: '/en/api/packages/algorithm' }, + { text: 'compare', link: '/en/api/packages/compare' }, + { text: 'concurrency', link: '/en/api/packages/concurrency' }, + { text: 'condition', link: '/en/api/packages/condition' }, + { text: 'convertor', link: '/en/api/packages/convertor' }, + { text: 'cryptor', link: '/en/api/packages/cryptor' }, + { + text: 'datastructure', + collapsed: true, + items: [ + { text: 'list', link: '/en/api/packages/datastructure/list' }, + { text: 'safelist', link: '/en/api/packages/datastructure/copyonwritelist' }, + { text: 'link', link: '/en/api/packages/datastructure/link' }, + { text: 'stack', link: '/en/api/packages/datastructure/stack' }, + { text: 'queue', link: '/en/api/packages/datastructure/queue' }, + { text: 'heap', link: '/en/api/packages/datastructure/heap' }, + { text: 'tree', link: '/en/api/packages/datastructure/tree' }, + { text: 'set', link: '/en/api/packages/datastructure/set' }, + { text: 'hashmap', link: '/en/api/packages/datastructure/hashmap' }, + ], + }, + { text: 'datetime', link: '/en/api/packages/datetime' }, + { text: 'eventbus', link: '/en/api/packages/eventbus' }, + { text: 'fileutil', link: '/en/api/packages/fileutil' }, + { text: 'formatter', link: '/en/api/packages/formatter' }, + { text: 'function', link: '/en/api/packages/function' }, + { text: 'mathutil', link: '/en/api/packages/mathutil' }, + { text: 'maputil', link: '/en/api/packages/maputil' }, + { text: 'netutil', link: '/en/api/packages/netutil' }, + { text: 'pointer', link: '/en/api/packages/pointer' }, + { text: 'random', link: '/en/api/packages/random' }, + { text: 'retry', link: '/en/api/packages/retry' }, + { text: 'slice', link: '/en/api/packages/slice' }, + { 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: 'xerror', link: '/en/api/packages/xerror' }, + ], + }, + ], + }, + }, +} diff --git a/docs/.vitepress/zh.ts b/docs/.vitepress/zh.ts new file mode 100644 index 00000000..0ade5fcf --- /dev/null +++ b/docs/.vitepress/zh.ts @@ -0,0 +1,153 @@ +import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress' + +export const META_URL = 'https://www.golancet.cn' +export const META_TITLE = 'Lancet' +export const META_DESCRIPTION = '一个强大的Go语言工具函数库' + +export const zhConfig: LocaleSpecificConfig = { + description: META_DESCRIPTION, + + head: [ + ['meta', { property: 'og:url', content: META_URL }], + ['meta', { property: 'og:description', content: META_DESCRIPTION }], + ], + + themeConfig: { + editLink: { + pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path', + text: '对本页提出修改建议', + }, + outline: { + label: '本页内容', + }, + + docFooter: { + prev: '上一页', + next: '下一页', + }, + + nav: [ + { + text: '首页', + link: '/', + activeMatch: '^/', + }, + { + text: '指南', + link: '/guide/introduction', + activeMatch: '^/guide/', + }, + { text: 'API', link: '/api/overview', activeMatch: '^/api/' }, + { + text: '相关链接', + items: [ + { + text: '论坛', + link: 'https://github.com/duke-git/lancet/discussions', + }, + { + text: '更新日志', + link: 'https://github.com/duke-git/lancet/releases', + }, + { + text: '参与贡献', + link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.zh-CN.md', + }, + ], + }, + ], + + sidebar: { + '/guide/': [ + { + text: '介绍', + collapsed: false, + items: [ + { + text: 'Lancet是什么?', + link: '/guide/introduction', + }, + { + text: '开始', + link: '/guide/getting_started', + }, + ], + }, + { + text: '贡献代码', + collapsed: false, + items: [ + { + text: '贡献指南', + link: '/guide/contribution_guide', + }, + { + text: '贡献者', + link: '/guide/contributors', + }, + ], + }, + { + text: 'API手册', + link: '/api/overview' + }, + ], + + '/api/': [ + { + text: '概览', + items: [{ text: 'API概述', link: '/api/overview' }], + }, + { + text: 'API文档', + collapsed: false, + items: [ + { text: '算法', link: '/api/packages/algorithm' }, + { text: '比较器', link: '/api/packages/compare' }, + { text: '并发处理', link: '/api/packages/concurrency' }, + { text: '条件判断', link: '/api/packages/condition' }, + { text: '类型转换', link: '/api/packages/convertor' }, + { text: '加密&解密', link: '/api/packages/cryptor' }, + { + text: '数据结构', + collapsed: true, + items: [ + { text: '线性表', link: '/api/packages/datastructure/list' }, + { + text: '线性表(线程安全)', + link: '/api/packages/datastructure/copyonwritelist', + }, + { text: '链表', link: '/api/packages/datastructure/link' }, + { text: '栈', link: '/api/packages/datastructure/stack' }, + { text: '队列', link: '/api/packages/datastructure/queue' }, + { text: '堆', link: '/api/packages/datastructure/heap' }, + { text: '树', link: '/api/packages/datastructure/tree' }, + { text: '集合', link: '/api/packages/datastructure/set' }, + { text: 'HashMap', link: '/api/packages/datastructure/hashmap' }, + ], + }, + { text: '日期&时间', link: '/api/packages/datetime' }, + { text: '事件总线', link: '/api/packages/eventbus' }, + { text: '文件', link: '/api/packages/fileutil' }, + { text: '格式化工具', link: '/api/packages/formatter' }, + { text: '函数', link: '/api/packages/function' }, + { text: '数学工具', link: '/api/packages/mathutil' }, + { text: 'Map', link: '/api/packages/maputil' }, + { text: '网络', link: '/api/packages/netutil' }, + { text: '指针', link: '/api/packages/pointer' }, + { text: '随机数', link: '/api/packages/random' }, + { text: '重试', link: '/api/packages/retry' }, + { text: '切片', link: '/api/packages/slice' }, + { text: '流', link: '/api/packages/stream' }, + { text: '结构体', link: '/api/packages/struct' }, + { text: '字符串', link: '/api/packages/strutil' }, + { text: '系统', link: '/api/packages/system' }, + { text: '元组', link: '/api/packages/tuple' }, + { text: '验证器', link: '/api/packages/validator' }, + { text: '错误处理', link: '/api/packages/xerror' }, + ], + }, + ], + }, + }, +} diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 00000000..b004174d --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,69 @@ +--- +outline: deep +--- + +# API 概述 + +lancet(柳叶刀)是一个功能强大、全面、高效、可复用的 go 语言工具函数库。包含 25 个包,超过 600 个工具函数。功能涵盖字符串处理、切片处理、网络、并发、加解密、文件处理、时间/日期、流处理、迭代器等等。 + + + +
+

lancet功能模块

+
+
algorithm
+
compare
+
concurrency
+
condition
+
convertor
+
cryptor
+
datastructure
+
datetime
+
eventbus
+
fileutil
+
formatter
+
function
+
iterator
+
maputil
+
mathutil
+
netutil
+
pointer
+
random
+
retry
+
slice
+
stream
+
structs
+
strutil
+
system
+
tuple
+
validator
+
xerror
+
+
diff --git a/docs/api/packages/algorithm.md b/docs/api/packages/algorithm.md new file mode 100644 index 00000000..c48a7367 --- /dev/null +++ b/docs/api/packages/algorithm.md @@ -0,0 +1,636 @@ +# Algorithm + +algorithm 算法包实现一些基本算法,sort,search,lrucache。 + +
+ +## 源码 + +- [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/lrucache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go) + +
+ +## 用法 + +```go +import ( + "github.com/duke-git/lancet/v2/algorithm" +) +``` + +
+ +## 目录 + +- [BubbleSort](#BubbleSort) +- [InsertionSort](#InsertionSort) +- [SelectionSort](#SelectionSort) +- [ShellSort](#ShellSort) +- [QuickSort](#QuickSort) +- [HeapSort](#HeapSort) +- [MergeSort](#MergeSort) +- [CountSort](#CountSort) +- [BinarySearch](#BinarySearch) +- [BinaryIterativeSearch](#BinaryIterativeSearch) +- [LinearSearch](#LinearSearch) +- [LRUCache](#LRUCache) + +
+ +## 文档 + +### BubbleSort + +

冒泡排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func BubbleSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/GNdv7Jg2Taj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.BubbleSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### InsertionSort + +

插入排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func InsertionSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/G5LJiWgJJW6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type people struct { + Name string + Age int +} + +// PeopleAageComparator sort people slice by age field +type peopleAgeComparator struct{} + +// Compare implements github.com/duke-git/lancet/constraints/constraints.go/Comparator +func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int { + p1, _ := v1.(people) + p2, _ := v2.(people) + + //ascending order + if p1.Age < p2.Age { + return -1 + } else if p1.Age > p2.Age { + return 1 + } + + return 0 +} + +func main() { + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + + comparator := &peopleAgeComparator{} + + algorithm.InsertionSort(peoples, comparator) + + fmt.Println(peoples) + + // Output: + // [{d 8} {b 10} {c 17} {a 20} {e 28}] +} +``` + +### SelectionSort + +

选择排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func SelectionSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/oXovbkekayS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.SelectionSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### ShellSort + +

希尔排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func ShellSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/3ibkszpJEu3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.ShellSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### QuickSort + +

快速排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func QuickSort[T any](slice []T comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/7Y7c1Elk3ax) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.QuickSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### HeapSort + +

堆排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func HeapSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/u6Iwa1VZS_f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.HeapSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### MergeSort + +

归并排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func MergeSort[T any](slice []T, comparator constraints.Comparator) +``` + +示例:[运行](https://go.dev/play/p/ydinn9YzUJn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.MergeSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### CountSort + +

计数排序,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func CountSort[T any](slice []T, comparator constraints.Comparator) []T +``` + +示例:[运行](https://go.dev/play/p/tB-Umgm0DrP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + sortedNums := algorithm.CountSort(numbers, comparator) + + fmt.Println(sortedNums) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### BinarySearch + +

二分递归查找,返回元素索引,未找到元素返回-1,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int +``` + +示例: [运行](https://go.dev/play/p/t6MeGiUSN47) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := algorithm.BinarySearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := algorithm.BinarySearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} +``` + +### BinaryIterativeSearch + +

二分迭代查找,返回元素索引,未找到元素返回-1,参数comparator需要实现包constraints.Comparator。

+ +函数签名: + +```go +func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int +``` + +示例: [运行](https://go.dev/play/p/Anozfr8ZLH3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := algorithm.BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := algorithm.BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} +``` + +### LinearSearch + +

基于传入的相等函数线性查找元素,返回元素索引,未找到元素返回-1。

+ +函数签名: + +```go +func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int +``` + +示例: [运行](https://go.dev/play/p/IsS7rgn5s3x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +func main() { + numbers := []int{3, 4, 5, 3, 2, 1} + + equalFunc := func(a, b int) bool { + return a == b + } + + result1 := algorithm.LinearSearch(numbers, 3, equalFunc) + result2 := algorithm.LinearSearch(numbers, 6, equalFunc) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} +``` + +### LRUCache + +

lru算法实现缓存。

+ +函数签名: + +```go +func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] +func (l *LRUCache[K, V]) Get(key K) (V, bool) +func (l *LRUCache[K, V]) Put(key K, value V) +func (l *LRUCache[K, V]) Delete(key K) bool +func (l *LRUCache[K, V]) Len() int +``` + +示例:[运行](https://go.dev/play/p/-EZjgOURufP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +func main() { + cache := algorithm.NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result1, ok1 := cache.Get(1) + result2, ok2 := cache.Get(2) + result3, ok3 := cache.Get(3) + + fmt.Println(result1, ok1) + fmt.Println(result2, ok2) + fmt.Println(result3, ok3) + + fmt.Println(cache.Len()) + + ok := cache.Delete(2) + fmt.Println(ok) + + + // Output: + // 1 true + // 2 true + // 0 false + // 2 + // true +} +``` diff --git a/docs/api/packages/compare.md b/docs/api/packages/compare.md new file mode 100644 index 00000000..773506ff --- /dev/null +++ b/docs/api/packages/compare.md @@ -0,0 +1,375 @@ +# Compare + +compare包提供几个轻量级的类型比较函数。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/compare/compare.go](https://github.com/duke-git/lancet/blob/main/compare/compare.go) + +- [https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/condition" +) +``` + +
+ +## 目录 + +- [Equal](#Equal) +- [EqualValue](#EqualValue) +- [LessThan](#LessThan) +- [GreaterThan](#GreaterThan) +- [LessOrEqual](#LessOrEqual) +- [GreaterOrEqual](#GreaterOrEqual) +- [InDelta](#InDelta) + + +
+ +## 文档 + +### Equal + +

检查两个值是否相等(检查类型和值)

+ +函数签名: + +```go +func Equal(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/wmVxR-to4lz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.Equal(1, 1) + result2 := compare.Equal("1", "1") + result3 := compare.Equal([]int{1, 2, 3}, []int{1, 2, 3}) + result4 := compare.Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}) + + result5 := compare.Equal(1, "1") + result6 := compare.Equal(1, int64(1)) + result7 := compare.Equal([]int{1, 2}, []int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### EqualValue + +

检查两个值是否相等(只检查值)

+ +函数签名: + +```go +func EqualValue(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/fxnna_LLD9u) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.EqualValue(1, 1) + result2 := compare.EqualValue(int(1), int64(1)) + result3 := compare.EqualValue(1, "1") + result4 := compare.EqualValue(1, "2") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### LessThan + +

验证参数`left`的值是否小于参数`right`的值。

+ +函数签名: + +```go +func LessThan(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/cYh7FQQj0ne) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.LessThan(1, 2) + result2 := compare.LessThan(1.1, 2.2) + result3 := compare.LessThan("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.LessThan(time1, time2) + + result5 := compare.LessThan(2, 1) + result6 := compare.LessThan(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} +``` + +### GreaterThan + +

验证参数`left`的值是否大于参数`right`的值。

+ +函数签名: + +```go +func GreaterThan(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/9-NYDFZmIMp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.GreaterThan(2, 1) + result2 := compare.GreaterThan(2.2, 1.1) + result3 := compare.GreaterThan("b", "a") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.GreaterThan(time2, time1) + + result5 := compare.GreaterThan(1, 2) + result6 := compare.GreaterThan(int64(2), 1) + result7 := compare.GreaterThan("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### LessOrEqual + +

验证参数`left`的值是否小于或等于参数`right`的值。

+ +函数签名: + +```go +func LessOrEqual(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/e4T_scwoQzp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.LessOrEqual(1, 1) + result2 := compare.LessOrEqual(1.1, 2.2) + result3 := compare.LessOrEqual("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.LessOrEqual(time1, time2) + + result5 := compare.LessOrEqual(2, 1) + result6 := compare.LessOrEqual(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} +``` + +### GreaterOrEqual + +

验证参数`left`的值是否大于或参数`right`的值。

+ +函数签名: + +```go +func GreaterOrEqual(left, right any) bool +``` + +示例: [运行](https://go.dev/play/p/vx8mP0U8DFk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.GreaterOrEqual(1, 1) + result2 := compare.GreaterOrEqual(2.2, 1.1) + result3 := compare.GreaterOrEqual("b", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.GreaterOrEqual(time2, time1) + + result5 := compare.GreaterOrEqual(1, 2) + result6 := compare.GreaterOrEqual(int64(2), 1) + result7 := compare.GreaterOrEqual("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### InDelta + +

检查增量内两个值是否相等。

+ +函数签名: + +```go +func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool +``` + +示例: [运行](https://go.dev/play/p/TuDdcNtMkjo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := InDelta(1, 1, 0) + result2 := InDelta(1, 2, 0) + + result3 := InDelta(2.0/3.0, 0.66667, 0.001) + result4 := InDelta(2.0/3.0, 0.0, 0.001) + + result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0) + result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // false + // true + // false + // false + // true +} +``` diff --git a/docs/api/packages/concurrency.md b/docs/api/packages/concurrency.md new file mode 100644 index 00000000..a6946426 --- /dev/null +++ b/docs/api/packages/concurrency.md @@ -0,0 +1,852 @@ +# Concurrency + +并发包包含一些支持并发编程的功能。例如:goroutine, channel 等。 + +
+ +## 源码: + +- [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) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/concurrency" +) +``` + +
+ +## 目录 + +### Channel + +- [NewChannel](#NewChannel) +- [Bridge](#Bridge) +- [FanIn](#FanIn) +- [Generate](#Generate) +- [Or](#Or) +- [OrDone](#OrDone) +- [Repeat](#Repeat) +- [RepeatFn](#RepeatFn) +- [Take](#Take) +- [Tee](#Tee) + +### KeyedLocker + +- [NewKeyedLocker](#NewKeyedLocker) +- [KeyedLocker_Do](#Do) +- [NewRWKeyedLocker](#NewRWKeyedLocker) +- [RLock](#RLock) +- [Lock](#Lock) +- [NewTryKeyedLocker](#NewTryKeyedLocker) +- [TryLock](#TryLock) +- [Unlock](#Unlock) + +
+ +## 文档 + +### Channel + +### NewChannel + +

返回一个Channel指针实例

+ +函数签名: + +```go +type Channel[T any] struct +func NewChannel[T any]() *Channel[T] +``` + +示例:[运行](https://go.dev/play/p/7aB4KyMMp9A) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + c := concurrency.NewChannel[int]() +} +``` + +### Bridge + +

将多个channel链接到一个channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/qmWSy1NVF-Y) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + genVals := func() <-chan <-chan int { + out := make(chan (<-chan int)) + go func() { + defer close(out) + for i := 1; i <= 5; i++ { + stream := make(chan int, 1) + stream <- i + close(stream) + out <- stream + } + }() + return out + } + + for v := range c.Bridge(ctx, genVals()) { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 + // 4 + // 5 +} +``` + +### FanIn + +

将多个channel合并为一个channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/2VYFMexEvTm) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + channels := make([]<-chan int, 2) + + for i := 0; i < 2; i++ { + channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2) + } + + chs := c.FanIn(ctx, channels...) + + for v := range chs { + fmt.Println(v) //1 1 0 0 or 0 0 1 1 + } +} +``` + +### Generate + +

根据传入的值,生成channel.

+ +函数签名: + +```go +func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/7aB4KyMMp9A) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Generate(ctx, 1, 2, 3) + + fmt.Println(<-intStream) + fmt.Println(<-intStream) + fmt.Println(<-intStream) + + // Output: + // 1 + // 2 + // 3 +} +``` + +### Repeat + +

返回一个channel,将参数`values`重复放入channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/k5N_ALVmYjE) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4) + + for v := range intStream { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 1 + // 2 +} +``` + +### RepeatFn + +

返回一个channel,重复执行函数fn,并将结果放入返回的channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/4J1zAWttP85) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + fn := func() string { + return "hello" + } + + c := concurrency.NewChannel[string]() + intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3) + + for v := range intStream { + fmt.Println(v) + } + // Output: + // hello + // hello + // hello +} +``` + +### Or + +

将一个或多个channel读取到一个channel中,当任何读取channel关闭时将结束读取。

+ +函数签名: + +```go +func (c *Channel[T]) Or(channels ...<-chan T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/Wqz9rwioPww) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + sig := func(after time.Duration) <-chan any { + c := make(chan any) + go func() { + defer close(c) + time.Sleep(after) + }() + return c + } + + start := time.Now() + + c := concurrency.NewChannel[any]() + <-c.Or( + sig(1*time.Second), + sig(2*time.Second), + sig(3*time.Second), + ) + + fmt.Println("done after %v", time.Since(start)) //1.003s +} +``` + +### OrDone + +

将一个channel读入另一个channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/lm_GoS6aDjo) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) + + for v := range c.OrDone(ctx, intStream) { + fmt.Println(v) + } + + // Output: + // 1 + // 1 + // 1 +} +``` + +### Take + +

返回一个channel,其值从另一个channel获取,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T +``` + +示例:[运行](https://go.dev/play/p/9Utt-1pDr2J) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + numbers := make(chan int, 5) + numbers <- 1 + numbers <- 2 + numbers <- 3 + numbers <- 4 + numbers <- 5 + defer close(numbers) + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, numbers, 3) + + for v := range intStream { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 +} +``` + +### Tee + +

将一个channel分成两个channel,直到取消上下文。

+ +函数签名: + +```go +func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) +``` + +示例:[运行](https://go.dev/play/p/3TQPKnCirrP) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 2) + + ch1, ch2 := c.Tee(ctx, intStream) + + for v := range ch1 { + fmt.Println(v) + fmt.Println(<-ch2) + } + // Output: + // 1 + // 1 + // 1 + // 1 +} +``` + +### KeyedLocker + +### NewKeyedLocker + +

NewKeyedLocker创建一个新的KeyedLocker,并为锁的过期设置指定的 TTL。KeyedLocker 是一个简单的键值锁实现,允许非阻塞的锁获取。

+ +函数签名: + +```go +func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] +``` + +示例:[运行](https://go.dev/play/p/GzeyC33T5rw) + +```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. +} +``` + +### Do + +

为指定的键获取锁并执行提供的函数。

+ +函数签名: + +```go +func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error +``` + +示例:[运行](https://go.dev/play/p/GzeyC33T5rw) + +```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. +} +``` + +### NewRWKeyedLocker + +

NewRWKeyedLocker创建一个新的RWKeyedLocker,并为锁的过期设置指定的 TTL。RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。

+ +函数签名: + +```go +func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] +``` + +示例:[运行](https://go.dev/play/p/CkaJWWwZm9) + +```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! +} +``` + +### RLock + +

RLock为指定的键获取读锁并执行提供的函数。

+ +函数签名: + +```go +func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error +``` + +示例:[运行](https://go.dev/play/p/ZrCr8sMo77T) + +```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! +} +``` + +### Lock + +

Lock为指定的键获取锁并执行提供的函数。

+ +函数签名: + +```go +func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error +``` + +示例:[运行](https://go.dev/play/p/WgAcXbOPKGk) + +```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! +} +``` + +### NewTryKeyedLocker + +

创建一个TryKeyedLocker实例,TryKeyedLocker是KeyedLocker的非阻塞版本。

+ +函数签名: + +```go +func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] +``` + +示例:[运行](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` + +### TryLock + +

TryLock尝试获取指定键的锁。如果锁成功获取,则返回true,否则返回false。

+ +函数签名: + +```go +func (l *TryKeyedLocker[K]) TryLock(key K) bool +``` + +示例:[运行](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` + +### Unlock + +

释放指定键的锁。

+ +函数签名: + +```go +func (l *TryKeyedLocker[K]) Unlock(key K) +``` + +示例:[运行](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` diff --git a/docs/api/packages/condition.md b/docs/api/packages/condition.md new file mode 100644 index 00000000..9dfe5b6e --- /dev/null +++ b/docs/api/packages/condition.md @@ -0,0 +1,335 @@ +# Condition +condition包含一些用于条件判断的函数。这个包的实现参考了carlmjohnson的truthy包的实现,更多有用的信息可以在[truthy](https://github.com/carlmjohnson/truthy)中找到,感谢carlmjohnson。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/condition/condition.go](https://github.com/duke-git/lancet/blob/main/condition/condition.go) + +
+ +## 用法: +```go +import ( + "github.com/duke-git/lancet/v2/condition" +) +``` + +
+ +## 目录 + +- [Bool](#Bool) +- [And](#And) +- [Or](#Or) +- [Xor](#Generate) +- [Nor](#Nor) +- [Xnor](#Xnor) +- [Nand](#Nand) +- [Ternary](#Ternary) +- [TernaryOperatordeprecated](#TernaryOperator) + +
+ +## 文档 + +### Bool +

返回传入参数的bool值.
+如果出入类型参数含有Bool方法, 会调用该方法并返回
+如果传入类型参数有IsZero方法, 返回IsZero方法返回值的取反
+slices和map的length大于0时,返回true,否则返回false
+其他类型会判断是否是零值

+ +函数签名: + +```go +func Bool[T any](value T) bool +``` +示例:[运行](https://go.dev/play/p/ETzeDJRSvhm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + // bool + result1 := condition.Bool(false) + result2 := condition.Bool(true) + fmt.Println(result1) // false + fmt.Println(result2) // true + + // integer + result3 := condition.Bool(0) + result4 := condition.Bool(1) + fmt.Println(result3) // false + fmt.Println(result4) // true + + // string + result5 := condition.Bool("") + result6 := condition.Bool(" ") + fmt.Println(result5) // false + fmt.Println(result6) // true + + // slice + nums := []int{} + result7 := condition.Bool(nums) + + nums = append(nums, 1, 2) + result8 := condition.Bool(nums) + fmt.Println(result7) // false + fmt.Println(result8) // true + + // struct + result9 = condition.Bool(struct{}{}) + fmt.Println(result8) // false + + + // Output: + // false + // true + // false + // true + // false + // true + // false + // true + // false +} +``` + +### And +

逻辑且操作,当切仅当a和b都为true时返回true

+ +函数签名: + +```go +func And[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/W1SSUmt6pvr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.And(0, 1)) // false + fmt.Println(condition.And(0, "")) // false + fmt.Println(condition.And(0, "0")) // false + fmt.Println(condition.And(1, "0")) // true +} +``` + +### Or +

逻辑或操作,当切仅当a和b都为false时返回false

+ +函数签名: + +```go +func Or[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/UlQTxHaeEkq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Or(0, "")) // false + fmt.Println(condition.Or(0, 1)) // true + fmt.Println(condition.Or(0, "0")) // true + fmt.Println(condition.Or(1, "0")) // true +} +``` + +### Xor +

逻辑异或操作,a和b相同返回false,a和b不相同返回true

+ +函数签名: + +```go +func Xor[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/gObZrW7ZbG8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Xor(0, 0)) // false + fmt.Println(condition.Xor(0, 1)) // true + fmt.Println(condition.Xor(1, 0)) // true + fmt.Println(condition.Xor(1, 1)) // false +} +``` + +### Nor +

异或的取反操作

+ +函数签名: + +```go +func Nor[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/g2j08F_zZky) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Nor(0, 0)) // true + fmt.Println(condition.Nor(0, 1)) // false + fmt.Println(condition.Nor(1, 0)) // false + fmt.Println(condition.Nor(1, 1)) // false +} +``` + +### Xnor +

如果a和b都是真的或a和b均是假的,则返回true。

+ +函数签名: + +```go +func Xnor[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/OuDB9g51643) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Xnor(0, 0)) // true + fmt.Println(condition.Xnor(0, 1)) // false + fmt.Println(condition.Xnor(1, 0)) // false + fmt.Println(condition.Xnor(1, 1)) // true +} +``` + +### Nand +

如果a和b都为真,返回false,否则返回true

+ +函数签名: + +```go +func Nand[T, U any](a T, b U) bool +``` +示例:[运行](https://go.dev/play/p/vSRMLxLIbq8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Nand(0, 0)) // true + fmt.Println(condition.Nand(0, 1)) // true + fmt.Println(condition.Nand(1, 0)) // true + fmt.Println(condition.Nand(1, 1)) // false +} +``` + +### Ternary +

三元运算符。

+ +函数签名: + +```go +func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U +``` +示例:[运行](https://go.dev/play/p/ElllPZY0guT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + conditionTrue := 2 > 1 + result1 := condition.Ternary(conditionTrue, 0, 1) + + conditionFalse := 2 > 3 + result2 := condition.Ternary(conditionFalse, 0, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} +``` + +### TernaryOperator +

三元运算符

+ +> ⚠️ 本函数已弃用,使用`Ternary`代替。 + +函数签名: + +```go +func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U +``` +示例:[运行](https://go.dev/play/p/ElllPZY0guT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + conditionTrue := 2 > 1 + result1 := condition.TernaryOperator(conditionTrue, 0, 1) + + conditionFalse := 2 > 3 + result2 := condition.TernaryOperator(conditionFalse, 0, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} +``` + + + + + + diff --git a/docs/api/packages/convertor.md b/docs/api/packages/convertor.md new file mode 100644 index 00000000..c3896941 --- /dev/null +++ b/docs/api/packages/convertor.md @@ -0,0 +1,1182 @@ +# Convertor + +convertor 转换器包支持一些常见的数据类型转换 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/convertor" +) +``` + +
+ +## 目录 + +- [ColorHexToRGB](#ColorHexToRGB) +- [ColorRGBToHex](#ColorRGBToHex) +- [ToBool](#ToBool) +- [ToBytes](#ToBytes) +- [ToChar](#ToChar) +- [ToChannel](#ToChannel) +- [ToFloat](#ToFloat) +- [ToInt](#ToInt) +- [ToJson](#ToJson) +- [ToMap](#ToMap) +- [ToPointer](#ToPointer) +- [ToString](#ToString) +- [StructToMap](#StructToMap) +- [MapToSlice](#MapToSlice) +- [EncodeByte](#EncodeByte) +- [DecodeByte](#DecodeByte) +- [DeepClone](#DeepClone) +- [CopyProperties](#CopyProperties) +- [ToInterface](#ToInterface) +- [Utf8ToGbk](#Utf8ToGbk) +- [GbkToUtf8](#GbkToUtf8) +- [ToStdBase64](#ToStdBase64) +- [ToUrlBase64](#ToUrlBase64) +- [ToRawStdBase64](#ToRawStdBase64) +- [ToRawUrlBase64](#ToRawUrlBase64) +- [ToBigInt](#ToBigInt) + +
+ +## 文档 + +### ColorHexToRGB + +

颜色值十六进制转rgb。

+ +函数签名: + +```go +func ColorHexToRGB(colorHex string) (red, green, blue int) +``` + +示例:[运行](https://go.dev/play/p/o7_ft-JCJBV) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + colorHex := "#003366" + r, g, b := convertor.ColorHexToRGB(colorHex) + + fmt.Println(r, g, b) + + // Output: + // 0 51 102 +} +``` + +### ColorRGBToHex + +

颜色值rgb转十六进制。

+ +函数签名: + +```go +func ColorRGBToHex(red, green, blue int) string +``` + +示例:[运行](https://go.dev/play/p/nzKS2Ro87J1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + r := 0 + g := 51 + b := 102 + colorHex := ColorRGBToHex(r, g, b) + + fmt.Println(colorHex) + + // Output: + // #003366 +} +``` + +### ToBool + +

字符串转布尔类型,使用strconv.ParseBool。

+ +函数签名: + +```go +func ToBool(s string) (bool, error) +``` + +示例:[运行](https://go.dev/play/p/ARht2WnGdIN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"} + + for i := 0; i < len(cases); i++ { + result, _ := convertor.ToBool(cases[i]) + fmt.Println(result) + } + + // Output: + // true + // true + // true + // false + // false + // false + // false + // false + // false +} +``` + +### ToBytes + +

Interface转字节切片。

+ +函数签名: + +```go +func ToBytes(data any) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/fAMXYFDvOvr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + bytesData, err := convertor.ToBytes("abc") + if err != nil { + fmt.Println(err) + } + + fmt.Println(bytesData) + + // Output: + // [97 98 99] +} +``` + +### ToChar + +

字符串转字符切片。

+ +函数签名: + +```go +func ToChar(s string) []string +``` + +示例:[运行](https://go.dev/play/p/JJ1SvbFkVdM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1 := convertor.ToChar("") + result2 := convertor.ToChar("abc") + result3 := convertor.ToChar("1 2#3") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [] + // [a b c] + // [1 2 # 3] +} +``` + +### ToChannel + +

将切片转为只读channel。

+ +函数签名: + +```go +func ToChannel[T any](array []T) <-chan T +``` + +示例:[运行](https://go.dev/play/p/hOx_oYZbAnL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + ch := convertor.ToChannel([]int{1, 2, 3}) + result1 := <-ch + result2 := <-ch + result3 := <-ch + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 3 +} +``` + +### ToFloat + +

将interface转成float64类型,如果参数无法转换,会返回0和error。

+ +函数签名: + +```go +func ToFloat(value any) (float64, error) +``` + +示例:[运行](https://go.dev/play/p/4YTmPCibqHJ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1, _ := convertor.ToFloat("") + result2, err := convertor.ToFloat("abc") + result3, _ := convertor.ToFloat("-1") + result4, _ := convertor.ToFloat("-.11") + result5, _ := convertor.ToFloat("1.23e3") + result6, _ := convertor.ToFloat(true) + + fmt.Println(result1) + fmt.Println(result2, err) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // 0 + // 0 strconv.ParseFloat: parsing "": invalid syntax + // -1 + // -0.11 + // 1230 + // 0 +} +``` + +### ToInt + +

将interface转成int64类型,如果参数无法转换,会返回0和error。

+ +函数签名: + +```go +func ToInt(value any) (int64, error) +``` + +示例:[运行](https://go.dev/play/p/9_h9vIt-QZ_b) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1, _ := convertor.ToInt("123") + result2, _ := convertor.ToInt("-123") + result3, _ := convertor.ToInt(float64(12.3)) + result4, err := convertor.ToInt("abc") + result5, _ := convertor.ToInt(true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4, err) + fmt.Println(result5) + + // Output: + // 123 + // -123 + // 12 + // 0 strconv.ParseInt: parsing "": invalid syntax + // 0 +} +``` + +### ToJson + +

将interface转成json字符串,如果参数无法转换,会返回""和error。

+ +函数签名: + +```go +func ToJson(value any) (string, error) +``` + +示例:[运行](https://go.dev/play/p/2rLIkMmXWvR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result, err := convertor.ToJson(aMap) + + if err != nil { + fmt.Printf("%v", err) + } + + fmt.Println(result) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### ToMap + +

将切片转为map。

+ +函数签名: + +```go +func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V +``` + +示例:[运行](https://go.dev/play/p/tVFy7E-t24l) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Message struct { + name string + code int + } + messages := []Message{ + {name: "Hello", code: 100}, + {name: "Hi", code: 101}, + } + + result := convertor.ToMap(messages, func(msg Message) (int, string) { + return msg.code, msg.name + }) + + fmt.Println(result) + + // Output: + // map[100:Hello 101:Hi] +} +``` + +### ToPointer + +

返回传入值的指针。

+ +函数签名: + +```go +func ToPointer[T any](value T) *T +``` + +示例:[运行](https://go.dev/play/p/ASf_etHNlw1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result := convertor.ToPointer(123) + fmt.Println(*result) + + // Output: + // 123 +} +``` + +### ToString + +

将值转换为字符串,对于数字、字符串、[]byte,将转换为字符串。 对于其他类型(切片、映射、数组、结构体)将调用 json.Marshal

+ +函数签名: + +```go +func ToString(value any) string +``` + +示例:[运行](https://go.dev/play/p/nF1zOOslpQq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1 := convertor.ToString("") + result2 := convertor.ToString(nil) + result3 := convertor.ToString(0) + result4 := convertor.ToString(1.23) + result5 := convertor.ToString(true) + result6 := convertor.ToString(false) + result7 := convertor.ToString([]int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // + // + // 0 + // 1.23 + // true + // false + // [1,2,3] +} +``` + +### StructToMap + +

将struct转成map,只会转换struct中可导出的字段。struct中导出字段需要设置json tag标记。

+ +函数签名: + +```go +func StructToMap(value any) (map[string]any, error) +``` + +示例:[运行](https://go.dev/play/p/KYGYJqNUBOI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type People struct { + Name string `json:"name"` + age int + } + p := People{ + "test", + 100, + } + pm, _ := convertor.StructToMap(p) + + fmt.Println(pm) + + // Output: + // map[name:test] +} +``` + +### MapToSlice + +

map中key和value执行函数iteratee后,转为切片。

+ +函数签名: + +```go +func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T +``` + +示例:[运行](https://go.dev/play/p/dmX4Ix5V6Wl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result := MapToSlice(aMap, func(key string, value int) string { + return key + ":" + strconv.Itoa(value) + }) + + fmt.Println(result) //[]string{"a:1", "b:2", "c:3"} +} +``` + +### EncodeByte + +

将data编码成字节切片。

+ +函数签名: + +```go +func EncodeByte(data any) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/DVmM1G5JfuP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + byteData, _ := convertor.EncodeByte("abc") + fmt.Println(byteData) + + // Output: + // [6 12 0 3 97 98 99] +} +``` + +### DecodeByte + +

解码字节切片到目标对象,目标对象需要传入一个指针实例。

+ +函数签名: + +```go +func DecodeByte(data []byte, target any) error +``` + +示例:[运行](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + var result string + byteData := []byte{6, 12, 0, 3, 97, 98, 99} + + err := convertor.DecodeByte(byteData, &result) + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // abc +} +``` + +### DeepClone + +

创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。

+ +函数签名: + +```go +func DeepClone[T any](src T) T +``` + +示例:[运行](https://go.dev/play/p/j4DP5dquxnk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Struct struct { + Str string + Int int + Float float64 + Bool bool + Nil interface{} + unexported string + } + + cases := []interface{}{ + true, + 1, + 0.1, + map[string]int{ + "a": 1, + "b": 2, + }, + &Struct{ + Str: "test", + Int: 1, + Float: 0.1, + Bool: true, + Nil: nil, + // unexported: "can't be cloned", + }, + } + + for _, item := range cases { + cloned := convertor.DeepClone(item) + + isPointerEqual := &cloned == &item + fmt.Println(cloned, isPointerEqual) + } + + // Output: + // true false + // 1 false + // 0.1 false + // map[a:1 b:2] false + // &{test 1 0.1 true } false +} +``` + +### CopyProperties + +

拷贝不同结构体之间的同名字段。使用json.Marshal序列化,需要设置dst和src struct字段的json tag。

+ +函数签名: + +```go +func CopyProperties[T, U any](dst T, src U) (err error) +``` + +示例:[运行](https://go.dev/play/p/oZujoB5Sgg5) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + err := convertor.CopyProperties(&indicatorVO, indicator) + + if err != nil { + return + } + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) + + // Output: + // 001 + // 127.0.0.1 + // 3 +} +``` + +### ToInterface + +

将反射值转换成对应的interface类型。

+ +函数签名: + +```go +func ToInterface(v reflect.Value) (value interface{}, ok bool) +``` + +示例:[运行](https://go.dev/play/p/syqw0-WG7Xd) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + val := reflect.ValueOf("abc") + iVal, ok := convertor.ToInterface(val) + + fmt.Printf("%T\n", iVal) + fmt.Printf("%v\n", iVal) + fmt.Println(ok) + + // Output: + // string + // abc + // true +} +``` + +### Utf8ToGbk + +

utf8编码转GBK编码。

+ +函数签名: + +```go +func Utf8ToGbk(bs []byte) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/9FlIaFLArIL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + utf8Data := []byte("hello") + gbkData, _ := convertor.Utf8ToGbk(utf8Data) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(validator.IsGBK(gbkData)) + + // Output: + // true + // true +} +``` + +### GbkToUtf8 + +

GBK编码转utf8编码。

+ +函数签名: + +```go +func GbkToUtf8(bs []byte) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/OphmHCN_9u8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + gbkData, _ := convertor.Utf8ToGbk([]byte("hello")) + utf8Data, _ := convertor.GbkToUtf8(gbkData) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(string(utf8Data)) + + // Output: + // true + // hello +} +``` + +### ToStdBase64 + +

将值转换为StdBase64编码的字符串。error类型的数据也会把error的原因进行编码,复杂的结构会转为JSON格式的字符串

+ +函数签名: + +```go +func ToStdBase64(value any) string +``` + +示例:[运行](https://go.dev/play/p/_fLJqJD3NMo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + afterEncode := convertor.ToStdBase64(nil) + fmt.Println(afterEncode) + + afterEncode = convertor.ToStdBase64("") + fmt.Println(afterEncode) + + stringVal := "hello" + afterEncode = convertor.ToStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // + // + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +``` + +### ToUrlBase64 + +

值转换为 ToUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串

+ +函数签名: + +```go +func ToUrlBase64(value any) string +``` + +示例:[运行](https://go.dev/play/p/C_d0GlvEeUR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + afterEncode := convertor.ToUrlBase64(nil) + fmt.Println(afterEncode) + + + stringVal := "hello" + afterEncode = convertor.ToUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +``` + +### ToRawStdBase64 + +

值转换为 ToRawStdBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串

+ +函数签名: + +```go +func ToRawStdBase64(value any) string +``` + +示例:[运行](https://go.dev/play/p/wSAr3sfkDcv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + + stringVal := "hello" + afterEncode = convertor.ToRawStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToRawStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToRawStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToRawStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode := convertor.ToRawStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToRawStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToRawStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} +``` + +### ToRawUrlBase64 + +

值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串

+ +函数签名:[运行](https://go.dev/play/p/HwdDPFcza1O) + +```go +func ToRawUrlBase64(value any) string +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + + stringVal := "hello" + afterEncode := convertor.ToRawUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToRawUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToRawUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToRawUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToRawUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToRawUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToRawUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} +``` + +### ToBigInt + +

将整数值转换为bigInt。

+ +函数签名:[运行](https://go.dev/play/p/X3itkCxwB_x) + +```go +func ToBigInt[T any](v T) (*big.Int, error) +``` + +示例: + +```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 +} +``` \ No newline at end of file diff --git a/docs/api/packages/cryptor.md b/docs/api/packages/cryptor.md new file mode 100644 index 00000000..2264c2ca --- /dev/null +++ b/docs/api/packages/cryptor.md @@ -0,0 +1,1831 @@ +# Cryptor + +cryptor 包包含数据加密和解密功能。支持 base64, md5, hmac, hash, aes, des, rsa。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/cryptor/basic.go](https://github.com/duke-git/lancet/blob/main/cryptor/basic.go) +- [https://github.com/duke-git/lancet/blob/main/cryptor/crypto.go](https://github.com/duke-git/lancet/blob/main/cryptor/crypto.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/cryptor" +) +``` + +
+ +## 目录 + +- [AesEcbEncrypt](#AesEcbEncrypt) +- [AesEcbDecrypt](#AesEcbDecrypt) +- [AesCbcEncrypt](#AesCbcEncrypt) +- [AesCbcDecrypt](#AesCbcDecrypt) +- [AesCtrCryptdeprecated](#AesCtrCrypt) +- [AesCtrEncrypt](#AesCtrEncrypt) +- [AesCtrDecrypt](#AesCtrDecrypt) +- [AesCfbEncrypt](#AesCfbEncrypt) +- [AesCfbDecrypt](#AesCfbDecrypt) +- [AesOfbEncrypt](#AesOfbEncrypt) +- [AesOfbDecrypt](#AesOfbDecrypt) +- [AesGcmEncrypt](#AesGcmEncrypt) +- [AesGcmDecrypt](#AesGcmDecrypt) +- [Base64StdEncode](#Base64StdEncode) +- [Base64StdDecode](#Base64StdDecode) +- [DesEcbEncrypt](#DesEcbEncrypt) +- [DesEcbDecrypt](#DesEcbDecrypt) +- [DesCbcEncrypt](#DesCbcEncrypt) +- [DesCbcDecrypt](#DesCbcDecrypt) +- [DesCtrCryptdeprecated](#DesCtrCrypt) +- [DesCfbEncrypt](#DesCfbEncrypt) +- [DesCfbDecrypt](#DesCfbDecrypt) +- [DesOfbEncrypt](#DesOfbEncrypt) +- [DesOfbDecrypt](#DesOfbDecrypt) +- [HmacMd5](#HmacMd5) +- [HmacMd5WithBase64](#HmacMd5WithBase64) +- [HmacSha1](#HmacSha1) +- [HmacSha1WithBase64](#HmacSha1WithBase64) +- [HmacSha256](#HmacSha256) +- [HmacSha256WithBase64](#HmacSha256WithBase64) +- [HmacSha512](#HmacSha512) +- [HmacSha512WithBase64](#HmacSha512WithBase64) +- [Md5String](#Md5String) +- [Md5StringWithBase64](#Md5StringWithBase64) +- [Md5Byte](#Md5Byte) +- [Md5ByteWithBase64](#Md5ByteWithBase64) +- [Md5File](#Md5File) +- [Sha1](#Sha1) +- [Sha1WithBase64](#Sha1WithBase64) +- [Sha256](#Sha256) +- [Sha256WithBase64](#Sha256WithBase64) +- [Sha512](#Sha512) +- [Sha512WithBase64](#Sha512WithBase64) +- [GenerateRsaKey](#GenerateRsaKey) +- [RsaEncrypt](#RsaEncrypt) +- [RsaDecrypt](#RsaDecrypt) +- [GenerateRsaKeyPair](#GenerateRsaKeyPair) +- [RsaEncryptOAEP](#RsaEncryptOAEP) +- [RsaDecryptOAEP](#RsaDecryptOAEP) +- [RsaSign](#RsaSign) +- [RsaVerifySign](#RsaVerifySign) + +
+ +## 文档 + +### AesEcbEncrypt + +

使用AES ECB算法模式加密数据. 参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesEcbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesEcbDecrypt + +

使用AES ECB算法模式解密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesEcbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCbcEncrypt + +

使用AES CBC算法模式加密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCbcEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/IOq_g8_lKZD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCbcDecrypt + +

使用AES CBC算法模式解密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCbcDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/IOq_g8_lKZD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCtrCrypt + +

使用AES CTR算法模式加密/解密数据,参数`key`的长度是16, 24 or 32。

+ +> ⚠️ 本函数已弃用,使用`AesCtrEncrypt`和`AesCtrDecrypt`代替。 + +函数签名: + +```go +func AesCtrCrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/SpaZO0-5Nsp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCtrCrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCtrEncrypt + +

使用AES CTR算法模式加密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCtrEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/x6pjPAvThRz) + +```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 +} +``` + +### AesCtrDecrypt + +

使用AES CTR算法模式解密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCtrDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/x6pjPAvThRz) + +```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 +} +``` + +### AesCfbEncrypt + +

使用AES CFB算法模式加密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCfbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/tfkF10B13kH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCfbDecrypt + +

使用AES CFB算法模式解密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesCfbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/tfkF10B13kH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesOfbEncrypt + +

使用AES OFB算法模式加密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesOfbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/VtHxtkUj-3F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesOfbDecrypt + +

使用AES OFB算法模式解密数据,参数`key`的长度是16, 24 or 32。

+ +函数签名: + +```go +func AesOfbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/VtHxtkUj-3F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesGcmEncrypt + +

使用AES GCM算法模式加密数据。

+ +函数签名: + +```go +func AesGcmEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/rUt0-DmsPCs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesGcmDecrypt + +

使用AES GCM算法解密数据。

+ +函数签名: + +```go +func AesGcmDecrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/rUt0-DmsPCs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### Base64StdEncode + +

将字符串base64编码。

+ +函数签名: + +```go +func Base64StdEncode(s string) string +``` + +示例:[运行](https://go.dev/play/p/VOaUyQUreoK) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + base64Str := cryptor.Base64StdEncode("hello") + fmt.Println(base64Str) + + // Output: + // aGVsbG8= +} +``` + +### Base64StdDecode + +

解码base64字符串。

+ +函数签名: + +```go +func Base64StdDecode(s string) string +``` + +示例:[运行](https://go.dev/play/p/RWQylnJVgIe) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := cryptor.Base64StdDecode("aGVsbG8=") + fmt.Println(str) + + // Output: + // hello +} +``` + +### DesEcbEncrypt + +

使用DES ECB算法模式加密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesEcbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/8qivmPeZy4P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesEcbDecrypt + +

使用DES ECB算法模式解决密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesEcbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/8qivmPeZy4P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCbcEncrypt + +

使用DES CBC算法模式加密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesCbcEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/4cC4QvWfe3_1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCbcDecrypt + +

使用DES CBC算法模式解密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesCbcDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/4cC4QvWfe3_1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCtrEncrypt + +

使用DES CTR算法模式加密数据,参数`key`的长度是8

+ +函数签名: + +```go +func DesCtrEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/S6p_WHCgH1d) + +```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 +} +``` + +### DesCtrDecrypt + +

使用DES CTR算法模式加密数据,参数`key`的长度是8

+ +函数签名: + +```go +func DesCtrDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/S6p_WHCgH1d) + +```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 +} +``` + +### DesCtrCrypt + +

使用DES CTR算法模式加密/解密数据,参数`key`的长度是8

+ +> ⚠️ 本函数已弃用,使用`DesCtrEncrypt`和`DesCtrDecrypt`代替。 + +函数签名: + +```go +func DesCtrCrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/9-T6OjKpcdw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCtrCrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCfbEncrypt + +

使用DES CFB算法模式加密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesCfbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/y-eNxcFBlxL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCfbDecrypt + +

使用DES CFB算法模式解决密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesCfbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/y-eNxcFBlxL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesOfbEncrypt + +

使用DES OFB算法模式加密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesOfbEncrypt(data, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/74KmNadjN1J) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesOfbDecrypt + +

使用DES OFB算法模式解密数据,参数`key`的长度是8。

+ +函数签名: + +```go +func DesOfbDecrypt(encrypted, key []byte) []byte +``` + +示例:[运行](https://go.dev/play/p/74KmNadjN1J) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### HmacMd5 + +

获取字符串md5 hmac值。

+ +函数签名: + +```go +func HmacMd5(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/uef0q1fz53I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacMd5(str, key) + fmt.Println(hms) + + // Output: + // e834306eab892d872525d4918a7a639a +} +``` + +### HmacMd5WithBase64 + +

获取字符串md5 hmac base64字符串值。

+ +函数签名: + +```go +func HmacMd5WithBase64(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/UY0ng2AefFC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacMd5WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 6DQwbquJLYclJdSRinpjmg== +} +``` + +### HmacSha1 + +

获取字符串的sha1 hmac值。

+ +函数签名: + +```go +func HmacSha1(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/1UI4oQ4WXKM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha1(str, key) + fmt.Println(hms) + + // Output: + // 5c6a9db0cccb92e36ed0323fd09b7f936de9ace0 +} +``` + +### HmacSha1WithBase64 + +

获取字符串的sha1 base64值。

+ +函数签名: + +```go +func HmacSha1WithBase64(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/47JmmGrnF7B) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha1WithBase64(str, key) + fmt.Println(hms) + + // Output: + // XGqdsMzLkuNu0DI/0Jt/k23prOA= +} +``` + +### HmacSha256 + +

获取字符串sha256 hmac值。

+ +函数签名: + +```go +func HmacSha256(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/HhpwXxFhhC0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha256(str, key) + fmt.Println(hms) + + // Output: + // 315bb93c4e989862ba09cb62e05d73a5f376cb36f0d786edab0c320d059fde75 +} +``` + +### HmacSha256WithBase64 + +

获取字符串sha256 hmac base64值。

+ +函数签名: + +```go +func HmacSha256WithBase64(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/EKbkUvPTLwO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha256WithBase64(str, key) + fmt.Println(hms) + + // Output: + // MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU= +} +``` + +### HmacSha512 + +

获取字符串sha512 hmac值。

+ +函数签名: + +```go +func HmacSha512(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/59Od6m4A0Ud) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha512(str, key) + fmt.Println(hms) + + // Output: + // dd8f1290a9dd23d354e2526d9a2e9ce8cffffdd37cb320800d1c6c13d2efc363288376a196c5458daf53f8e1aa6b45a6d856303d5c0a2064bff9785861d48cfc +} +``` + +### HmacSha512WithBase64 + +

获取字符串sha512 hmac base64值。

+ +函数签名: + +```go +func HmacSha512WithBase64(str, key string) string +``` + +示例:[运行](https://go.dev/play/p/c6dSe3E2ydU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha512WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A== +} +``` + +### Md5String + +

获取字符串md5值。

+ +函数签名: + +```go +func Md5String(str string) string +``` + +示例:[运行](https://go.dev/play/p/1bLcVetbTOI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + md5Str := cryptor.Md5String(str) + fmt.Println(md5Str) + + // Output: + // 5d41402abc4b2a76b9719d911017c592 +} +``` + +### Md5StringWithBase64 + +

获取字符串md5 base64值。

+ +函数签名: + +```go +func Md5StringWithBase64(s string) string +``` + +示例:[运行](https://go.dev/play/p/Tcb-Z7LN2ax) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5StringWithBase64("hello") + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} +``` + +### Md5Byte + +

获取byte slice的md5值。

+ +函数签名: + +```go +func Md5Byte(data []byte) string +``` + +示例:[运行](https://go.dev/play/p/suraalH8lyC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5Byte([]byte{'a'}) + fmt.Println(md5Str) + + // Output: + // 0cc175b9c0f1b6a831c399e269772661 +} +``` + +### Md5ByteWithBase64 + +

获取byte slice的md5 base64值。

+ +函数签名: + +```go +func Md5ByteWithBase64(data []byte) string +``` + +示例:[运行](https://go.dev/play/p/Lx4gH7Vdr5_y) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5ByteWithBase64([]byte("hello")) + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} +``` + +### Md5File + +

获取文件md5值。

+ +函数签名: + +```go +func Md5File(filepath string) (string, error) +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + s := cryptor.Md5File("./main.go")) + fmt.Println(s) +} +``` + +### Sha1 + +

获取字符串sha1值。

+ +函数签名: + +```go +func Sha1(str string) string +``` + +示例:[运行](https://go.dev/play/p/_m_uoD1deMT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha1(str) + fmt.Println(result) + + // Output: + // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d +} +``` + +### Sha1WithBase64 + +

获取字符串sha1 base64值。

+ +函数签名: + +```go +func Sha1WithBase64(str string) string +``` + +示例:[运行](https://go.dev/play/p/fSyx-Gl2l2-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha1WithBase64("hello") + fmt.Println(result) + + // Output: + // qvTGHdzF6KLavt4PO0gs2a6pQ00= +} +``` + +### Sha256 + +

获取字符串sha256值。

+ +函数签名: + +```go +func Sha256(str string) string +``` + +示例:[运行](https://go.dev/play/p/tU9tfBMIAr1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha256(str) + fmt.Println(result) + + // Output: + // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 +} +``` + +### Sha256WithBase64 + +

获取字符串sha256 base64值。

+ +函数签名: + +```go +func Sha256WithBase64(str string) string +``` + +示例:[运行](https://go.dev/play/p/85IXJHIal1k) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha256WithBase64("hello") + fmt.Println(result) + + // Output: + // LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= +} +``` + +### Sha512 + +

获取字符串sha512值。

+ +函数签名: + +```go +func Sha512(str string) string +``` + +示例:[运行](https://go.dev/play/p/3WsvLYZxsHa) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha512(str) + fmt.Println(result) + + // Output: + // 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 +} +``` + +### Sha512WithBase64 + +

获取字符串sha512 base64值。

+ +函数签名: + +```go +func Sha512WithBase64(str string) string +``` + +示例:[运行](https://go.dev/play/p/q_fY2rA-k5I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha512WithBase64("hello") + fmt.Println(result) + + // Output: + // m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw== +} +``` + +### GenerateRsaKey + +

在当前目录下创建rsa私钥文件和公钥文件。

+ +函数签名: + +```go +func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error +``` + +示例:[运行](https://go.dev/play/p/zutRHrDqs0X) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + fmt.Println(err) + } +} +``` + +### RsaEncrypt + +

用公钥文件ras加密数据。

+ +函数签名: + +```go +func RsaEncrypt(data []byte, pubKeyFileName string) []byte +``` + +示例:[运行](https://go.dev/play/p/7_zo6mrx-eX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") + decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### RsaDecrypt + +

用私钥文件rsa解密数据。

+ +函数签名: + +```go +func RsaDecrypt(data []byte, privateKeyFileName string) []byte +``` + +示例:[运行](https://go.dev/play/p/7_zo6mrx-eX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") + decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### GenerateRsaKeyPair + +

创建rsa公钥私钥和key。

+ +函数签名: + +```go +func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) +``` + +示例:[运行](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) +} +``` + +### RsaEncryptOAEP + +

rsa OAEP加密。

+ +函数签名: + +```go +func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub) + if err != nil { + return + } + + decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri) + if err != nil { + return + } + + fmt.Println(string(decrypted)) + + // Output: + // hello world +} +``` + +### RsaDecryptOAEP + +

rsa OAEP解密。

+ +函数签名: + +```go +func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub) + if err != nil { + return + } + + decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri) + if err != nil { + return + } + + fmt.Println(string(decrypted)) + + // Output: + // hello world +} +``` + +### RsaSign + +

应用RSA算法签名数据。

+ +函数签名: + +```go +func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/qhsbf8BJ6Mf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private.pem" + publicKey := "./rsa_public.pem" + + signature, err := cryptor.RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = cryptor.RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } +} +``` + +### RsaVerifySign + +

验证数据的签名是否符合RSA算法。

+ +函数签名: + +```go +func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error +``` + +示例:[运行](https://go.dev/play/p/qhsbf8BJ6Mf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private.pem" + publicKey := "./rsa_public.pem" + + signature, err := cryptor.RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = cryptor.RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } +} +``` diff --git a/docs/api/packages/datastructure/copyonwritelist.md b/docs/api/packages/datastructure/copyonwritelist.md new file mode 100644 index 00000000..db054a4b --- /dev/null +++ b/docs/api/packages/datastructure/copyonwritelist.md @@ -0,0 +1,525 @@ +# CopyOnWriteList + +CopyOnWriteList 是一个线程安全的 List 实现,底层使用 go 切片。写入时,会复制一份新的切片,写入完成后,再将新的切片赋值给原来的切片。读取时,直接读取原来的切片。 + +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go](https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go) + +## 用法 + +```go +import ( + "github.com/duke-git/lancet/datastructure/list" +) + +``` + +
+ +## 目录 + +- [NewCopyOnWriteList](#NewCopyOnWriteList) +- [Size](#Size) +- [Get](#Get) +- [Set](#Set) +- [Remove](#Remove) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [IndexOfFunc](#IndexOfFunc) +- [LastIndexOfFunc](#LastIndexOfFunc) +- [IsEmpty](#IsEmpty) +- [Contain](#Contain) +- [ValueOf](#ValueOf) +- [Add](#Add) +- [AddAll](#AddAll) +- [AddByIndex](#AddByIndex) +- [DeleteAt](#DeleteAt) +- [DeleteIf](#DeleteIf) +- [DeleteBy](#DeleteBy) +- [DeleteRange](#DeleteRange) +- [Equal](#Equal) + +## 文档 + +### NewCopyOnWriteList + +返回一个具有空切片的 CopyOnWriteList。 + +```go +type CopyOnWriteList[T any] struct { + data []T + lock sync.Locker +} + +func NewCopyOnWriteList() *CopyOnWriteList + +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l) +} + +``` + +### Size + +返回 CopyOnWriteList 的长度。 + +```go +func (l *CopyOnWriteList[T]) Size() int +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Size()) +} + +``` + +### Get + +返回列表中指定位置的元素 + +```go +func (c *CopyOnWriteList[T]) Get(index int) *T +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Get(2)) +} + +``` + +### Set + +将此列表中指定位置的元素替换为指定元素。 + +```go +func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool) +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Set(2, 4)) +} + +``` + +### Remove + +### IndexOf + +返回列表中值的索引,如果没有找到返回-1。 + +```go +func (c *CopyOnWriteList[T]) IndexOf(e T) int +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.IndexOf(1)) +} + +``` + +### LastIndexOf + +返回指定元素在此列表中最后出现的索引,如果此列表不包含该元素,则返回-1。 + +```go +func (c *CopyOnWriteList[T]) LastIndexOf(e T) int +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,1}) + fmt.Println(l.LastIndexOf(1)) +} + +``` + +### IndexOfFunc +

返回第一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。

+ +函数签名: + +```go +func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1, 2, 3}) + + fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0 + fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### LastIndexOfFunc +

返回最后一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。

+ +函数签名: + +```go +func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1, 2, 3, 1}) + + fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3 + fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### IsEmpty + +如果此列表不包含任何元素,则返回 true。 + +```go +func (c *CopyOnWriteList[T]) IsEmpty() bool +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{}) + fmt.Println(l.IsEmpty()) +} +``` + +### Contain + +判断 CopyOnWriteList 是否包含某个元素 + +```go +func (c *CopyOnWriteList[T]) Contain(e T) bool +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Contain(1)) +} +``` + +### ValueOf + +返回列表中索引处的值指针 + +```go +func (c *CopyOnWriteList[T]) ValueOf(index int) []T +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.ValueOf(2)) +} + +``` + +### Add + +将指定的元素追加到此列表的末尾。 + +```go +func (c *CopyOnWriteList[T]) Add(e T) bool +``` + +#### 示例 + +```go + +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + l.Add(4) + fmt.Println(l.getList()) +} + +``` + +### AddAll + +将指定集合中的所有元素追加到此列表的末尾 + +```go +func (c *CopyOnWriteList[T]) AddAll(e []T) bool +``` + +#### 示例 + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + l.AddAll([]int{4,5,6}) + fmt.Println(l.getList()) +} + +``` + +### AddByIndex + +将指定元素插入此列表中的指定位置。 + +```go +func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.AddByIndex(2, 6) + fmt.Println(l.getList()) +} + +``` + +### DeleteAt + +移除此列表中指定位置的元素。 + +```go +func (c *CopyOnWriteList[T]) DeleteAt(index int) (oldValue *T, ok bool) +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteAt(2) + fmt.Println(l.getList()) +} +``` + +### DeleteIf + +从此列表中删除第一个出现的指定元素(如果该元素存在)。 + +```go +func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) (oldValue *T, ok bool) +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteIf(func(i int) bool { + return i == 2 + }) + fmt.Println(l.getList()) +} +``` + +### DeleteBy + +从此列表中删除第一个出现的指定元素(如果该元素存在)。 + +```go +func (c *CopyOnWriteList[T]) DeleteBy(e T) (*T bool) +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteBy(2) + fmt.Println(l.getList()) +} +``` + +### DeleteRange + +从该列表中删除索引介于 fromIndex(包含)和 toIndex(不包含)之间的所有元素。 +(左闭右开)。 + +```go +func (c *CopyOnWriteList[T]) DeleteRange(start int, end int) +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9}) + list.DeleteRange(2, 5) + fmt.Println(l.getList()) +} +``` + +### Equal + +如果指定的对象等于此列表,则返回 true。 + +```go +func (c *CopyOnWriteList[T]) Equal(e []T) bool +``` + +#### 示例 + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9}) + fmt.Println(l.Equal([]int{1,2,3,4,5,6,7,8,9})) +} +``` diff --git a/docs/api/packages/datastructure/hashmap.md b/docs/api/packages/datastructure/hashmap.md new file mode 100644 index 00000000..3369852f --- /dev/null +++ b/docs/api/packages/datastructure/hashmap.md @@ -0,0 +1,346 @@ +# HashMap + +HashMap 数据结构实现 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go](https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go) + +
+ +## 用法 + +```go +import ( + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) +``` + +
+ +## 目录 + +- [NewHashMap](#NewHashMap) +- [NewHashMapWithCapacity](#NewHashMapWithCapacity) +- [Get](#Get) +- [Put](#Put) +- [Delete](#Delete) +- [Contains](#Contains) +- [Iterate](#Iterate) +- [Keys](#Keys) +- [Values](#Values) +- [FilterByValue](#FilterByValue) + +
+ +## API 文档 + +### NewHashMap + +

新建默认容量(1 << 10)的HashMap指针实例

+ +函数签名: + +```go +func NewHashMap() *HashMap +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + fmt.Println(hm) +} +``` + +### NewHashMapWithCapacity + +

新建指定容量和长度的HashMap指针实例.

+ +函数签名: + +```go +func NewHashMapWithCapacity(size, capacity uint64) *HashMap +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMapWithCapacity(uint64(100), uint64(1000)) + fmt.Println(hm) +} +``` + +### Get + +

在hashmap中根据key获取值

+ +函数签名: + +```go +func (hm *HashMap) Get(key any) any +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + val := hm.Get("a") + + fmt.Println(val) //nil +} +``` + +### Put + +

将key-value放入hashmap中

+ +函数签名: + +```go +func (hm *HashMap) Put(key any, value any) any +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + + val := hm.Get("a") + fmt.Println(val) //1 +} +``` + +### Delete + +

将指定的key从hashmap中删除

+ +函数签名: + +```go +func (hm *HashMap) Delete(key any) +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + val := hm.Get("a") + fmt.Println(val) //1 + + hm.Delete("a") + val = hm.Get("a") + fmt.Println(val) //nil +} +``` + +### Contains + +

判断hashmap中是否包含指定的key

+ +函数签名: + +```go +func (hm *HashMap) Contains(key any) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + + fmt.Println(hm.Contains("a")) //true + fmt.Println(hm.Contains("b")) //false +} +``` + + +### Iterate + +

迭代hashmap,对每个key和value执行iteratee函数

+ +函数签名: + +```go +func (hm *HashMap) Iterate(iteratee func(key, value any)) +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + hm.Iterate(func(key, value any) { + fmt.Println(key) + fmt.Println(value) + }) +} +``` + + + +### Keys + +

返回hashmap所有key的切片 (随机顺序)

+ +函数签名: + +```go +func (hm *HashMap) Keys() []any +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + keys := hm.Keys() + fmt.Println(keys) //[]interface{"a", "b", "c"} +} +``` + + +### Values + +

返回hashmap所有值的切片 (随机顺序)。

+ +函数签名: + +```go +func (hm *HashMap) Values() []any +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + values := hm.Values() + fmt.Println(values) //[]interface{2, 1, 3} +} +``` + + +### FilterByValue + +

返回一个过滤后的HashMap。 如果任何值与 perdicate 函数不匹配,则返回 nil,否则返回包含选定值的 HashMap。

+ +函数签名: + +```go +func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap +``` + +示例: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := hashmap.NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + hm.Put("d", 4) + hm.Put("e", 5) + hm.Put("f", 6) + + filteredHM := hm.FilterByValue(func(value any) bool { + return value.(int) == 1 || value.(int) == 3 + }) + + fmt.Println(filteredHM.Size()) //2 +} +``` diff --git a/docs/api/packages/datastructure/heap.md b/docs/api/packages/datastructure/heap.md new file mode 100644 index 00000000..e4c59613 --- /dev/null +++ b/docs/api/packages/datastructure/heap.md @@ -0,0 +1,364 @@ +# Heap +堆,切片实现的二叉堆数据结构。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go](https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go) + + +
+ +## 用法 +```go +import ( + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) +``` + +
+ +## 目录 + +- [MaxHeap](#MaxHeap) +- [Push](#Push) +- [Pop](#Pop) +- [Peek](#Peek) +- [Data](#Data) +- [Size](#Size) + + +
+ +## API文档 + +### 1. MaxHeap +MaxHeap是通过slice实现的二叉堆树,根节点的key既大于等于左子树的key值且大于等于右子树的key值。 + +### NewMaxHeap +

返回NewMaxHeap指针实例

+ +函数签名: + +```go +type MaxHeap[T any] struct { + data []T + comparator constraints.Comparator +} +func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T] +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + fmt.Println(maxHeap) +} +``` + + + + +### Push +

向堆中插入数据

+ +函数签名: + +```go +func (h *MaxHeap[T]) Push(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} +} +``` + + + + +### Pop +

返回堆中最大值并将其从堆中删除,如果堆为空,返回零值并返回false

+ +函数签名: + +```go +func (h *MaxHeap[T]) Pop() (T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + val, ok := maxHeap.Pop() + + fmt.Println(val) //12 + fmt.Println(ok) //true +} +``` + + + +### Peek +

返回堆中最大值,如果堆为空,返回零值并返回false

+ +函数签名: + +```go +func (h *MaxHeap[T]) Peek() (T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + val, ok := maxHeap.Peek() + + fmt.Println(val) //12 + fmt.Println(maxHeap.Size()) //12 +} +``` + + + +### Data +

返回堆中全部元素的切片

+ +函数签名: + +```go +func (h *MaxHeap[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} +} +``` + + +### Size +

返回堆中元素的数量

+ +函数签名: + +```go +func (h *MaxHeap[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Size()) //3 +} +``` + + + +### PrintStructure +

打印堆的树形结构

+ +函数签名: + +```go +func (h *MaxHeap[T]) PrintStructure() +``` +示例: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.PrintStructure()) +// 12 +// 9 11 +// 4 8 10 7 +// 1 3 5 6 2 +} +``` \ No newline at end of file diff --git a/docs/api/packages/datastructure/link.md b/docs/api/packages/datastructure/link.md new file mode 100644 index 00000000..76b5e8d6 --- /dev/null +++ b/docs/api/packages/datastructure/link.md @@ -0,0 +1,1017 @@ +# Linklist +Linklist是链表数据结构,它的节点有一个值和一个指向下一个节点的指针。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/link/singlylink.go](https://github.com/duke-git/lancet/blob/main/datastructure/link/singlylink.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/link/doublylink.go](https://github.com/duke-git/lancet/blob/main/datastructure/link/doublylink.go) + + +
+ +## 用法 +```go +import ( + link "github.com/duke-git/lancet/v2/datastructure/link" +) +``` + +
+ +## 目录 + +### 1. SinglyLink单链表 + +- [NewSinglyLink](#NewSinglyLink) +- [Values](#SinglyLink_Values) +- [InsertAt](#SinglyLink_InsertAt) +- [InsertAtHead](#SinglyLink_InsertAtHead) +- [InsertAtTail](#SinglyLink_InsertAtTail) +- [DeleteAt](#SinglyLink_DeleteAt) +- [DeleteAtHead](#SinglyLink_DeleteAtHead) +- [DeleteAtTail](#SinglyLink_DeleteAtTail) +- [DeleteValue](#SinglyLink_DeleteValue) +- [Reverse](#SinglyLink_Reverse) +- [GetMiddleNode](#SinglyLink_GetMiddleNode) +- [Size](#SinglyLink_Size) +- [IsEmpty](#SinglyLink_IsEmpty) +- [Clear](#SinglyLink_Clear) +- [Print](#SinglyLink_Print) + +### 2. DoublyLink双向链表 + +- [NewDoublyLink](#NewDoublyLink) +- [Values](#DoublyLink_Values) +- [InsertAt](#DoublyLink_InsertAt) +- [InsertAtHead](#DoublyLink_InsertAtHead) +- [InsertAtTail](#DoublyLink_InsertAtTail) +- [DeleteAt](#DoublyLink_DeleteAt) +- [DeleteAtHead](#DoublyLink_DeleteAtHead) +- [DeleteAtTail](#DoublyLink_DeleteAtTail) +- [Reverse](#DoublyLink_Reverse) +- [GetMiddleNode](#DoublyLink_GetMiddleNode) +- [Size](#DoublyLink_Size) +- [IsEmpty](#DoublyLink_IsEmpty) +- [Clear](#DoublyLink_Clear) +- [Print](#DoublyLink_Print) + + +
+ +## 文档 + +### 1. SinglyLink +SingleLink是单向链表,它的节点有一个值和一个指向链表的下一个节点的指针。 + +### NewSinglyLink +

创建SinglyLink指针实例

+ +函数签名: + +```go +type LinkNode[T any] struct { + Value T + Next *LinkNode[T] +} +type SinglyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} +func NewSinglyLink[T any]() *SinglyLink[T] +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + fmt.Println(lk) +} +``` + + + +### Values +

返回链表中所有节点值的切片

+ +函数签名: + +```go +func (link *SinglyLink[T]) Values() []T +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + + +### InsertAt +

将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数

+ +函数签名: + +```go +func (link *SinglyLink[T]) InsertAt(index int, value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAt(1, 1) //do nothing + + lk.InsertAt(0, 1) + lk.InsertAt(1, 2) + lk.InsertAt(2, 3) + lk.InsertAt(2, 4) + + fmt.Println(lk.Values()) //[]int{1, 2, 4, 3} +} +``` + + + + +### InsertAtHead +

将值插入到链表表头

+ +函数签名: + +```go +func (link *SinglyLink[T]) InsertAtHead(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtHead(1) + lk.InsertAtHead(2) + lk.InsertAtHead(3) + + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + + +### InsertAtTail +

将值插入到链表末尾

+ +函数签名: + +```go +func (link *SinglyLink[T]) InsertAtTail(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAt +

删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数-1

+ +函数签名: + +```go +func (link *SinglyLink[T]) DeleteAt(index int) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAt(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAtHead +

删除链表头节点

+ +函数签名: + +```go +func (link *SinglyLink[T]) DeleteAtHead() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAtHead() + + fmt.Println(lk.Values()) //[]int{2, 3, 4} +} +``` + + + + +### DeleteAtTail +

删除链表末尾节点

+ +函数签名: + +```go +func (link *SinglyLink[T]) DeleteAtTail() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.DeleteAtTail() + + fmt.Println(lk.Values()) //[]int{1, 2} +} +``` + + + +### DeleteValue +

删除链表中指定的value值

+ +函数签名: + +```go +func (link *SinglyLink[T]) DeleteValue(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.DeleteValue(2) + fmt.Println(lk.Values()) //[]int{1, 3} +} +``` + + + + +### Reverse +

反转链表所有节点顺序

+ +函数签名: + +```go +func (link *SinglyLink[T]) Reverse() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Reverse() + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + +### GetMiddleNode +

获取链表中部节点

+ +函数签名: + +```go +func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + midNode := lk.GetMiddleNode() + fmt.Println(midNode.Value) //2 +} +``` + + + +### Size +

获取链表节点数量

+ +函数签名: + +```go +func (link *SinglyLink[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Size()) //3 +} +``` + + + +### IsEmpty +

判断链表是否为空

+ +函数签名: + +```go +func (link *SinglyLink[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + fmt.Println(lk.IsEmpty()) //true + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.IsEmpty()) //false +} +``` + + + +### Clear +

清空链表所有节点

+ +函数签名: + +```go +func (link *SinglyLink[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Clear() + + fmt.Println(lk.Values()) // +} +``` + + + +### Print +

打印链表结构

+ +函数签名: + +```go +func (link *SinglyLink[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Print() //[ &{Value:1 Pre: Next:0xc0000a4048}, &{Value:2 Pre: Next:0xc0000a4060}, &{Value:3 Pre: Next:} ] +} +``` + + + +### 2. DoublyLink +DoublyLink是双向链表,它的节点有一个值,next指针指向下一个节点,pre指针指向前一个节点。 + +### NewDoublyLink +

创建NewDoublyLink指针实例

+ +函数签名: + +```go +type LinkNode[T any] struct { + Value T + Pre *LinkNode[T] + Next *LinkNode[T] +} +type DoublyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} +func NewDoublyLink[T any]() *DoublyLink[T] +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + fmt.Println(lk) +} +``` + + + +### Values +

返回链表中所有节点值的切片

+ +函数签名: + +```go +func (link *DoublyLink[T]) Values() []T +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + + +### InsertAt +

将值插入到索引处的链表中,索引应大于或等于0且小于或等于链表节点数

+ +函数签名: + +```go +func (link *DoublyLink[T]) InsertAt(index int, value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAt(1, 1) //do nothing + + lk.InsertAt(0, 1) + lk.InsertAt(1, 2) + lk.InsertAt(2, 3) + lk.InsertAt(2, 4) + + fmt.Println(lk.Values()) //[]int{1, 2, 4, 3} +} +``` + + + + +### InsertAtHead +

将值插入到链表表头

+ +函数签名: + +```go +func (link *DoublyLink[T]) InsertAtHead(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtHead(1) + lk.InsertAtHead(2) + lk.InsertAtHead(3) + + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + + +### InsertAtTail +

将值插入到链表末尾

+ +函数签名: + +```go +func (link *DoublyLink[T]) InsertAtTail(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAt +

删除特定索引处的值,索引应大于或等于0且小于或等于链接节点数-1

+ +函数签名: + +```go +func (link *DoublyLink[T]) DeleteAt(index int) +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAt(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAtHead +

删除链表头节点

+ +函数签名: + +```go +func (link *DoublyLink[T]) DeleteAtHead() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAtHead() + + fmt.Println(lk.Values()) //[]int{2, 3, 4} +} +``` + + + + +### DeleteAtTail +

删除链表末尾节点

+ +函数签名: + +```go +func (link *DoublyLink[T]) DeleteAtTail() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.DeleteAtTail() + + fmt.Println(lk.Values()) //[]int{1, 2} +} +``` + + + + +### Reverse +

反转链表所有节点顺序

+ +函数签名: + +```go +func (link *DoublyLink[T]) Reverse() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Reverse() + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + +### GetMiddleNode +

获取链表中部节点

+ +函数签名: + +```go +func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + midNode := lk.GetMiddleNode() + fmt.Println(midNode.Value) //2 +} +``` + + + +### Size +

获取链表节点数量

+ +函数签名: + +```go +func (link *DoublyLink[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Size()) //3 +} +``` + + + +### IsEmpty +

判断链表是否为空

+ +函数签名: + +```go +func (link *DoublyLink[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + fmt.Println(lk.IsEmpty()) //true + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.IsEmpty()) //false +} +``` + + + +### Clear +

清空链表所有节点

+ +函数签名: + +```go +func (link *DoublyLink[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Clear() + + fmt.Println(lk.Values()) // +} +``` + + + +### Print +

打印链表结构

+ +函数签名: + +```go +func (link *DoublyLink[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Print() // +} +``` \ No newline at end of file diff --git a/docs/api/packages/datastructure/list.md b/docs/api/packages/datastructure/list.md new file mode 100644 index 00000000..5051efca --- /dev/null +++ b/docs/api/packages/datastructure/list.md @@ -0,0 +1,1116 @@ +# List +List是线性表数据结构, 用go切片实现。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/list/list.go](https://github.com/duke-git/lancet/blob/main/datastructure/list/list.go) + + +
+ +## 用法 +```go +import ( + "github.com/duke-git/lancet/v2/datastructure" +) +``` + +
+ +## 目录 + +- [NewList](#NewList) +- [Contain](#Contain) +- [Data](#Data) +- [ValueOf](#ValueOf) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [IndexOfFunc](#IndexOfFunc) +- [LastIndexOfFunc](#LastIndexOfFunc) +- [Push](#Push) +- [PopFirst](#PopFirst) +- [PopLast](#PopLast) +- [DeleteAt](#DeleteAt) +- [InsertAt](#InsertAt) +- [UpdateAt](#UpdateAt) +- [Equal](#Equal) +- [IsEmpty](#IsEmpty) +- [Clear](#Clear) +- [Clone](#Clone) +- [Merge](#Merge) +- [Size](#Size) +- [Cap](#Cap) +- [Swap](#Swap) +- [Reverse](#Reverse) +- [Unique](#Unique) +- [Union](#Union) +- [Intersection](#Intersection) +- [Difference](#Difference) +- [SymmetricDifference](#SymmetricDifference) +- [RetainAll](#RetainAll) +- [DeleteAll](#DeleteAll) +- [ForEach](#ForEach) +- [Iterator](#Iterator) +- [ListToMap](#ListToMap) +- [SubList](#SubList) +- [DeleteIf](#DeleteIf) + +
+ +## 文档 + +### NewList +

返回List指针实例

+ +函数签名: + +```go +type List[T any] struct { + data []T +} +func NewList[T any](data []T) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + fmt.Println(li) +} +``` + + + +### Contain +

判断列表中是否包含特定值

+ +函数签名: + +```go +func (l *List[T]) Contain(value T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.Contain(1)) //true + fmt.Println(li.Contain(0)) //false +} +``` + + + + +### Data +

返回List中所有数据(切片)

+ +函数签名: + +```go +func (l *List[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + data := li.Data() + + fmt.Println(data) //[]int{1, 2, 3} +} +``` + + + + +### ValueOf +

返回列表中索引处的值指针

+ +函数签名: + +```go +func (l *List[T]) ValueOf(index int) (*T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.ValueOf(0) + + fmt.Println(*v) //1 + fmt.Println(ok) //true +} +``` + + + + +### IndexOf +

返回列表中值的索引,如果没有找到返回-1

+ +函数签名: + +```go +func (l *List[T]) IndexOf(value T) int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.IndexOf(1)) //0 + fmt.Println(li.IndexOf(0)) //-1 +} +``` + + +### LastIndexOf +

返回列表中最后一次出现的值的索引。如果未找到,则返回-1

+ +函数签名: + +```go +func (l *List[T]) LastIndexOf(value T) int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 1}) + + fmt.Println(li.LastIndexOf(1)) // 3 + fmt.Println(li.LastIndexOf(0)) //-1 +} +``` + +### IndexOfFunc +

返回第一个符合函数条件的元素的索引。如果未找到,则返回-1

+ +函数签名: + +```go +func (l *List[T]) IndexOfFunc(f func(T) bool) int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.IndexOfFunc(func(a int) bool { return a == 1 })) //0 + fmt.Println(li.IndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### LastIndexOfFunc +

返回最后一个符合函数条件的元素的索引。如果未找到,则返回-1

+ +函数签名: + +```go +func (l *List[T]) LastIndexOfFunc(f func(T) bool) int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 1}) + + fmt.Println(li.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3 + fmt.Println(li.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + + + +### Push +

将值附加到列表末尾

+ +函数签名: + +```go +func (l *List[T]) Push(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + li.Push(4) + + fmt.Println(li.Data()) //[]int{1, 2, 3, 4} +} +``` + + + + +### PopFirst +

删除列表的第一个值并返回该值

+ +函数签名: + +```go +func (l *List[T]) PopFirst() (*T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.PopFirst() + + fmt.Println(*v) //1 + fmt.Println(ok) //true + fmt.Println(li.Data()) //2, 3 +} +``` + + + + + +### PopFirst +

删除列表的最后一个值并返回该值

+ +函数签名: + +```go +func (l *List[T]) PopLast() (*T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.PopLast() + + fmt.Println(*v) //3 + fmt.Println(ok) //true + fmt.Println(li.Data()) //1, 2 +} +``` + + + + +### DeleteAt +

删除索引处列表的值,如果索引不在0和列表数据长度之间,则不执行任何操作

+ +函数签名: + +```go +func (l *List[T]) DeleteAt(index int) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + + li.DeleteAt(-1) + fmt.Println(li.Data()) //1,2,3,4 + + li.DeleteAt(4) + fmt.Println(li.Data()) //1,2,3,4 + + li.DeleteAt(0) + fmt.Println(li.Data()) //2,3,4 + + li.DeleteAt(2) + fmt.Println(li.Data()) //2,3 +} +``` + + + + +### InsertAt +

在索引处插入值到列表中,如果索引不在 0 和列表数据长度之间,则不执行任何操作

+ +函数签名: + +```go +func (l *List[T]) InsertAt(index int, value T) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + li.InsertAt(-1, 0) + fmt.Println(li.Data()) //1,2,3 + + li.InsertAt(4, 0) + fmt.Println(li.Data()) //1,2,3 + + li.InsertAt(3, 4) + fmt.Println(li.Data()) //1,2,3,4 + + // li.InsertAt(2, 4) + // fmt.Println(li.Data()) //1,2,4,3 +} +``` + + + +### UpdateAt +

更新索引处列表的值,索引应该在0和列表数据长度-1之间

+ +函数签名: + +```go +func (l *List[T]) UpdateAt(index int, value T) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + li.UpdateAt(-1, 0) + fmt.Println(li.Data()) //1,2,3 + + li.UpdateAt(2, 4) + fmt.Println(li.Data()) //1,2,4 + + li.UpdateAt(3, 5) + fmt.Println(li.Data()) //1,2,4 +} +``` + + +### Equal +

比较一个列表和另一个列表,在每个元素上使用 reflect.DeepEqual

+ +函数签名: + +```go +func (l *List[T]) Equal(other *List[T]) bool +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{1, 2, 3, 4}) + li3 := list.NewList([]int{1, 2, 3}) + + fmt.Println(li1.Equal(li2)) //true + fmt.Println(li1.Equal(li3)) //false +} +``` + + + +### IsEmpty +

判断列表是否为空

+ +函数签名: + +```go +func (l *List[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3}) + li2 := list.NewList([]int{}) + + fmt.Println(li1.IsEmpty()) //false + fmt.Println(li2.IsEmpty()) //true +} +``` + + + + +### Clear +

清空列表数据

+ +函数签名: + +```go +func (l *List[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + li.Clear() + + fmt.Println(li.Data()) // empty +} +``` + + + +### Clone +

返回列表的一个拷贝

+ +函数签名: + +```go +func (l *List[T]) Clone() *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + cloneList := li.Clone() + + fmt.Println(cloneList.Data()) // 1,2,3 +} +``` + + + + +### Merge +

合并两个列表,返回新的列表

+ +函数签名: + +```go +func (l *List[T]) Merge(other *List[T]) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Merge(li2) + + fmt.Println(li3.Data()) //1, 2, 3, 4, 4, 5, 6 +} +``` + + + +### Size +

返回列表数据项的数量

+ +函数签名: + +```go +func (l *List[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + + fmt.Println(li.Size()) //4 +} +``` + + + +### Cap +

返回列表数据容量

+ +函数签名: + +```go +func (l *List[T]) Cap() int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + data := make([]int, 0, 100) + + li := list.NewList(data) + + fmt.Println(li.Cap()) // 100 +} +``` + + + +### Swap +

交换列表中两个索引位置的值

+ +函数签名: + +```go +func (l *List[T]) Swap(i, j int) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + li.Swap(0, 3) + + fmt.Println(li.Data()) //4, 2, 3, 1 +} +``` + + + + +### Reverse +

反转列表的数据项顺序

+ +函数签名: + +```go +func (l *List[T]) Reverse() +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + li.Reverse() + + fmt.Println(li.Data()) //4, 3, 2, 1 +} +``` + + + + +### Unique +

列表去除重复数据项

+ +函数签名: + +```go +func (l *List[T]) Unique() +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 2, 3, 4}) + li.Unique() + + fmt.Println(li.Data()) //1,2,3,4 +} +``` + + + + +### Union +

两个列表取并集,去除重复数据项

+ +函数签名: + +```go +func (l *List[T]) Union(other *List[T]) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Union(li2) + + fmt.Println(li3.Data()) //1,2,3,4,5,6 +} +``` + + + + +### Intersection +

两个列表取交集

+ +函数签名: + +```go +func (l *List[T]) Intersection(other *List[T]) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Intersection(li2) + + fmt.Println(li3.Data()) //4 +} +``` + + +### Difference +

差集运算。

+ +函数签名: + +```go +func (l *List[T]) Difference(other *List[T]) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + + list3 := list1.Intersection(list2) + + fmt.Println(list3.Data()) //3 +} +``` + + +### SymmetricDifference +

对称差集运算。

+ +函数签名: + +```go +func (l *List[T]) SymmetricDifference(other *List[T]) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + + list3 := list1.Intersection(list2) + + fmt.Println(list3.Data()) //3, 4 +} +``` + + +### RetainAll +

仅保留列表中包含在给定列表中的元素。

+ +函数签名: + +```go +func (l *List[T]) RetainAll(list *List[T]) bool +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + retain := NewList([]int{1, 2}) + retain1 := NewList([]int{2, 3}) + retain2 := NewList([]int{1, 2, 5}) + + list.RetainAll(retain) + list1.RetainAll(retain1) + list2.RetainAll(retain2) + + fmt.Println(list.Data()) //1, 2 + fmt.Println(list1.Data()) //2, 3 + fmt.Println(list2.Data()) //1, 2 +} +``` + + +### DeleteAll +

从列表中删除给定列表中包含的所有元素。

+ +函数签名: + +```go +func (l *List[T]) DeleteAll(list *List[T]) bool +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + del := NewList([]int{1}) + del1 := NewList([]int{2, 3}) + del2 := NewList([]int{1, 2, 5}) + + list.DeleteAll(del) + list1.DeleteAll(del1) + list2.DeleteAll(del2) + + fmt.Println(list.Data()) //2,3,4 + fmt.Println(list1.Data()) //1,4 + fmt.Println(list2.Data()) //3,4 +} +``` + + +### ForEach +

对列表的每个元素执行给定的操作。

+ +函数签名: + +```go +func (l *List[T]) ForEach(consumer func(T)) +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + result := make([]int, 0) + list.ForEach(func(i int) { + result = append(result, i) + }) + + fmt.Println(result.Data()) //1,2,3,4 +} +``` + + +### Iterator +

按顺序返回列表中元素的迭代器。

+ +函数签名: + +```go +func (l *List[T]) Iterator() iterator.Iterator[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + iterator := list.Iterator() + + result := make([]int, 0) + for iterator.HasNext() { + item, _ := iterator.Next() + result = append(result, item) + } + + fmt.Println(result.Data()) //1,2,3,4 +} +``` + + +### ListToMap +

基于iteratee函数将列表转换为映射map。

+ +函数签名: + +```go +func ListToMap[T any, K comparable, V any](list *List[T], iteratee func(T) (K, V)) map[K]V +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + result := ListToMap(list, func(n int) (int, bool) { + return n, n > 1 + }) + + fmt.Println(result) //map[int]bool{1: false, 2: true, 3: true, 4: true} +} +``` + + +### SubList +

返回指定的fromIndex(包含)和toIndex(不包含)之间的原始列表的子列表。

+ +函数签名: + +```go +func (l *List[T]) SubList(fromIndex, toIndex int) *List[T] +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewList([]int{1, 2, 3, 4, 5, 6}) + + fmt.Println(l.SubList(2, 5)) // []int{3, 4, 5} +} +``` + + + + +### DeleteIf +

删除列表中所有符合函数(调用函数返回true)的元素,返回删除元素的数量

+ +函数签名: + +```go +func (l *List[T]) DeleteIf(f func(T) bool) int +``` +示例: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewList([]int{1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1}) + + fmt.Println(l.DeleteIf(func(a int) bool { return a == 1 })) // 12 + fmt.Println(l.Data()) // []int{2, 3, 4} +} +``` \ No newline at end of file diff --git a/docs/api/packages/datastructure/optional.md b/docs/api/packages/datastructure/optional.md new file mode 100644 index 00000000..4d9365ab --- /dev/null +++ b/docs/api/packages/datastructure/optional.md @@ -0,0 +1,412 @@ +# Optional +Optional类型代表一个可选的值,它要么包含一个实际值,要么为空。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go) + + +
+ +## 用法 +```go +import ( + "github.com/duke-git/lancet/v2/datastructure/optional" +) +``` + +
+ +## 目录 + +- [Of](#Of) +- [FromNillable](#FromNillable) +- [Default](#Default) +- [IsNotNil](#IsNotNil) +- [IsNil](#IsNil) +- [IsNotNil](#IsNotNil) +- [IfNotNilOrElse](#IfNotNilOrElse) +- [Umwarp](#Umwarp) +- [OrElse](#OrElse) +- [OrElseGet](#OrElseGet) +- [OrElseTrigger](#OrElseTrigger) + + +
+ +## 文档 + +### Of +

返回一个包含非空值的Optional。

+ +函数签名: + +```go +func Of[T any](value T) Optional[T] +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + value := 42 + opt := optional.Of(value) + + fmt.Println(opt.Get()) + + // Output: + // 42 +} +``` + +### FromNillable +

返回一个包含给定值的Optional,该值可能为空 (nil)。

+ +函数签名: + +```go +func FromNillable[T any](value *T) Optional[T] +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + var value *int = nil + opt := optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + value = new(int) + *value = 42 + opt = optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + + // Output: + // false + // true +} +``` + + +### Default +

返回一个空Optional实例。

+ +函数签名: + +```go +func Default[T any]() Optional[T] +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + fmt.Println(optDefault.IsNil()) + + // Output: + // true +} +``` + + +### IsNil +

验证Optional是否为空。

+ +函数签名: + +```go +func (o Optional[T]) IsNil() bool +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + fmt.Println(optDefault.IsNil()) + + // Output: + // true +} +``` + +### IsNotNil +

检查当前Optional内是否存在值。

+ +函数签名: + +```go +func (o Optional[T]) IsNotNil() bool +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + var value *int = nil + opt := optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + value = new(int) + *value = 42 + opt = optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + + // Output: + // false + // true +} +``` + +### IfNotNil +

如果值存在,则使用action方法执行给定的操作。

+ +函数签名: + +```go +func (o Optional[T]) IfNotNil(action func(value T)) +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + called := false + action := func(value int) { called = true } + + optDefault := optional.Default[int]() + optDefault.IfNotNil(action) + + fmt.Println(called) + + called = false // Reset for next test + optWithValue := optional.Of(42) + optWithValue.IfNotNil(action) + + fmt.Println(optWithValue.IsNotNil()) + + // Output: + // false + // true +} +``` + + +### IfNotNilOrElse +

根据是否存在值执行相应的操作:有值则执行指定操作,没有值则执行默认操作。

+ +函数签名: + +```go +func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + calledWithValue := false + valueAction := func(value int) { calledWithValue = true } + emptyAction := func() { t.Errorf("Empty action should not be called when value is present") } + + optWithValue := optional.Of(42) + optWithValue.IfNotNilOrElse(valueAction, emptyAction) + + fmt.Println(calledWithValue) + + calledWithEmpty := false + valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") } + emptyAction = func() { calledWithEmpty = true } + + optDefault := optional.Default[int]() + optDefault.IfNotNilOrElse(valueAction, emptyAction) + + fmt.Println(calledWithEmpty) + + // Output: + // true + // true +} +``` + +### Unwrap +

如果存在,返回该值,否则引发panic。

+ +函数签名: + +```go +func (o Optional[T]) Unwrap() T +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + value := 42 + opt := optional.Of(value) + + fmt.Println(opt.Unwrap()) + + // Output: + // 42 +} +``` + + +### OrElse +

检查Optional值是否存在,如果存在,则直接返回该值。如果不存在,返回参数other值。

+ +函数签名: + +```go +func (o Optional[T]) OrElse(other T) T +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Empty[int]() + val := optDefault.OrElse(100) + fmt.Println(val) + + optWithValue := optional.Of(42) + val = optWithValue.OrElse(100) + fmt.Println(val) + + // Output: + // 100 + // 42 +} +``` + + +### OrElseGet +

检查Optional值是否存在,如果存在,则直接返回该值。如果不存在,则调用一个提供的函数 (supplier),并返回该函数的执行结果。

+ +函数签名: + +```go +func (o Optional[T]) OrElseGet(action func() T) T +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + action := func() int { return 100 } + + val := optDefault.OrElseGet(action) + fmt.Println(val) + + // Output: + // 100 +} +``` + +### OrElseTrigger +

检查Optional值是否存在,如果存在,则直接返回该值,否则返回错误。

+ +函数签名: + +```go + OrElseTrigger(errorHandler func() error) (T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + _, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") }) + + fmt.Println(err.Error()) + + optWithValue := optional.Of(42) + val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") }) + + fmt.Println(val) + fmt.Println(err) + + // Output: + // no value + // 42 + // nil +} +``` \ No newline at end of file diff --git a/docs/api/packages/datastructure/queue.md b/docs/api/packages/datastructure/queue.md new file mode 100644 index 00000000..fddc43e3 --- /dev/null +++ b/docs/api/packages/datastructure/queue.md @@ -0,0 +1,1387 @@ +# Queue +队列数据结构,包括ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/arrayqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/arrayqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/linkedqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/linkedqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/circularqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/circularqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/priorityqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/priorityqueue.go) + +
+ +## 用法 +```go +import ( + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) +``` + +
+ +## 目录 + +### 1. ArrayQueue +- [NewArrayQueue](#NewArrayQueue) +- [Data](#ArrayQueue_Data) +- [Enqueue](#ArrayQueue_Enqueue) +- [Dequeue](#ArrayQueue_Dequeue) +- [Front](#ArrayQueue_Front) +- [Back](#ArrayQueue_Back) +- [Size](#ArrayQueue_Size) +- [IsEmpty](#ArrayQueue_IsEmpty) +- [IsFull](#ArrayQueue_IsFull) +- [Clear](#ArrayQueue_Clear) +- [Contain](#ArrayQueue_Contain) + + + +### 2. LinkedQueue +- [NewLinkedQueue](#NewLinkedQueue) +- [Data](#LinkedQueue_Data) +- [Enqueue](#LinkedQueue_Enqueue) +- [Dequeue](#LinkedQueue_Dequeue) +- [Front](#LinkedQueue_Front) +- [Back](#LinkedQueue_Back) +- [Size](#LinkedQueue_Size) +- [IsEmpty](#LinkedQueue_IsEmpty) +- [Clear](#LinkedQueue_Clear) +- [Contain](#LinkedQueue_Contain) + + +### 3. CircularQueue +- [NewCircularQueue](#NewCircularQueue) +- [Data](#CircularQueue_Data) +- [Enqueue](#CircularQueue_Enqueue) +- [Dequeue](#CircularQueue_Dequeue) +- [Front](#CircularQueue_Front) +- [Back](#CircularQueue_Back) +- [Size](#CircularQueue_Size) +- [IsEmpty](#CircularQueue_IsEmpty) +- [IsFull](#CircularQueue_IsFull) +- [Clear](#CircularQueue_Clear) +- [Contain](#CircularQueue_Contain) + + + +### 4. PriorityQueue +- [NewPriorityQueue](#NewPriorityQueue) +- [Data](#PriorityQueue_Data) +- [Enqueue](#PriorityQueue_Enqueue) +- [Dequeue](#PriorityQueue_Dequeue) +- [IsEmpty](#PriorityQueue_IsEmpty) +- [IsFull](#PriorityQueue_IsFull) +- [Size](#PriorityQueue_Size) + + +
+ +## 文档 + +### 1. ArrayQueue +切片实现普通队列数据结构 + +### NewArrayQueue +

返回具有特定容量的ArrayQueue指针

+ +函数签名: + +```go +func NewArrayQueue[T any](capacity int) *ArrayQueue[T] + +type ArrayQueue[T any] struct { + items []T + head int + tail int + capacity int + size int +} +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

获取队列所有元素的切片

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

元素入队列

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Enqueue(item T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

移除队列的头元素并返回

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Dequeue() (T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Dequeue()) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

获取对列头部元素

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Front() T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

获取对列尾部元素

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Back() T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

获取队列元素的数量

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

判断对了是否为空

+ +函数签名: + +```go +func (q *ArrayQueue[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

判断对了是否为满

+ +函数签名: + +```go +func (q *ArrayQueue[T]) IsFull() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](3) + fmt.Println(q.IsFull()) // false + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsFull()) // true +} +``` + + + +### Clear +

清空队列元素

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

判断队列是否包含某个值

+ +函数签名: + +```go +func (q *ArrayQueue[T]) Contain(value T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + + +### 2. LinkedQueue +链表实现普通队列数据结构 + +### NewLinkedQueue +

返回LinkedQueue指针

+ +函数签名: + +```go +func NewLinkedQueue[T any]() *LinkedQueue[T] + +type LinkedQueue[T any] struct { + head *datastructure.QueueNode[T] + tail *datastructure.QueueNode[T] + length int +} +type QueueNode[T any] struct { + Value T + Next *QueueNode[T] +} +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int]() + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

获取队列所有元素的切片

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int]() + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

元素入队列

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Enqueue(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

移除队列的头元素并返回

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Dequeue() (T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Dequeue()) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

获取对列头部元素

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Front() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

获取对列尾部元素

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Back() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

获取队列元素的数量

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

判断对了是否为空

+ +函数签名: + +```go +func (q *LinkedQueue[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### Clear +

清空队列元素

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

判断队列是否包含某个值

+ +函数签名: + +```go +func (q *LinkedQueue[T]) Contain(value T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + + + +### 3. CircularQueue +切片实现的循环队列. + +### NewCircularQueue +

返回具有特定容量的CircularQueue指针

+ +函数签名: + +```go +func NewCircularQueue[T any](capacity int) *CircularQueue[T] + +type CircularQueue[T any] struct { + data []T + front int + rear int + capacity int +} +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

获取队列所有元素的切片

+ +函数签名: + +```go +func (q *CircularQueue[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

元素入队列

+ +函数签名: + +```go +func (q *CircularQueue[T]) Enqueue(value T) error +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

移除队列的头元素并返回

+ +函数签名: + +```go +func (q *CircularQueue[T]) Dequeue() (*T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + val := q.Dequeue() + fmt.Println(*val) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

获取对列头部元素

+ +函数签名: + +```go +func (q *CircularQueue[T]) Front() T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

获取对列尾部元素

+ +函数签名: + +```go +func (q *CircularQueue[T]) Back() T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

获取队列元素的数量

+ +函数签名: + +```go +func (q *CircularQueue[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

判断对了是否为空

+ +函数签名: + +```go +func (q *CircularQueue[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

判断对了是否为满

+ +函数签名: + +```go +func (q *CircularQueue[T]) IsFull() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](3) + fmt.Println(q.IsFull()) // false + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsFull()) // true +} +``` + + + +### Clear +

清空队列元素

+ +函数签名: + +```go +func (q *CircularQueue[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

判断队列是否包含某个值

+ +函数签名: + +```go +func (q *CircularQueue[T]) Contain(value T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + +### 4. PriorityQueue +切片实现的优先级队列。 + +### NewPriorityQueue +

返回一个具有特定容量的PriorityQueue指针,参数 `comarator` 用于比较队列中T类型的值。

+ +函数签名: + +```go +func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T] + +type PriorityQueue[T any] struct { + items []T + size int + comparator constraints.Comparator +} +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewPriorityQueue[int](3) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

获取队列所有元素的切片

+ +函数签名: + +```go +func (q *PriorityQueue[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewPriorityQueue[int](3) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

元素入队列

+ +函数签名: + +```go +func (q *PriorityQueue[T]) Enqueue(item T) bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + + fmt.Println(q.Data()) // 10, 9, 6, 7, 8, 2, 5, 1, 4, 3 +} +``` + + + + +### Dequeue +

移除队列的头元素并返回

+ +函数签名: + +```go +func (q *PriorityQueue[T]) Dequeue() (T, bool) +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + val, ok := pq.Dequeue() + fmt.Println(val) // 10 + fmt.Println(q.Data()) // 9, 8, 6, 7, 3, 2, 5, 1, 4 +} +``` + + + +### IsEmpty +

判断对了是否为空

+ +函数签名: + +```go +func (q *PriorityQueue[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsEmpty()) // true + + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

判断对了是否为满

+ +函数签名: + +```go +func (q *PriorityQueue[T]) IsFull() bool +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsFull()) // false + + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + fmt.Println(q.IsFull()) // true +} +``` + + + + +### Size +

获取队列元素的数量

+ +函数签名: + +```go +func (q *PriorityQueue[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsFull()) // false + + for i := 1; i < 5; i++ { + q.Enqueue(i) + } + fmt.Println(q.Size()) // 4 +} +``` + + diff --git a/docs/api/packages/datastructure/set.md b/docs/api/packages/datastructure/set.md new file mode 100644 index 00000000..93dcbf50 --- /dev/null +++ b/docs/api/packages/datastructure/set.md @@ -0,0 +1,709 @@ +# Set + +集合数据结构,类似列表。Set中元素不重复。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go) + +
+ +## 用法 + +```go +import ( + set "github.com/duke-git/lancet/v2/datastructure/set" +) +``` + +
+ +## 目录 + +- [New](#New) +- [FromSlice](#FromSlice) +- [Valuesdeprecated](#Values) +- [Add](#Add) +- [AddIfNotExist](#AddIfNotExist) +- [AddIfNotExistBy](#AddIfNotExistBy) +- [Delete](#Delete) +- [Contain](#Contain) +- [ContainAll](#ContainAll) +- [Clone](#Clone) +- [Size](#Size) +- [Equal](#Equal) +- [Iterate](#Iterate) +- [IsEmpty](#IsEmpty) +- [Union](#Union) +- [Intersection](#Intersection) +- [SymmetricDifference](#SymmetricDifference) +- [Minus](#Minus) +- [Pop](#Pop) +- [ToSlice](#ToSlice) +- [ToSortedSlice](#ToSortedSlice) + +
+ +## 文档 + +### New + +

返回Set结构体对象

+ +函数签名: + +```go +type Set[T comparable] map[T]struct{} +func New[T comparable](items ...T) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int](1,2,2,3) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### FromSlice + +

基于切片创建集合

+ +函数签名: + +```go +func FromSlice[T comparable](items []T) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.FromSlice([]int{1, 2, 2, 3}) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### Values + +

获取集合中所有元素的切片。

+ +> ⚠️ 本函数已弃用,使用`ToSlice`代替。 + +函数签名: + +```go +func (s Set[T]) Values() []T +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int](1,2,2,3) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### Add + +

向集合中添加元素

+ +函数签名: + +```go +func (s Set[T]) Add(items ...T) +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + fmt.Println(st.Values()) //1,2,3 +} +``` + +### AddIfNotExist + +

如果集合中不存在元素,则添加该元素返回true, 如果集合中存在元素, 不做任何操作,返回false

+ +函数签名: + +```go +func (s Set[T]) AddIfNotExist(item T) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + r1 := st.AddIfNotExist(1) + r2 := st.AddIfNotExist(4) + + fmt.Println(r1) // false + fmt.Println(r2) // true + fmt.Println(st.Values()) // 1,2,3,4 +} +``` + +### AddIfNotExistBy + +

根据checker函数判断元素是否在集合中,如果集合中不存在元素且checker返回true,则添加该元素返回true, 否则不做任何操作,返回false

+ +函数签名: + +```go +func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2) + + ok := st.AddIfNotExistBy(3, func(val int) bool { + return val%2 != 0 + }) + fmt.Println(ok) // true + + + notOk := st.AddIfNotExistBy(4, func(val int) bool { + return val%2 != 0 + }) + fmt.Println(notOk) // false + + fmt.Println(st.Values()) // 1, 2, 3 +} +``` + +### Delete + +

删除集合中元素

+ +函数签名: + +```go +func (s Set[T]) Delete(items ...T) +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + set.Delete(3) + fmt.Println(st.Values()) //1,2 +} +``` + +### Contain + +

判断集合是否包含某个值

+ +函数签名: + +```go +func (s Set[T]) Contain(item T) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + fmt.Println(st.Contain(1)) //true + fmt.Println(st.Contain(4)) //false +} +``` + +### ContainAll + +

判断集合是否包含另一个集合

+ +函数签名: + +```go +func (s Set[T]) ContainAll(other Set[T]) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(1, 2) + set3 := set.New(1, 2, 3, 4) + + fmt.Println(set1.ContainAll(set2)) //true + fmt.Println(set1.ContainAll(set3)) //false +} +``` + +### Size + +

获取集合中元素的个数

+ +函数签名: + +```go +func (s Set[T]) Size() int +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + + fmt.Println(set1.Size()) //3 +} +``` + +### Clone + +

克隆一个集合

+ +函数签名: + +```go +func (s Set[T]) Clone() Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set1.Clone() + + fmt.Println(set1.Size() == set2.Size()) //true + fmt.Println(set1.ContainAll(set2)) //true +} +``` + +### Equal + +

比较两个集合是否相等,包含相同元素为相等

+ +函数签名: + +```go +func (s Set[T]) Equal(other Set[T]) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(1, 2, 3) + set3 := set.New(1, 2, 3, 4) + + fmt.Println(set1.Equal(set2)) //true + fmt.Println(set1.Equal(set3)) //false +} +``` + +### Iterate + +

迭代结合,在每个元素上调用函数

+ +函数签名: + +```go +func (s Set[T]) Iterate(fn func(item T)) +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + arr := []int{} + set.Iterate(func(item int) { + arr = append(arr, item) + }) + + fmt.Println(arr) //1,2,3 +} +``` + +### EachWithBreak + +

遍历集合的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。

+ +函数签名: + +```go +func (s Set[T]) EachWithBreak(iteratee func(item T) bool) +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + s := set.New(1, 2, 3, 4, 5) + + var sum int + + s.EachWithBreak(func(n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + fmt.Println(sum) //6 +} +``` + +### IsEmpty + +

判断集合是否为空

+ +函数签名: + +```go +func (s Set[T]) IsEmpty() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New() + + fmt.Println(set1.IsEmpty()) //false + fmt.Println(set2.IsEmpty()) //true +} +``` + +### Union + +

求两个集合的并集

+ +函数签名: + +```go +func (s Set[T]) Union(other Set[T]) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.Union(set2) + + fmt.Println(set3.Values()) //1,2,3,4,5 +} +``` + +### Intersection + +

求两个集合的交集

+ +函数签名: + +```go +func (s Set[T]) Intersection(other Set[T]) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.Intersection(set2) + + fmt.Println(set3.Values()) //2,3 +} +``` + +### SymmetricDifference + +

返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中

+ +函数签名: + +```go +func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.SymmetricDifference(set2) + + fmt.Println(set3.Values()) //1,4,5 +} +``` + +### Minus + +

创建一个集合,其元素在原始集中但不在比较集中

+ +函数签名: + +```go +func (s Set[T]) Minus(comparedSet Set[T]) Set[T] +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set.New(2, 3) + + res1 := set1.Minus(set2) + fmt.Println(res1.Values()) //1 + + res2 := set2.Minus(set3) + fmt.Println(res2.Values()) //4,5 +} +``` + +### Pop + +

删除并返回集合中的顶部元素

+ +函数签名: + +```go +func (s Set[T]) Pop() (v T, ok bool) +``` + +示例: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + s := set.New[int]() + s.Add(1) + s.Add(2) + s.Add(3) + + val, ok = s.Pop() + + fmt.Println(val) // 3 + fmt.Println(ok) // true +} +``` + +### ToSlice + +

以切片的形式返回集合中所有的元素(无序)

+ +函数签名: + +```go +func (s Set[T]) ToSlice() (v T, ok bool) +``` + +示例: + +```go +func main() { + s := set.New(1, 2, 3, 4, 5) + + val := s.ToSlice() + fmt.Println(val) // [2 3 4 5 1] +} +``` + +### ToSortedSlice + +

以切片的形式返回集合中所有的元素(按给定的规则排序)

+ +函数签名: + +```go +func (s Set[T]) ToSortedSlice() (v T, ok bool) +``` + +示例: + +```go +func main() { + s1 := set.New(1, 2, 3, 4, 5) + type Person struct { + Name string + Age int + } + s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}}) + + res1 := s1.ToSortedSlice(func(v1, v2 int) bool { + return v1 < v2 + }) + + res2 := s2.ToSortedSlice(func(v1, v2 Person) bool { + return v1.Age < v2.Age + }) + + fmt.Println(res1) // [1 2 3 4 5] + fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}] +} +``` diff --git a/docs/api/packages/datastructure/stack.md b/docs/api/packages/datastructure/stack.md new file mode 100644 index 00000000..3f044e4d --- /dev/null +++ b/docs/api/packages/datastructure/stack.md @@ -0,0 +1,611 @@ +# Stack +栈数据结构,包括ArrayStack(数组栈)和LinkedStack(链表栈)。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go) + + +
+ +## 用法 +```go +import ( + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) +``` + +
+ +## 目录 + +### 1. ArrayStack(数组栈) + +- [NewArrayStack](#NewArrayStack) +- [Push](#ArrayStack_Push) +- [Pop](#ArrayStack_Pop) +- [Peak](#ArrayStack_Peak) +- [Data](#ArrayStack_Data) +- [Size](#ArrayStack_Size) +- [IsEmpty](#ArrayStack_IsEmpty) +- [Clear](#ArrayStack_Clear) + +### 2. LinkedStack(链表栈) + +- [NewLinkedStack](#NewLinkedStack) +- [Push](#LinkedStack_Push) +- [Pop](#LinkedStack_Pop) +- [Peak](#LinkedStack_Peak) +- [Data](#LinkedStack_Data) +- [Size](#LinkedStack_Size) +- [IsEmpty](#LinkedStack_IsEmpty) +- [Clear](#LinkedStack_Clear) +- [Print](#LinkedStack_Print) + +
+ +## 文档 + +### 1. ArrayStack +用切片实现栈结构 + +### NewArrayStack +

返回ArrayStack指针实例

+ +函数签名: + +```go +type ArrayStack[T any] struct { + data []T + length int +} +func NewArrayStack[T any]() *ArrayStack[T] +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + fmt.Println(sk) +} +``` + + + + +### Push +

将元素加入数组栈

+ +函数签名: + +```go +func (s *ArrayStack[T]) Push(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Pop +

删除栈顶元素并返回该元素指针

+ +函数签名: + +```go +func (s *ArrayStack[T]) Pop() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Pop() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{2, 1} +} +``` + + + + +### Peak +

返回栈顶元素指针

+ +函数签名: + +```go +func (s *ArrayStack[T]) Peak() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Peak() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Data +

返回栈中所有元素组成的切片

+ +函数签名: + +```go +func (s *ArrayStack[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Size +

返回栈中元素的数量

+ +函数签名: + +```go +func (s *ArrayStack[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Size()) //3 +} +``` + + + + +### IsEmpty +

判断栈是否为空

+ +函数签名: + +```go +func (s *ArrayStack[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + fmt.Println(sk.IsEmpty()) //true + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.IsEmpty()) //false +} +``` + + + + +### Clear +

清空栈元素,使栈为空

+ +函数签名: + +```go +func (s *ArrayStack[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + sk.Clear() + + fmt.Println(sk.Data()) //[]int{} +} +``` + + + +### 2. LinkedStack +链表实现的栈结构。 + +### NewLinkedStack +

返回LinkedStack指针实例

+ +函数签名: + +```go +type StackNode[T any] struct { + Value T + Next *StackNode[T] +} +type LinkedStack[T any] struct { + top *datastructure.StackNode[T] + length int +} +func NewLinkedStack[T any]() *LinkedStack[T] +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + fmt.Println(sk) +} +``` + + + + +### Push +

将元素加入链表栈

+ +函数签名: + +```go +func (s *LinkedStack[T]) Push(value T) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Pop +

删除栈顶元素并返回该元素指针

+ +函数签名: + +```go +func (s *LinkedStack[T]) Pop() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Pop() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{2, 1} +} +``` + + + + +### Peak +

返回栈顶元素指针

+ +函数签名: + +```go +func (s *LinkedStack[T]) Peak() (*T, error) +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Peak() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Data +

返回栈中所有元素组成的切片

+ +函数签名: + +```go +func (s *LinkedStack[T]) Data() []T +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Size +

返回栈中元素的数量

+ +函数签名: + +```go +func (s *LinkedStack[T]) Size() int +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Size()) //3 +} +``` + + + + +### IsEmpty +

判断栈是否为空

+ +函数签名: + +```go +func (s *LinkedStack[T]) IsEmpty() bool +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + fmt.Println(sk.IsEmpty()) //true + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.IsEmpty()) //false +} +``` + + + + +### Clear +

清空栈元素,使栈为空

+ +函数签名: + +```go +func (s *LinkedStack[T]) Clear() +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + sk.Clear() + + fmt.Println(sk.Data()) //[]int{} +} +``` + + + + +### Print +

打印链表栈结构

+ +函数签名: + +```go +func (s *LinkedStack[T]) Print() +``` +示例: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + + sk.Print() //[ &{Value:3 Next:0xc000010260}, &{Value:2 Next:0xc000010250}, &{Value:1 Next:}, ] +} +``` diff --git a/docs/api/packages/datastructure/tree.md b/docs/api/packages/datastructure/tree.md new file mode 100644 index 00000000..d01997f3 --- /dev/null +++ b/docs/api/packages/datastructure/tree.md @@ -0,0 +1,526 @@ +# Tree +树是树节点的集合。 每个树节点都有一个值,一个指向左节点的左指针和一个指向右节点的右指针。 + +
+ +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go](https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go) + + +
+ +## 用法 +```go +import ( + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) +``` + +
+ +## 目录 + +### 1. BSTree + +- [NewBSTree](#NewBSTree) +- [Insert](#BSTree_Insert) +- [Delete](#BSTree_Delete) +- [PreOrderTraverse](#BSTree_PreOrderTraverse) +- [InOrderTraverse](#BSTree_InOrderTraverse) +- [PostOrderTraverse](#BSTree_PostOrderTraverse) +- [LevelOrderTraverse](#BSTree_LevelOrderTraverse) +- [Depth](#BSTree_Depth) +- [HasSubTree](#BSTree_HasSubTree) +- [Print](#BSTree_Print) + + + +
+ +## 文档 + +## 1. BSTree +BSTree是一种二叉搜索树数据结构,其中每个节点有两个孩子,分别称为左孩子和右孩子。 在 BSTree 中:leftNode < rootNode < rightNode。 T类型应该实现constraints.Comparator。 + +### NewBSTree +

返回BSTree指针实例

+ +函数签名: + +```go +func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T] + +type BSTree[T any] struct { + root *datastructure.TreeNode[T] + comparator constraints.Comparator +} + +type TreeNode[T any] struct { + Value T + Left *TreeNode[T] + Right *TreeNode[T] +} +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + fmt.Println(bstree) // +} +``` + + + + +### Insert +

将值插入二叉搜索树

+ +函数签名: + +```go +func (t *BSTree[T]) Insert(data T) +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7 +} +``` + + + + +### Delete +

删除插入二叉搜索树中指定的值

+ +函数签名: + +```go +func (t *BSTree[T]) Delete(data T) +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + bstree.Delete(4) + + fmt.Println(bstree.PreOrderTraverse()) //2, 5, 6, 7 +} +``` + + + + +### PreOrderTraverse +

按前序遍历树节点

+ +函数签名: + +```go +func (t *BSTree[T]) PreOrderTraverse() []T +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7 +} +``` + + + + +### InOrderTraverse +

按中序遍历树节点

+ +函数签名: + +```go +func (t *BSTree[T]) InOrderTraverse() []T +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.InOrderTraverse()) //2, 4, 5, 6, 7 +} +``` + + + + +### PostOrderTraverse +

按后序遍历树节点

+ +函数签名: + +```go +func (t *BSTree[T]) PostOrderTraverse() []T +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PostOrderTraverse()) //5, 2, 4, 7, 6 +} +``` + + + + +### LevelOrderTraverse +

按节点层次遍历树节点

+ +函数签名: + +```go +func (t *BSTree[T]) LevelOrderTraverse() []T +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.LevelOrderTraverse()) //6, 5, 7, 2, 4 +} +``` + + + + +### Depth +

获取树的深度

+ +函数签名: + +```go +func (t *BSTree[T]) Depth() int +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.Depth()) //4 +} +``` + + + + +### HasSubTree +

判断给定树是否是子树

+ +函数签名: + +```go +func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + superTree := tree.NewBSTree(8, &intComparator{}) + superTree.Insert(4) + superTree.Insert(5) + superTree.Insert(6) + superTree.Insert(9) + superTree.Insert(4) + + subTree := tree.NewBSTree(5, &intComparator{}) + subTree.Insert(4) + subTree.Insert(6) + + fmt.Println(superTree.HasSubTree(subTree)) //true + fmt.Println(subTree.HasSubTree(superTree)) //false +} +``` + + + + +### Print +

打印树结构

+ +函数签名: + +```go +func (t *BSTree[T]) Print() +``` +示例: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.Print()) +// 6 +// / \ +// / \ +// / \ +// / \ +// 5 7 +// / +// / +// 2 +// \ +// 4 +} +``` \ No newline at end of file diff --git a/docs/api/packages/datetime.md b/docs/api/packages/datetime.md new file mode 100644 index 00000000..28c984d4 --- /dev/null +++ b/docs/api/packages/datetime.md @@ -0,0 +1,1853 @@ +# Datetime + +datetime 日期时间处理包,格式化日期,比较日期。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/datetime/datetime.go](https://github.com/duke-git/lancet/blob/main/datetime/datetime.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/datetime" +) +``` + +
+ +## 目录 + +- [AddDay](#AddDay) +- [AddWeek](#AddWeek) +- [AddMonth](#AddMonth) +- [AddHour](#AddHour) +- [AddMinute](#AddMinute) +- [AddYear](#AddYear) +- [AddDaySafe](#AddDaySafe) +- [AddMonthSafe](#AddMonthSafe) +- [AddYearSafe](#AddYearSafe) +- [BeginOfMinute](#BeginOfMinute) +- [BeginOfHour](#BeginOfHour) +- [BeginOfDay](#BeginOfDay) +- [BeginOfWeek](#BeginOfWeek) +- [BeginOfMonth](#BeginOfMonth) +- [BeginOfYear](#BeginOfYear) +- [EndOfMinute](#EndOfMinute) +- [EndOfHour](#EndOfHour) +- [EndOfDay](#EndOfDay) +- [EndOfWeek](#EndOfWeek) +- [EndOfMonth](#EndOfMonth) +- [EndOfYear](#EndOfYear) +- [GetNowDate](#GetNowDate) +- [GetNowTime](#GetNowTime) +- [GetNowDateTime](#GetNowDateTime) +- [GetTodayStartTime](#GetTodayStartTime) +- [GetTodayEndTime](#GetTodayEndTime) +- [GetZeroHourTimestamp](#GetZeroHourTimestamp) +- [GetNightTimestamp](#GetNightTimestamp) +- [FormatTimeToStr](#FormatTimeToStr) +- [FormatStrToTime](#FormatStrToTime) +- [NewUnixNow](#NewUnixNow) +- [NewUnix](#NewUnix) +- [NewFormat](#NewFormat) +- [NewISO8601](#NewISO8601) +- [ToUnix](#ToUnix) +- [ToFormat](#ToFormat) +- [ToFormatForTpl](#ToFormatForTpl) +- [ToIso8601](#ToIso8601) +- [IsLeapYear](#IsLeapYear) +- [BetweenSeconds](#BetweenSeconds) +- [DayOfYear](#DayOfYear) +- [IsWeekenddeprecated](#IsWeekend) +- [NowDateOrTime](#NowDateOrTime) +- [Timestamp](#Timestamp) +- [TimestampMilli](#TimestampMilli) +- [TimestampMicro](#TimestampMicro) +- [TimestampNano](#TimestampNano) +- [TrackFuncTime](#TrackFuncTime) +- [DaysBetween](#DaysBetween) +- [GenerateDatetimesBetween](#GenerateDatetimesBetween) +- [Min](#Min) +- [Max](#Max) +- [MaxMin](#MaxMin) + +
+ +## 文档 + +## 注: + +1. 函数中`format`参数值需要传以下值之一 (忽略大小写): + +- yyyy-mm-dd hh:mm:ss +- yyyy-mm-dd hh:mm +- yyyy-mm-dd hh +- yyyy-mm-dd +- yyyy-mm +- mm-dd +- dd-mm-yy hh:mm:ss +- yyyy/mm/dd hh:mm:ss +- yyyy/mm/dd hh:mm +- yyyy/mm/dd hh +- yyyy/mm/dd +- yyyy/mm +- mm/dd +- dd/mm/yy hh:mm:ss +- yyyymmdd +- mmddyy +- yyyy +- yy +- mm +- hh:mm:ss +- hh:mm +- mm:ss + +### AddDay + +

将日期加/减天数。

+ +函数签名: + +```go +func AddDay(t time.Time, days int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/dIGbs_uTdFa) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after1Day := datetime.AddDay(date, 1) + before1Day := datetime.AddDay(date, -1) + + fmt.Println(after1Day.Format("2006-01-02 15:04:05")) + fmt.Println(before1Day.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-02 00:00:00 + // 2020-12-31 00:00:00 +} +``` + +### AddWeek + +

将日期加/减星期数。

+ +函数签名: + +```go +func AddWeek(t time.Time, weeks int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/M9TqdMiaA2p) + +```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 +} +``` + +### AddMonth + +

将日期加/减月数。

+ +函数签名: + +```go +func AddMonth(t time.Time, months int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/DLoiOnpLvsN) + +```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 +} +``` + +### AddHour + +

将日期加/减小时数。

+ +函数签名: + +```go +func AddHour(t time.Time, hours int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/rcMjd7OCsi5) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Hours := datetime.AddHour(date, 2) + before2Hours := datetime.AddHour(date, -2) + + fmt.Println(after2Hours.Format("2006-01-02 15:04:05")) + fmt.Println(before2Hours.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 02:00:00 + // 2020-12-31 22:00:00 +} +``` + +### AddMinute + +

将日期加/减分钟数。

+ +函数签名: + +```go +func AddMinute(t time.Time, minutes int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/nT1heB1KUUK) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Minutes := datetime.AddMinute(date, 2) + before2Minutes := datetime.AddMinute(date, -2) + + fmt.Println(after2Minutes.Format("2006-01-02 15:04:05")) + fmt.Println(before2Minutes.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 00:02:00 + // 2020-12-31 23:58:00 +} +``` + +### AddYear + +

将日期加/减年数。

+ +函数签名: + +```go +func AddYear(t time.Time, years int64) time.Time +``` + +示例:[运行](https://go.dev/play/p/MqW2ujnBx10) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02", "2021-01-01") + + after2Years := AddYear(date, 2) + before2Years := AddYear(date, -2) + + fmt.Println(after2Years.Format("2006-01-02")) + fmt.Println(before2Years.Format("2006-01-02")) + + // Output: + // 2023-01-01 + // 2019-01-01 +} +``` + +### AddDaySafe + +

增加/减少指定的天数,并确保日期是有效日期。

+ +函数签名: + +```go +func AddDaySafe(t time.Time, days int) time.Time +``` + +示例:[运行](https://go.dev/play/p/JTohZFpoDJ3) + +```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 +} +``` + +### AddMonthSafe + +

增加/减少指定的月份,并确保日期是有效日期。

+ +函数签名: + +```go +func AddMonthSafe(t time.Time, months int) time.Time +``` + +示例:[运行](https://go.dev/play/p/KLw0lo6mbVW) + +```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 +} +``` + +### AddYearSafe + +

增加/减少指定的年份,并确保日期是有效日期。

+ +函数签名: + +```go +func AddYearSafe(t time.Time, years int) time.Time +``` + +示例:[运行](https://go.dev/play/p/KVGXWZZ54ZH) + +```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 +} +``` + +### BeginOfMinute + +

返回指定时间的分钟开始时间。

+ +函数签名: + +```go +func BeginOfMinute(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/ieOLVJ9CiFT) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:00 +0000 UTC +} +``` + +### BeginOfHour + +

返回指定时间的小时开始时间。

+ +函数签名: + +```go +func BeginOfHour(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/GhdGFnDWpYs) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:00:00 +0000 UTC +} +``` + +### BeginOfDay + +

返回指定时间的当天开始时间。

+ +函数签名: + +```go +func BeginOfDay(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/94m_UT6cWs9) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 00:00:00 +0000 UTC +} +``` + +### BeginOfWeek + +

返回指定时间的每周开始时间,默认开始时间星期日。

+ +函数签名: + +```go +func BeginOfWeek(t time.Time, beginFrom time.Weekday) time.Time +``` + +示例:[运行](https://go.dev/play/p/ynjoJPz7VNV) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfWeek(input, time.Monday) + + fmt.Println(result) + + // Output: + // 2023-01-09 00:00:00 +0000 UTC +} +``` + +### BeginOfMonth + +

返回指定时间的当月开始时间。

+ +函数签名: + +```go +func BeginOfMonth(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/bWXVFsmmzwL) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} +``` + +### BeginOfYear + +

返回指定时间的当年开始时间

+ +函数签名: + +```go +func BeginOfYear(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/i326DSwLnV8) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} +``` + +### EndOfMinute + +

返回指定时间的分钟结束时间。

+ +函数签名: + +```go +func EndOfMinute(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/yrL5wGzPj4z) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:59.999999999 +0000 UTC +} +``` + +### EndOfHour + +

返回指定时间的小时结束时间。

+ +函数签名: + +```go +func EndOfHour(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/6ce3j_6cVqN) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:59:59.999999999 +0000 UTC +} +``` + +### EndOfDay + +

返回指定时间的当天结束时间。

+ +函数签名: + +```go +func EndOfDay(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/eMBOvmq5Ih1) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-02 00:00:00 +0000 UTC +} +``` + +### EndOfWeek + +

返回指定时间的星期结束时间,默认结束时间星期六。

+ +函数签名: + +```go +func EndOfWeek(t time.Time, endWith time.Weekday) time.Time +``` + +示例:[运行](https://go.dev/play/p/mGSA162YgX9) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfWeek(input, time.Sunday) + + fmt.Println(result) + + // Output: + // 2023-01-08 23:59:59.999999999 +0000 UTC +} +``` + +### EndOfMonth + +

返回指定时间的当月结束时间。

+ +函数签名: + +```go +func EndOfMonth(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/_GWh10B3Nqi) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-31 23:59:59.999999999 +0000 UTC +} +``` + +### EndOfYear + +

返回指定时间的当年结束时间。

+ +函数签名: + +```go +func EndOfYear(t time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/G01cKlMCvNm) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-12-31 23:59:59.999999999 +0000 UTC +} +``` + +### GetNowDate + +

获取当天日期,返回格式:yyyy-mm-dd。

+ +函数签名: + +```go +func GetNowDate() string +``` + +示例:[运行](https://go.dev/play/p/PvfkPpcpBBf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + currentDate := datetime.GetNowDate() + fmt.Println(currentDate) + + // Output: + // 2022-01-28 +} +``` + +### GetNowTime + +

获取当时时间,返回格式:hh:mm:ss

+ +函数签名: + +```go +func GetNowTime() string +``` + +示例:[运行](https://go.dev/play/p/l7BNxCkTmJS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + currentTime := datetime.GetNowTime() + fmt.Println(currentTime) + + // Output: + // 15:57:33 +} +``` + +### GetNowDateTime + +

获取当时日期和时间,返回格式:yyyy-mm-dd hh:mm:ss。

+ +函数签名: + +```go +func GetNowDateTime() string +``` + +示例:[运行](https://go.dev/play/p/pI4AqngD0al) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + current := datetime.GetNowDateTime() + fmt.Println(current) + + // Output: + // 2022-01-28 15:59:33 +} +``` + +### GetTodayStartTime + +

返回当天开始时间, 格式: yyyy-mm-dd 00:00:00。

+ +函数签名: + +```go +func GetTodayStartTime() string +``` + +示例:[运行](https://go.dev/play/p/84siyYF7t99) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + startTime := datetime.GetTodayStartTime() + fmt.Println(startTime) + + // Output: + // 2023-06-29 00:00:00 +} +``` + +### GetTodayEndTime + +

返回当天结束时间,格式: yyyy-mm-dd 23:59:59。

+ +函数签名: + +```go +func GetTodayEndTime() string +``` + +示例:[运行](https://go.dev/play/p/jjrLnfoqgn3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + endTime := datetime.GetTodayEndTime() + fmt.Println(endTime) + + // Output: + // 2023-06-29 23:59:59 +} +``` + +### GetZeroHourTimestamp + +

获取零点时间戳(timestamp of 00:00)

+ +函数签名: + +```go +func GetZeroHourTimestamp() int64 +``` + +示例:[运行](https://go.dev/play/p/QmL2oIaGE3q) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + zeroTime := datetime.GetZeroHourTimestamp() + fmt.Println(zeroTime) + + // Output: + // 1643299200 +} +``` + +### GetNightTimestamp + +

获取午夜时间戳(timestamp of 23:59)。

+ +函数签名: + +```go +func GetNightTimestamp() int64 +``` + +示例:[运行](https://go.dev/play/p/UolysR3MYP1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + nightTime := datetime.GetNightTimestamp() + fmt.Println(nightTime) + + // Output: + // 1643385599 +} +``` + +### FormatTimeToStr + +

将日期格式化成字符串,`format` 参数格式参考注1。

+ +函数签名: + +```go +func FormatTimeToStr(t time.Time, format string, timezone ...string) string +``` + +示例:[运行](https://go.dev/play/p/_Ia7M8H_OvE) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + t, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") + + result1 := datetime.FormatTimeToStr(t, "yyyy-mm-dd hh:mm:ss") + result2 := datetime.FormatTimeToStr(t, "yyyy-mm-dd") + result3 := datetime.FormatTimeToStr(t, "dd-mm-yy hh:mm:ss") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2021-01-02 16:04:08 + // 2021-01-02 + // 02-01-21 16:04:08 +} +``` + +### FormatStrToTime + +

将字符串格式化成时间,`format` 参数格式参考注1。

+ +函数签名: + +```go +func FormatStrToTime(str, format string, timezone ...string) (time.Time, error) +``` + +示例:[运行](https://go.dev/play/p/1h9FwdU8ql4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1, _ := datetime.FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss") + result2, _ := datetime.FormatStrToTime("2021-01-02", "yyyy-mm-dd") + result3, _ := datetime.FormatStrToTime("02-01-21 16:04:08", "dd-mm-yy hh:mm:ss") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2021-01-02 16:04:08 +0000 UTC + // 2021-01-02 00:00:00 +0000 UTC + // 2021-01-02 16:04:08 +0000 UTC +} +``` + +### NewUnixNow + +

创建一个当前时间的unix时间戳。

+ +函数签名: + +```go +type theTime struct { + unix int64 +} +func NewUnixNow() *theTime +``` + +示例:[运行](https://go.dev/play/p/U4PPx-9D0oz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnixNow() + fmt.Println(tm) + + // Output: + // &{1647597438} +} +``` + +### NewUnix + +

创建一个unix时间戳。

+ +函数签名: + +```go +type theTime struct { + unix int64 +} +func NewUnix(unix int64) *theTime +``` + +示例:[运行](https://go.dev/play/p/psoSuh_kLRt) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnix(1647597438) + fmt.Println(tm) + + // Output: + // &{1647597438} +} +``` + +### NewFormat + +

创建一个yyyy-mm-dd hh:mm:ss格式时间字符串的unix时间戳。

+ +函数签名: + +```go +type theTime struct { + unix int64 +} +func NewFormat(t string) (*theTime, error) +``` + +示例:[运行](https://go.dev/play/p/VkW08ZOaXPZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, err := datetime.NewFormat("2022-03-18 17:04:05") + fmt.Println(tm) + + // Output: + // &{1647594245} +} +``` + +### NewISO8601 + +

创建一个iso8601格式时间字符串的unix时间戳。

+ +函数签名: + +```go +type theTime struct { + unix int64 +} +func NewISO8601(iso8601 string) (*theTime, error) +``` + +示例:[运行](https://go.dev/play/p/mkhOHQkdeA2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, err := datetime.NewISO8601("2006-01-02T15:04:05.999Z") + fmt.Println(tm) + + // Output: + // &{1136214245} +} +``` + +### ToUnix + +

返回unix时间戳。

+ +函数签名: + +```go +func (t *theTime) ToUnix() int64 +``` + +示例:[运行](https://go.dev/play/p/_LUiwAdocjy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnixNow() + fmt.Println(tm.ToUnix()) + + // Output: + // 1647597438 +} +``` + +### ToFormat + +

返回格式'yyyy-mm-dd hh:mm:ss'的日期字符串。

+ +函数签名: + +```go +func (t *theTime) ToFormat() string +``` + +示例:[运行](https://go.dev/play/p/VkW08ZOaXPZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewFormat("2022-03-18 17:04:05") + fmt.Println(tm.ToFormat()) + + // Output: + // 2022-03-18 17:04:05 +} +``` + +### ToFormatForTpl + +

返回tpl格式指定的日期字符串。

+ +函数签名: + +```go +func (t *theTime) ToFormatForTpl(tpl string) string +``` + +示例:[运行](https://go.dev/play/p/nyXxXcQJ8L5) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewFormat("2022-03-18 17:04:05") + ts := tm.ToFormatForTpl("2006/01/02 15:04:05") + fmt.Println(ts) + + // Output: + // 2022/03/18 17:04:05 +} +``` + +### ToIso8601 + +

返回iso8601日期字符串。

+ +函数签名: + +```go +func (t *theTime) ToIso8601() string +``` + +示例:[运行](https://go.dev/play/p/mkhOHQkdeA2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewISO8601("2006-01-02T15:04:05.999Z") + ts := tm.ToIso8601() + fmt.Println(ts) + + // Output: + // 2006-01-02T23:04:05+08:00 +} +``` + +### IsLeapYear + +

验证是否是闰年。

+ +函数签名: + +```go +func IsLeapYear(year int) bool +``` + +示例:[运行](https://go.dev/play/p/xS1eS2ejGew) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1 := datetime.IsLeapYear(2000) + result2 := datetime.IsLeapYear(2001) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### BetweenSeconds + +

返回两个时间的间隔秒数。

+ +函数签名: + +```go +func BetweenSeconds(t1 time.Time, t2 time.Time) int64 +``` + +示例:[运行](https://go.dev/play/p/n3YDRyfyXJu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + today := time.Now() + tomorrow := datetime.AddDay(today, 1) + yesterday := datetime.AddDay(today, -1) + + result1 := datetime.BetweenSeconds(today, tomorrow) + result2 := datetime.BetweenSeconds(today, yesterday) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 86400 + // -86400 +} +``` + +### DayOfYear + +

返回参数日期是一年中的第几天。

+ +函数签名: + +```go +func DayOfYear(t time.Time) int +``` + +示例:[运行](https://go.dev/play/p/0hjqhTwFNlH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local) + result1 := datetime.DayOfYear(date1) + + date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local) + result2 := datetime.DayOfYear(date2) + + date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local) + result3 := datetime.DayOfYear(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 31 + // 1 + // 0 +} +``` + +### IsWeekend(已废弃, 使用 '== Weekday') + +

判断日期是否是周末。

+ +函数签名: + +```go +func IsWeekend(t time.Time) bool +``` + +示例:[运行](https://go.dev/play/p/cupRM5aZOIY) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date1 := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local) + date2 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local) + date3 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local) + + result1 := datetime.IsWeekend(date1) + result2 := datetime.IsWeekend(date2) + result3 := datetime.IsWeekend(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### NowDateOrTime + +

根据指定的格式和时区返回当前时间字符串。

+ +函数签名: + +```go +func NowDateOrTime(format string, timezone ...string) string +``` + +示例:[运行](https://go.dev/play/p/EZ-begEjtT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss") + + result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 2023-07-26 15:01:30 + // 2023-07-26 02:01:30 +} +``` + +### Timestamp + +

返回当前秒级时间戳。

+ +函数签名: + +```go +func Timestamp(timezone ...string) int64 +``` + +示例:[运行](https://go.dev/play/p/iU5b7Vvjx6x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.Timestamp() + + fmt.Println(ts) + + // Output: + // 1690363051 +} +``` + + +### TimestampMilli + +

返回当前毫秒级时间戳。

+ +函数签名: + +```go +func TimestampMilli(timezone ...string) int64 +``` + +示例:[运行](https://go.dev/play/p/4gvEusOTu1T) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampMilli() + + fmt.Println(ts) + + // Output: + // 1690363051331 +} +``` + +### TimestampMicro + +

返回当前微秒级时间戳。

+ +函数签名: + +```go +func TimestampMicro(timezone ...string) int64 +``` + +示例:[运行](https://go.dev/play/p/2maANglKHQE) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampMicro() + + fmt.Println(ts) + + // Output: + // 1690363051331784 +} +``` + +### TimestampNano + +

返回当前纳秒级时间戳。

+ +函数签名: + +```go +func TimestampNano(timezone ...string) int64 +``` + +示例:[运行](https://go.dev/play/p/A9Oq_COrcCF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampNano() + + fmt.Println(ts) + + // Output: + // 1690363051331788000 +} +``` + +### TrackFuncTime + +

测试函数执行时间。

+ +函数签名: + +```go +func TrackFuncTime(pre time.Time) func() +``` + +示例:[运行](https://go.dev/play/p/QBSEdfXHPTp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + defer datetime.TrackFuncTime(time.Now())() + + var n int + for i := 0; i < 5000000; i++ { + n++ + } + + fmt.Println(1) // Function main execution time: 1.460287ms +} +``` + +### DaysBetween + +

返回两个日期之间的天数差。

+ +函数签名: + +```go +func DaysBetween(start, end time.Time) int +``` + +示例:[运行](https://go.dev/play/p/qD6qGb3TbOy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC) + + result := datetime.DaysBetween(start, end) + + fmt.Println(result) + + // Output: + // 9 +} +``` + +### GenerateDatetimesBetween + +

生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式,例如"2006-01-02 15:04:05",interval参数表示时间间隔,例如"1h"表示1小时,"30m"表示30分钟。

+ +函数签名: + +```go +func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) +``` + +示例:[运行](https://go.dev/play/p/6kHBpAxD9ZC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC) + + layout := "2006-01-02 15:04:05" + interval := "1h" + + result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval) + + fmt.Println(result) + fmt.Println(err) + + // Output: + // [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00] + // +} +``` + +### Min + +

返回最早时间。

+ +函数签名: + +```go +func Min(t1 time.Time, times ...time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/MCIDvHNOGGb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + minTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(minTime) + + // Output: + // 2024-09-01 00:00:00 +0000 UTC +} +``` + +### Max + +

返回最晚时间。

+ +函数签名: + +```go +func Max(t1 time.Time, times ...time.Time) time.Time +``` + +示例:[运行](https://go.dev/play/p/9m6JMk1LB7-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + maxTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(maxTime) + + // Output: + // 2024-09-02 00:00:00 +0000 UTC +} +``` + +### MaxMin + +

返回最早和最晚时间。

+ +函数签名: + +```go +func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) +``` + +示例:[运行](https://go.dev/play/p/rbW51cDtM_2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + max, min := datetime.MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC)) + + fmt.Println(max) + fmt.Println(min) + + // Output: + // 2024-09-03 00:00:00 +0000 UTC + // 2024-09-01 00:00:00 +0000 UTC +} +``` \ No newline at end of file diff --git a/docs/api/packages/eventbus.md b/docs/api/packages/eventbus.md new file mode 100644 index 00000000..9d26304f --- /dev/null +++ b/docs/api/packages/eventbus.md @@ -0,0 +1,407 @@ +# EventBus + +EventbBus是一个事件总线,用于在应用程序中处理事件。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go](https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/eventbus" +) +``` + +
+ +## 目录 + +- [NewEventBus](#NewEventBus) +- [Subscribe](#Subscribe) +- [Unsubscribe](#Unsubscribe) +- [Publish](#Publish) +- [ClearListeners](#ClearListeners) +- [ClearListenersByTopic](#ClearListenersByTopic) +- [GetListenersCount](#GetListenersCount) +- [GetAllListenersCount](#GetAllListenersCount) +- [GetEvents](#GetEvents) +- [SetErrorHandler](#SetErrorHandler) + + +
+ +## 文档 + +### NewEventBus + +

创建EventBus实例。

+ +函数签名: + +```go +func NewEventBus[T any]() *EventBus[T] +``` + +示例:[运行](https://go.dev/play/p/gHbOPV_NUOJ) + +```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 +} +``` + +### Subscribe + +

订阅具有特定事件主题和监听函数的事件。支持异步,事件优先级,事件过滤器。

+ +函数签名: + +```go +func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool) +``` + +示例:[运行](https://go.dev/play/p/EYGf_8cHei-) + +```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 +} +``` + +### Unsubscribe + +

取消订阅具有特定事件主题的事件。

+ +函数签名: + +```go +func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T)) +``` + +示例:[运行](https://go.dev/play/p/Tmh7Ttfvprf) + +```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 +} +``` + +### Publish + +

发布一个带有特定事件主题和数据负载的事件。

+ +函数签名: + +```go +func (eb *EventBus[T]) Publish(event eventbus.Event[T]) +``` + +示例:[运行](https://go.dev/play/p/gHTtVexFSH9) + +```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 +} +``` + +### ClearListeners + +

清空所有事件监听器。

+ +函数签名: + +```go +func (eb *EventBus[T]) ClearListeners() +``` + +示例:[运行](https://go.dev/play/p/KBfBYlKPgqD) + +```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 +} +``` + +### ClearListenersByTopic + +

清空特定事件监听器。

+ +函数签名: + +```go +func (eb *EventBus[T]) ClearListenersByTopic(topic string) +``` + +示例:[运行](https://go.dev/play/p/gvMljmJOZmU) + +```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 +} +``` + +### GetListenersCount + +

获取特定事件的监听器数量。

+ +函数签名: + +```go +func (eb *EventBus[T]) GetListenersCount(topic string) int +``` + +示例:[运行](https://go.dev/play/p/j6yaJ2xAmFz) + +```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 +} +``` + +### GetAllListenersCount + +

获取所有事件的监听器数量。

+ +函数签名: + +```go +func (eb *EventBus[T]) GetAllListenersCount() int +``` + +示例:[运行](https://go.dev/play/p/PUlr0xcpEOz) + +```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 +} +``` + +### GetEvents + +

获取所有事件的topic。

+ +函数签名: + +```go +func (eb *EventBus[T]) GetEvents() []string +``` + +示例:[运行](https://go.dev/play/p/etgjjcOtAjX) + +```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 +} +``` + +### SetErrorHandler + +

设置事件的错误处理函数。

+ +函数签名: + +```go +func (eb *EventBus[T]) SetErrorHandler(handler func(err error)) +``` + +示例:[运行](https://go.dev/play/p/gmB0gnFe5mc) + +```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 +} +``` \ No newline at end of file diff --git a/docs/api/packages/fileutil.md b/docs/api/packages/fileutil.md new file mode 100644 index 00000000..8cee0bec --- /dev/null +++ b/docs/api/packages/fileutil.md @@ -0,0 +1,1147 @@ +# Fileutil + +fileutil 包支持文件基本操作。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/fileutil/file.go](https://github.com/duke-git/lancet/blob/main/fileutil/file.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/fileutil" +) +``` + +
+ +## 目录 + +- [ClearFile](#ClearFile) +- [CreateFile](#CreateFile) +- [CreateDir](#CreateDir) +- [CopyFile](#CopyFile) +- [CopyDir](#CopyDir) +- [CurrentPath](#CurrentPath) +- [FileMode](#FileMode) +- [MiMeType](#MiMeType) +- [IsExist](#IsExist) +- [IsLink](#IsLink) +- [IsDir](#IsDir) +- [ListFileNames](#ListFileNames) +- [RemoveFile](#RemoveFile) +- [RemoveDir](#RemoveDir) +- [ReadFileToString](#ReadFileToString) +- [ReadFileByLine](#ReadFileByLine) +- [Zip](#Zip) +- [ZipAppendEntry](#ZipAppendEntry) +- [UnZip](#UnZip) +- [IsZipFile](#IsZipFile) +- [FileSize](#FileSize) +- [MTime](#MTime) +- [Sha](#Sha) +- [ReadCsvFile](#ReadCsvFile) +- [WriteCsvFile](#WriteCsvFile) +- [WriteMapsToCsv](#WriteMapsToCsv) +- [WriteStringToFile](#WriteStringToFile) +- [WriteBytesToFile](#WriteBytesToFile) +- [ReadFile](#ReadFile) +- [ChunkRead](#ChunkRead) +- [ParallelChunkRead](#ParallelChunkRead) +- [GetExeOrDllVersion](#GetExeOrDllVersion) + +
+ +## 文档 + +### ClearFile + +

清空文件内容

+ +函数签名: + +```go +func ClearFile(path string) error +``` + +示例:[运行](https://go.dev/play/p/NRZ0ZT-G94H) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.ClearFile("./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### CreateFile + +

创建文件,创建成功返回true, 否则返回false

+ +函数签名: + +```go +func CreateFile(path string) bool +``` + +示例:[运行](https://go.dev/play/p/lDt8PEsTNKI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isCreatedSucceed := fileutil.CreateFile("./test.txt") + fmt.Println(isCreatedSucceed) +} +``` + +### CreateDir + +

使用绝对路径创建嵌套目录,例如/a/, /a/b

+ +函数签名: + +```go +func CreateDir(absPath string) error +``` + +示例:[运行](https://go.dev/play/p/qUuCe1OGQnM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CreateDir("/a/b") // will create folder /a/b + fmt.Println(err) +} +``` + +### CopyFile + +

拷贝文件,会覆盖原有的文件

+ +函数签名: + +```go +func CopyFile(srcPath string, dstPath string) error +``` + +示例:[运行](https://go.dev/play/p/Jg9AMJMLrJi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CopyFile("./test.txt", "./test_copy.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### CopyDir + +

拷贝文件夹到目标路径,会递归复制文件夹下所有的文件及文件夹,并且访问权限也与源文件夹保持一致。当dstPath存在时会返回error

+ +函数签名: + +```go +func CopyDir(srcPath string, dstPath string) error +``` + +示例:[运行](https://go.dev/play/p/YAyFTA_UuPb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CopyFile("./test_src", "./test_dest") + if err != nil { + fmt.Println(err) + } +} +``` + +### CurrentPath + +

返回当前位置的绝对路径。

+ +函数签名: + +```go +func CurrentPath() string +``` + +示例:[运行](https://go.dev/play/p/s74a9iBGcSw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + absPath := CurrentPath() + fmt.Println(absPath) +} +``` + +### FileMode + +

获取文件mode信息

+ +函数签名: + +```go +func FileMode(path string) (fs.FileMode, error) +``` + +示例:[运行](https://go.dev/play/p/2l2hI42fA3p) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + mode, err := fileutil.FileMode("./test.txt") + if err != nil { + fmt.Println(err) + } + fmt.Println(mode) +} +``` + +### MiMeType + +

获取文件mime类型, 'file'参数的类型必须是string或者*os.File

+ +函数签名: + +```go +func MiMeType(file any) string +``` + +示例:[运行](https://go.dev/play/p/bd5sevSUZNu) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + type1 := fileutil.MiMeType("./test.txt") + fmt.Println(type1) //text/plain; charset=utf-8 + + f, _ := os.Open("./file.go") + type2 := fileutil.MiMeType(f) + fmt.Println(type2) //text/plain; charset=utf-8 +} +``` + +### IsExist + +

判断文件或目录是否存在

+ +函数签名: + +```go +func IsExist(path string) bool +``` + +示例:[运行](https://go.dev/play/p/nKKXt8ZQbmh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fileutil.CreateFile("./test.txt") + isFileExist := fileutil.IsExist("./test.txt") + fmt.Println(isFileExist) //true +} +``` + +### IsLink + +

判断文件是否是符号链接

+ +函数签名: + +```go +func IsLink(path string) bool +``` + +示例:[运行](https://go.dev/play/p/TL-b-Kzvf44) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isLinkFile := fileutil.IsLink("./test.txt") + fmt.Println(isLinkFile) //false +} +``` + +### IsDir + +

判断参数是否是目录

+ +函数签名: + +```go +func IsDir(path string) bool +``` + +示例:[运行](https://go.dev/play/p/WkVwEKqtOWk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isDir := fileutil.IsDir("./") + fmt.Println(isDir) //true + + isDir = fileutil.IsDir("./test.txt") + fmt.Println(isDir) //false +} +``` + +### ListFileNames + +

返回目录下所有文件名

+ +函数签名: + +```go +func ListFileNames(path string) ([]string, error) +``` + +示例:[运行](https://go.dev/play/p/Tjd7Y07rejl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fileNames, _ := fileutil.ListFileNames("./") + fmt.Println(fileNames) +} +``` + +### RemoveFile + +

删除文件,支持传入删除前的回调函数。

+ +函数签名: + +```go +func RemoveFile(path string, onDelete ...func(path string)) error +``` + +示例:[运行](https://go.dev/play/p/P2y0XW8a1SH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.RemoveFile("./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### RemoveDir + +

删除目录,支持传入删除前的回调函数。

+ +函数签名: + +```go +func RemoveDir(path string, onDelete ...func(path string)) error +``` + +示例:[运行](https://go.dev/play/p/Oa6KnPek2uy) + +```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) +} +``` + +### ReadFileToString + +

读取文件内容并返回字符串

+ +函数签名: + +```go +func ReadFileToString(path string) (string, error) +``` + +示例:[运行](https://go.dev/play/p/cmfwp_5SQTp) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + path := "./test.txt" + fileutil.CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + f.WriteString("hello world") + + content, _ := fileutil.ReadFileToString(path) + fmt.Println(content) //hello world +} +``` + +### ReadFileByLine + +

按行读取文件内容,返回字符串切片包含每一行

+ +函数签名: + +```go +func ReadFileByLine(path string)([]string, error) +``` + +示例:[运行](https://go.dev/play/p/svJP_7ZrBrD) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + path := "./text.txt" + fileutil.CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + f.WriteString("hello\nworld") + + contents, _ := fileutil.ReadFileByLine(path) + fmt.Println(contents) //[]string{"hello", "world"} +} +``` + +### Zip + +

zip压缩文件, fpath参数可以是文件或目录

+ +函数签名: + +```go +func Zip(fpath string, destPath string) error +``` + +示例:[运行](https://go.dev/play/p/j-3sWBp8ik_P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.Zip("./test.txt", "./test.zip") + if err != nil { + fmt.Println(err) + } +} +``` + +### ZipAppendEntry + +

通过将单个文件或目录追加到现有的zip文件

+ +函数签名: + +```go +func ZipAppendEntry(fpath string, destPath string) error +``` + +示例:[运行](https://go.dev/play/p/cxvaT8TRNQp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.ZipAppendEntry("./test.txt", "./test.zip") + if err != nil { + fmt.Println(err) + } +} +``` + +### UnZip + +

zip解压缩文件并保存在目录中

+ +函数签名: + +```go +func UnZip(zipFile string, destPath string) error +``` + +示例:[运行](https://go.dev/play/p/g0w34kS7B8m) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.UnZip("./test.zip", "./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### IsZipFile + +

判断文件是否是zip压缩文件。

+ +函数签名: + +```go +func IsZipFile(filepath string) bool +``` + +示例:[运行](https://go.dev/play/p/9M0g2j_uF_e) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isZip := fileutil.IsZipFile("./zipfile.zip") + fmt.Println(isZip) +} +``` + +### FileSize + +

返回文件字节大小。

+ +函数签名: + +```go +func FileSize(path string) (int64, error) +``` + +示例:[运行](https://go.dev/play/p/H9Z05uD-Jjc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + size, err := fileutil.FileSize("./testdata/test.txt") + + fmt.Println(size) + fmt.Println(err) + + // Output: + // 20 + // +} +``` + +### MTime + +

返回文件修改时间(unix timestamp).

+ +函数签名: + +```go +func MTime(filepath string) (int64, error) +``` + +示例:[运行](https://go.dev/play/p/s_Tl7lZoAaY) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + mtime, err := fileutil.MTime("./testdata/test.txt") + + fmt.Println(mtime) + fmt.Println(err) + + // Output: + // 1682391110 + // +} +``` + +### Sha + +

返回文件sha值,参数`shaType` 应传值为: 1, 256,512.

+ +函数签名: + +```go +func Sha(filepath string, shaType ...int) (string, error) +``` + +示例:[运行](https://go.dev/play/p/VfEEcO2MJYf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + sha1, err := fileutil.Sha("./testdata/test.txt", 1) + sha256, _ := fileutil.Sha("./testdata/test.txt", 256) + sha512, _ := fileutil.Sha("./testdata/test.txt", 512) + + fmt.Println(sha1) + fmt.Println(sha256) + fmt.Println(sha512) + fmt.Println(err) + + // Output: + // dda3cf10c5a6ff6c6659a497bf7261b287af2bc7 + // aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35 + // d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870 + // +} +``` + +### ReadCsvFile + +

读取csv文件内容到切片

+ +函数签名: + +```go +func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) +``` + +示例:[运行](https://go.dev/play/p/OExTkhGEd3_u) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + content, err := fileutil.ReadCsvFile("./testdata/test.csv") + + fmt.Println(content) + fmt.Println(err) + + // Output: + // [[Bob 12 male] [Duke 14 male] [Lucy 16 female]] + // +} +``` + +### WriteCsvFile + +

向csv文件写入内容。

+ +函数签名: + +```go +func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error +``` + +示例:[运行](https://go.dev/play/p/dAXm58Q5U1o) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fpath := "./test.csv" + fileutil.CreateFile(fpath) + + f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + data := [][]string{ + {"Lili", "22", "female"}, + {"Jim", "21", "male"}, + } + err := fileutil.WriteCsvFile(fpath, data, false) + + if err != nil { + return + } + + content, err := fileutil.ReadCsvFile(fpath) + + if err != nil { + return + } + fmt.Println(content) + + // Output: + // [[Lili 22 female] [Jim 21 male]] +} +``` + +### WriteMapsToCsv + +

将map切片写入csv文件中。

+ +函数签名: + +```go +// filepath: CSV文件路径。 +// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。 +// appendToExistingFile: 是否为追加写模式。 +// delimiter: CSV文件分割符。 +// headers: CSV文件表头顺序(需要与map key保持一致),不指定时按字母排序。 +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error +``` + +示例:[运行](https://go.dev/play/p/umAIomZFV1c) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fpath := "./test.csv" + fileutil.CreateFile(fpath) + + f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + records := []map[string]any{ + {"Name": "Lili", "Age": "22", "Gender": "female"}, + {"Name": "Jim", "Age": "21", "Gender": "male"}, + } + + headers := []string{"Name", "Age", "Gender"} + err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers) + + if err != nil { + log.Fatal(err) + } + + content, err := fileutil.ReadCsvFile(csvFilePath, ';') + + fmt.Println(content) + + // Output: + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] +} +``` + +### WriteBytesToFile + +

将bytes写入文件。

+ +函数签名: + +```go +func WriteBytesToFile(filepath string, content []byte) error +``` + +示例:[运行](https://go.dev/play/p/s7QlDxMj3P8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + filepath := "./bytes.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = fileutil.WriteBytesToFile(filepath, []byte("hello")) + if err != nil { + return + } + + content, err := fileutil.ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} +``` + +### WriteStringToFile + +

将字符串写入文件。

+ +函数签名: + +```go +func WriteStringToFile(filepath string, content string, append bool) error +``` + +示例:[运行](https://go.dev/play/p/GhLS6d8lH_g) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + filepath := "./test.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = fileutil.WriteStringToFile(filepath, "hello", true) + if err != nil { + return + } + + content, err := fileutil.ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} +``` + +### ReadFile + +

读取文件或者URL。

+ +函数签名: + +```go +func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) +``` + +示例:[运行](https://go.dev/play/p/uNep3Tr8fqF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + reader, fn, err := fileutil.ReadFile("https://httpbin.org/robots.txt") + if err != nil { + return + } + defer fn() + + dat, err := io.ReadAll(reader) + if err != nil { + return + } + + fmt.Println(string(dat)) + + // Output: + // User-agent: * + // Disallow: /deny +} +``` + +### ChunkRead + +

从文件的指定偏移读取块并返回块内所有行。

+ +函数签名: + +```go +func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) +``` + +示例:[运行](https://go.dev/play/p/r0hPmKWhsgf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 + + // test1.csv file content: + // Lili,22,female + // Jim,21,male + filePath := "./testdata/test1.csv" // 替换为你的文件路径 + f, err := os.Open(filePath) + if err != nil { + return + } + + defer f.Close() + + var bufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, defaultChunkSizeMB*mb) + }, + } + + lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool) + if err != nil { + return + } + + fmt.Println(lines[0]) + fmt.Println(lines[1]) + + // Output: + // Lili,22,female + // Jim,21,male +} +``` + +### ParallelChunkRead + +

并行读取文件并将每个块的行发送到指定通道。

+ +函数签名: + +```go +// filePath:文件路径 +// chunkSizeMB: 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整 +// maxGoroutine: 并发读取分块的数量,设置为0时使用CPU核心数 +// linesCh: 用于接收返回结果的通道。 +func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error +``` + +示例:[运行](https://go.dev/play/p/teMXnCsdSEw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 // 默认值 + + numParsers := runtime.NumCPU() + + linesCh := make(chan []string, numParsers) + + // test1.csv file content: + // Lili,22,female + // Jim,21,male + filePath := "./testdata/test1.csv" + + go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers) + + var totalLines int + for lines := range linesCh { + totalLines += len(lines) + + for _, line := range lines { + fmt.Println(line) + } + } + + fmt.Println(totalLines) + + // Output: + // Lili,22,female + // Jim,21,male + // 2 +} +``` + +### GetExeOrDllVersion + +

返回exe,dll文件版本号(仅Window平台).

+ +函数签名: + +```go +func GetExeOrDllVersion(filePath string) (string, error) +``` + +示例:[运行](https://go.dev/play/p/iLRrDBhE38E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`) + if err != nil { + panic(err) + } + fmt.Println(v) + // Output: + // 3.9.10.19 +} +``` diff --git a/docs/api/packages/formatter.md b/docs/api/packages/formatter.md new file mode 100644 index 00000000..9d33dcfb --- /dev/null +++ b/docs/api/packages/formatter.md @@ -0,0 +1,310 @@ +# Formatter + +formatter 格式化器包含一些数据格式化处理方法。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/formatter/formatter.go](https://github.com/duke-git/lancet/blob/main/formatter/formatter.go) +- [https://github.com/duke-git/lancet/blob/main/formatter/byte.go](https://github.com/duke-git/lancet/blob/main/formatter/byte.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/formatter" +) +``` + +
+ +## 目录 + +- [Comma](#Comma) +- [Pretty](#Pretty) +- [PrettyToWriter](#PrettyToWriter) +- [DecimalBytes](#DecimalBytes) +- [BinaryBytes](#BinaryBytes) +- [ParseDecimalBytes](#ParseDecimalBytes) +- [ParseBinaryBytes](#ParseBinaryBytes) + +
+ +## 文档 + +### Comma + +

用逗号每隔3位分割数字/字符串,支持添加前缀符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串

+ +函数签名: + +```go +func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string +``` + +示例:[运行](https://go.dev/play/p/eRD5k2vzUVX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.Comma("123", "") + result2 := formatter.Comma("12345", "$") + result3 := formatter.Comma(1234567, "¥") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 123 + // $12,345 + // ¥1,234,567 +} +``` + +### Pretty + +

返回pretty JSON字符串.

+ +函数签名: + +```go +func Pretty(v any) (string, error) +``` + +示例:[运行](https://go.dev/play/p/YsciGj3FH2x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.Pretty([]string{"a", "b", "c"}) + result2, _ := formatter.Pretty(map[string]int{"a": 1}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [ + // "a", + // "b", + // "c" + // ] + // { + // "a": 1 + // } +} +``` + +### PrettyToWriter + +

Pretty encode数据到writer。

+ +函数签名: + +```go +func PrettyToWriter(v any, out io.Writer) error +``` + +示例:[运行](https://go.dev/play/p/LPLZ3lDi5ma) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + type User struct { + Name string `json:"name"` + Aage uint `json:"age"` + } + user := User{Name: "King", Aage: 10000} + + buf := &bytes.Buffer{} + err := formatter.PrettyToWriter(user, buf) + + fmt.Println(buf) + fmt.Println(err) + + // Output: + // { + // "name": "King", + // "age": 10000 + // } + // + // +} +``` + +### DecimalBytes + +

返回十进制标准(以1000为基数)下的可读字节单位字符串。precision参数指定小数点后的位数,默认为4。

+ +函数签名: + +```go +func DecimalBytes(size float64, precision ...int) string +``` + +示例:[运行](https://go.dev/play/p/FPXs1suwRcs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.DecimalBytes(1000) + result2 := formatter.DecimalBytes(1024) + result3 := formatter.DecimalBytes(1234567) + result4 := formatter.DecimalBytes(1234567, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KB + // 1.024KB + // 1.2346MB + // 1.235MB +} +``` + +### BinaryBytes + +

返回binary标准(以1024为基数)下的可读字节单位字符串。precision参数指定小数点后的位数,默认为4。

+ +函数签名: + +```go +func BinaryBytes(size float64, precision ...int) string +``` + +示例:[运行](https://go.dev/play/p/G9oHHMCAZxP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.BinaryBytes(1024) + result2 := formatter.BinaryBytes(1024 * 1024) + result3 := formatter.BinaryBytes(1234567) + result4 := formatter.BinaryBytes(1234567, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KiB + // 1MiB + // 1.1774MiB + // 1.18MiB +} +``` + +### ParseDecimalBytes + +

将字节单位字符串转换成其所表示的字节数(以1000为基数)。

+ +函数签名: + +```go +func ParseDecimalBytes(size string) (uint64, error) +``` + +示例:[运行](https://go.dev/play/p/Am98ybWjvjj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.ParseDecimalBytes("12") + result2, _ := formatter.ParseDecimalBytes("12k") + result3, _ := formatter.ParseDecimalBytes("12 Kb") + result4, _ := formatter.ParseDecimalBytes("12.2 kb") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12000 + // 12000 + // 12200 +} +``` + +### ParseBinaryBytes + +

将字节单位字符串转换成其所表示的字节数(以1024为基数)。

+ +函数签名: + +```go +func ParseBinaryBytes(size string) (uint64, error) +``` + +示例:[运行](https://go.dev/play/p/69v1tTT62x8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.ParseBinaryBytes("12") + result2, _ := formatter.ParseBinaryBytes("12ki") + result3, _ := formatter.ParseBinaryBytes("12 KiB") + result4, _ := formatter.ParseBinaryBytes("12.2 kib") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12288 + // 12288 + // 12492 +} +``` diff --git a/docs/api/packages/function.md b/docs/api/packages/function.md new file mode 100644 index 00000000..c20e35bb --- /dev/null +++ b/docs/api/packages/function.md @@ -0,0 +1,786 @@ +# Function + +function 函数包控制函数执行流程,包含部分函数式编程。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go) +- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go) +- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/function" +) +``` + +
+ +## 目录 + +- [After](#After) +- [Before](#Before) +- [CurryFn](#CurryFn) +- [Compose](#Compose) +- [Debounce](#Debounce) +- [Debounceddeprecated](#Debounced) +- [Delay](#Delay) +- [Schedule](#Schedule) +- [Pipeline](#Pipeline) +- [Watcher](#Watcher) +- [And](#And) +- [Or](#Or) +- [Negate](#Negate) +- [Nor](#Nor) +- [Xnor](#Xnor) +- [Nand](#Nand) +- [AcceptIf](#AcceptIf) +- [Throttle](#Throttle) + + +
+ +## 文档 + +### After + +

创建一个函数,当他被调用n或更多次之后将马上触发fn

+ +函数签名: + +```go +func After(n int, fn any) func(args ...any) []reflect.Value +``` + +示例:[运行](https://go.dev/play/p/eRD5k2vzUVX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + fn := function.After(2, func() { + fmt.Println("hello") + }) + + fn() + fn() + + // Output: + // hello +} +``` + +### Before + +

创建一个函数,调用次数不超过n次,之后再调用这个函数,将返回一次最后调用fn的结果

+ +函数签名: + +```go +func Before(n int, fn any) func(args ...any) []reflect.Value +``` + +示例:[运行](https://go.dev/play/p/0HqUDIFZ3IL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + fn := function.Before(2, func() { + fmt.Println("hello") + }) + + fn() + fn() + fn() + fn() + + // Output: + // hello + // hello +} +``` + +### CurryFn + +

创建柯里化函数

+ +函数签名: + +```go +type CurryFn[T any] func(...T) T +func (cf CurryFn[T]) New(val T) func(...T) T +``` + +示例:[运行](https://go.dev/play/p/5HopfDwANKX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + add := func(a, b int) int { + return a + b + } + + var addCurry function.CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) + } + add1 := addCurry.New(1) + + result := add1(2) + + fmt.Println(result) + + // Output: + // 3 +} +``` + +### Compose + +

从右至左组合函数列表fnList,返回组合后的函数

+ +函数签名: + +```go +func Compose[T any](fnList ...func(...T) T) func(...T) T +``` + +示例:[运行](https://go.dev/play/p/KKfugD4PKYF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) + } + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) + } + transform := function.Compose(toUpper, toLower) + + result := transform("aBCde") + + fmt.Println(result) + + // Output: + // ABCDE +} +``` + +### Debounce + +

创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。

+ +函数签名: + +```go +func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) +``` + +示例:[运行](https://go.dev/play/p/-dGFrYn_1Zi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + callCount := 0 + fn := func() { + callCount++ + } + + debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond) + + for i := 0; i < 10; i++ { + debouncedFn() + time.Sleep(50 * time.Millisecond) + } + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + debouncedFn() + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + // Output: + // 1 + // 2 +} +``` + +### Debounced + +

创建一个函数的去抖动版本。

+ +> ⚠️ 本函数已弃用. 使用 `Debounce` 代替. + +函数签名: + +```go +func Debounced(fn func(), duration time.Duration) func() +``` + +示例:[运行](https://go.dev/play/p/absuEGB_GN7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + count := 0 + + add := func() { + count++ + } + + debouncedAdd := function.Debounced(add, 50*time.Microsecond) + + debouncedAdd() + debouncedAdd() + debouncedAdd() + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + // Output: + // 1 + // 2 +} +``` + +### Delay + +

延迟delay时间后调用函数

+ +函数签名: + +```go +func Delay(delay time.Duration, fn any, args ...any) +``` + +示例:[运行](https://go.dev/play/p/Ivtc2ZE-Tye) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + var print = func(s string) { + fmt.Println(s) + } + + function.Delay(2*time.Second, print, "hello") + + // Output: + // hello +} +``` + +### Schedule + +

每次持续时间调用函数,直到关闭返回的 bool chan

+ +函数签名: + +```go +func Schedule(d time.Duration, fn any, args ...any) chan bool +``` + +示例:[运行](https://go.dev/play/p/hbON-Xeyn5N) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + count := 0 + + increase := func() { + count++ + } + + stop := function.Schedule(2*time.Second, increase) + + time.Sleep(2 * time.Second) + close(stop) + + fmt.Println(count) + + // Output: + // 2 +} +``` + +### Pipeline + +

执行函数pipeline.

+ +函数签名: + +```go +func Pipeline[T any](funcs ...func(T) T) func(T) T +``` + +示例:[运行](https://go.dev/play/p/mPdUVvj6HD6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + addOne := func(x int) int { + return x + 1 + } + double := func(x int) int { + return 2 * x + } + square := func(x int) int { + return x * x + } + + fn := function.Pipeline(addOne, double, square) + + result := fn(2) + + fmt.Println(result) + + // Output: + // 36 +} +``` + +### Watcher + +

Watcher用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。

+ +函数签名: + +```go +type Watcher struct { + startTime int64 + stopTime int64 + excuting bool +} +func NewWatcher() *Watcher +func (w *Watcher) Start() +func (w *Watcher) Stop() +func (w *Watcher) Reset() +func (w *Watcher) GetElapsedTime() time.Duration + +``` + +示例:[运行](https://go.dev/play/p/l2yrOpCLd1I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + w := function.NewWatcher() + + w.Start() + + longRunningTask() + + fmt.Println(w.excuting) //true + + w.Stop() + + eapsedTime := w.GetElapsedTime().Milliseconds() + + fmt.Println(eapsedTime) + + w.Reset() + +} + +func longRunningTask() { + var slice []int64 + for i := 0; i < 10000000; i++ { + slice = append(slice, int64(i)) + } +} + +``` + +### And + +

返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。只有当所有谓词判断函数对于给定的值都返回true时,返回true, 否则返回false。

+ +函数签名: + +```go +func And[T any](predicates ...func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/dTBHJMQ0zD2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isNumericAndLength5 := function.And( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcde")) + + // Output: + // true + // false + // false +} +``` + +### Or + +

返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。只有当所有谓词判断函数对于给定的值都返回false时,返回false, 否则返回true。

+ +函数签名: + +```go +func Or[T any](predicates ...func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/LitCIsDFNDA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + containsDigitOrSpecialChar := function.Or( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return strings.ContainsAny(s, "!@#$%") }, + ) + + fmt.Println(containsDigitOrSpecialChar("hello!")) + fmt.Println(containsDigitOrSpecialChar("hello")) + + // Output: + // true + // false +} +``` + +### Negate + +

返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。

+ +函数签名: + +```go +func Negate[T any](predicate func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/jbI8BtgFnVE) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + // Define some simple predicates for demonstration + isUpperCase := func(s string) bool { + return strings.ToUpper(s) == s + } + isLowerCase := func(s string) bool { + return strings.ToLower(s) == s + } + isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase)) + + fmt.Println(isMixedCase("ABC")) + fmt.Println(isMixedCase("AbC")) + + // Output: + // false + // true +} +``` + + +### Nor + +

返回一个组合谓词函数,表示给定值上所有谓词逻辑非或 (nor) 的结果。只有当所有谓词函数对给定值都返回false时,该组合谓词函数才返回true。

+ +函数签名: + +```go +func Nor[T any](predicates ...func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/2KdCoBEOq84) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + match := function.Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("dbcdckkeee")) + + + match = function.Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("0123456789")) + + // Output: + // true + // false +} +``` + +### Nand + +

返回一个复合谓词函数,表示给定谓词函数列表的逻辑非与 (NAND)。仅当列表中所有函数对给定参数返回false时,才返回true,否则返回false。

+ +函数签名: + +```go +func Nand[T any](predicates ...func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/Rb-FdNGpgSO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isNumericAndLength5 := function.Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcdef")) + + // Output: + // false + // false + // true +} +``` + +### Xnor + +

返回一个复合谓词函数,表示给定一组谓词函数的逻辑异或 (XNOR)。只有当所有 谓词函数对给参数都返回true或false时,该谓词函数才返回true。

+ +函数签名: + +```go +func Xnor[T any](predicates ...func(T) bool) func(T) bool +``` + +示例:[运行](https://go.dev/play/p/FJxko8SFbqc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isEven := func(i int) bool { return i%2 == 0 } + isPositive := func(i int) bool { return i > 0 } + + match := function.Xnor(isEven, isPositive) + + fmt.Println(match(2)) + fmt.Println(match(-3)) + fmt.Println(match(3)) + + // Output: + // true + // true + // false +} +``` + +### AcceptIf + +

AcceptIf函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。

+ +函数签名: + +```go +func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) +``` + +示例:[运行](https://go.dev/play/p/XlXHHtzCf7d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + adder := function.AcceptIf( + function.And( + func(x int) bool { + return x > 10 + }, func(x int) bool { + return x%2 == 0 + }), + func(x int) int { + return x + 1 + }, + ) + + result, ok := adder(20) + fmt.Println(result) + fmt.Println(ok) + + result, ok = adder(21) + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 21 + // true + // 0 + // false +} + +``` + +### Throttle + +

创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。

+ +函数签名: + +```go +func Throttle(fn func(), interval time.Duration) func() +``` + +示例:[运行](https://go.dev/play/p/HpoMov-tJSN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + callCount := 0 + + fn := func() { + callCount++ + } + + throttledFn := function.Throttle(fn, 1*time.Second) + + for i := 0; i < 5; i++ { + throttledFn() + } + + time.Sleep(1 * time.Second) + + fmt.Println(callCount) + + // Output: + // 1 +} +``` \ No newline at end of file diff --git a/docs/api/packages/maputil.md b/docs/api/packages/maputil.md new file mode 100644 index 00000000..daa8b1a5 --- /dev/null +++ b/docs/api/packages/maputil.md @@ -0,0 +1,2347 @@ +# Maputil + +maputil 包包括一些操作 map 的函数。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go) +- [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) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/maputil" +) +``` + +
+ +## 目录: + +- [MapTo](#MapTo) +- [ForEach](#ForEach) +- [Filter](#Filter) +- [FilterByKeys](#FilterByKeys) +- [FilterByValues](#FilterByValues) +- [OmitBy](#OmitBy) +- [OmitByKeys](#OmitByKeys) +- [OmitByValues](#OmitByValues) +- [Intersect](#Intersect) +- [Keys](#Keys) +- [Values](#Values) +- [KeysBy](#KeysBy) +- [ValuesBy](#ValuesBy) +- [MapKeys](#MapKeys) +- [MapValues](#MapValues) +- [Entries](#Entries) +- [FromEntries](#FromEntries) +- [Transform](#Transform) +- [Merge](#Merge) +- [Minus](#Minus) +- [IsDisjoint](#IsDisjoint) +- [HasKey](#HasKey) +- [MapToStruct](#MapToStruct) +- [ToSortedSlicesDefault](#ToSortedSlicesDefault) +- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator) +- [NewOrderedMap](#NewOrderedMap) +- [OrderedMap_Set](#OrderedMap_Set) +- [OrderedMap_Get](#OrderedMap_Get) +- [OrderedMap_Front](#OrderedMap_Front) +- [OrderedMap_Back](#OrderedMap_Back) +- [OrderedMap_Delete](#OrderedMap_Delete) +- [OrderedMap_Clear](#OrderedMap_Clear) +- [OrderedMap_Len](#OrderedMap_Len) +- [OrderedMap_Keys](#OrderedMap_Keys) +- [OrderedMap_Values](#OrderedMap_Values) +- [OrderedMap_Contains](#OrderedMap_Contains) +- [OrderedMap_Range](#OrderedMap_Range) +- [OrderedMap_Elements](#OrderedMap_Elements) +- [OrderedMap_Iter](#OrderedMap_Iter) +- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter) +- [OrderedMap_SortByKey](#OrderedMap_SortByKey) +- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON) +- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON) +- [NewConcurrentMap](#NewConcurrentMap) +- [ConcurrentMap_Get](#ConcurrentMap_Get) +- [ConcurrentMap_Set](#ConcurrentMap_Set) +- [ConcurrentMap_GetOrSet](#ConcurrentMap_GetOrSet) +- [ConcurrentMap_Delete](#ConcurrentMap_Delete) +- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete) +- [ConcurrentMap_Has](#ConcurrentMap_Has) +- [ConcurrentMap_Range](#ConcurrentMap_Range) +- [GetOrSet](#GetOrSet) +- [SortByKey](#SortByKey) +- [GetOrDefault](#GetOrDefault) +- [FindValuesBy](#FindValuesBy) + +
+ +## API 文档: + +### MapTo + +

快速将map或者其他类型映射到结构体或者指定类型。

+ +函数签名: + +```go +func MapTo(src any, dst any) error +``` + +示例:[运行](https://go.dev/play/p/4K7KBEPgS5M) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} +``` + +### ForEach + +

对map中的每对key和value执行iteratee函数

+ +函数签名: + +```go +func ForEach[K comparable, V any](m map[K]V, iteratee func(key K, value V)) +``` + +示例:[运行](https://go.dev/play/p/OaThj6iNVXK) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + } + + var sum int + + maputil.ForEach(m, func(_ string, value int) { + sum += value + }) + + fmt.Println(sum) + + // Output: + // 10 +} +``` + +### Filter + +

迭代map中的每对key和value, 返回符合predicate函数的key, value。

+ +函数签名: + +```go +func Filter[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V +``` + +示例:[运行](https://go.dev/play/p/fSvF3wxuNG7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + maputil.Filter(m, func(_ string, value int) { + sum += value + }) + + result := Filter(m, isEven) + + fmt.Println(result) + + // Output: + // map[b:2 d:4] +} +``` + +### FilterByKeys + +

迭代map, 返回一个新map,其key都是给定的key值。

+ +函数签名: + +```go +func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V +``` + +示例:[运行](https://go.dev/play/p/7ov6BJHbVqh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.FilterByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2] +} +``` + +### FilterByValues + +

迭代map, 返回一个新map,其value都是给定的value值。

+ +函数签名: + +```go +func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V +``` + +示例:[运行](https://go.dev/play/p/P3-9MdcXegR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.FilterByValues(m, []int{3, 4}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4] +} +``` + +### OmitBy + +

Filter的反向操作, 迭代map中的每对key和value, 删除符合predicate函数的key, value, 返回新map。

+ +函数签名: + +```go +func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V +``` + +示例:[运行](https://go.dev/play/p/YJM4Hj5hNwm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + result := maputil.OmitBy(m, isEven) + + fmt.Println(result) + + // Output: + // map[a:1 c:3 e:5] +} +``` + +### OmitByKeys + +

FilterByKeys的反向操作, 迭代map, 返回一个新map,其key不包括给定的key值。

+ +函数签名: + +```go +func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V +``` + +示例:[运行](https://go.dev/play/p/jXGrWDBfSRp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.OmitByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4 e:5] +} +``` + +### OmitByValues + +

FilterByValues的反向操作, 迭代map, 返回一个新map,其value不包括给定的value值。

+ +函数签名: + +```go +func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V +``` + +示例:[运行](https://go.dev/play/p/XB7Y10uw20_U) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.OmitByValues(m, []int{4, 5}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### Intersect + +

多个map的交集操作

+ +函数签名: + +```go +func Intersect[K comparable, V any](maps ...map[K]V) map[K]V +``` + +示例:[运行](https://go.dev/play/p/Zld0oj3sjcC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 1, + "b": 2, + "c": 6, + "d": 7, + } + + m3 := map[string]int{ + "a": 1, + "b": 9, + "e": 9, + } + + result1 := maputil.Intersect(m1) + result2 := maputil.Intersect(m1, m2) + result3 := maputil.Intersect(m1, m2, m3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // map[a:1 b:2 c:3] + // map[a:1 b:2] + // map[a:1] +} +``` + +### Keys + +

返回map中所有key的切片

+ +函数签名: + +```go +func Keys[K comparable, V any](m map[K]V) []K +``` + +示例:[运行](https://go.dev/play/p/xNB5bTb97Wd) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + keys := maputil.Keys(m) + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [1 2 3 4 5] +} +``` + +### Merge + +

合并多个maps, 相同的key会被后来的key覆盖

+ +函数签名: + +```go +func Merge[K comparable, V any](maps ...map[K]V) map[K]V +``` + +示例:[运行](https://go.dev/play/p/H95LENF1uB-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[int]string{ + 1: "a", + 2: "b", + } + m2 := map[int]string{ + 1: "1", + 3: "2", + } + + result := maputil.Merge(m1, m2) + + fmt.Println(result) + + // Output: + // map[1:c 2:b 3:d] +} +``` + +### Minus + +

返回一个map,其中的key存在于mapA,不存在于mapB.

+ +函数签名: + +```go +func Minus[K comparable, V any](mapA, mapB map[K]V) map[K]V +``` + +示例:[运行](https://go.dev/play/p/3u5U9K7YZ9m) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 11, + "b": 22, + "d": 33, + } + + result := maputil.Minus(m1, m2) + + fmt.Println(result) + + // Output: + // map[c:3] +} +``` + +### Values + +

返回map中所有value的切片

+ +函数签名: + +```go +func Values[K comparable, V any](m map[K]V) []V +``` + +示例:[运行](https://go.dev/play/p/CBKdUc5FTW6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + values := maputil.Values(m) + sort.Strings(values) + + // Output: + // [a a b c d] +} +``` + +### KeysBy + +

创建一个切片,其元素是每个map的key调用mapper函数的结果。

+ +函数签名: + +```go +func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T +``` + +示例:[运行](https://go.dev/play/p/hI371iB8Up8) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + } + + keys := maputil.KeysBy(m, func(n int) int { + return n + 1 + }) + + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [2 3 4] +} +``` + +### ValuesBy + +

创建一个切片,其元素是每个map的value调用mapper函数的结果。

+ +函数签名: + +```go +func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T +``` + +示例:[运行](https://go.dev/play/p/sg9-oRidh8f) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + values := maputil.ValuesBy(m, func(v string) string { + switch v { + case "a": + return "a-1" + case "b": + return "b-2" + case "c": + return "c-3" + default: + return "" + } + }) + + sort.Strings(values) + + fmt.Println(values) + + // Output: + // [a-1 b-2 c-3] +} +``` + +### MapKeys + +

操作map的每个key,然后转为新的map。

+ +函数签名: + +```go +func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V +``` + +示例:[运行](https://go.dev/play/p/8scDxWeBDKd) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := maputil.MapKeys(m, func(k int, _ string) string { + return strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c] +} +``` + +### MapValues + +

操作map的每个value,然后转为新的map。

+ +函数签名: + +```go +func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T +``` + +示例:[运行](https://go.dev/play/p/g92aY3fc7Iw) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := maputil.MapValues(m, func(k int, v string) string { + return v + strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a1 2:b2 3:c3] +} +``` + +### Entry + +

将map转换为键/值对切片。

+ +函数签名: + +```go +type Entry[K comparable, V any] struct { + Key K + Value V +} +func Entries[K comparable, V any](m map[K]V) []Entry[K, V] +``` + +示例:[运行](https://go.dev/play/p/Ltb11LNcElY) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := maputil.Entries(m) + + sort.Slice(result, func(i, j int) bool { + return result[i].Value < result[j].Value + }) + + fmt.Println(result) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### FromEntries + +

基于键/值对的切片创建map。

+ +函数签名: + +```go +type Entry[K comparable, V any] struct { + Key K + Value V +} +func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V +``` + +示例:[运行](https://go.dev/play/p/C8L4ul9TVwf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + result := maputil.FromEntries([]Entry[string, int]{ + {Key: "a", Value: 1}, + {Key: "b", Value: 2}, + {Key: "c", Value: 3}, + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### Transform + +

将map转换为其他类型的map。

+ +函数签名: + +```go +func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 +``` + +示例:[运行](https://go.dev/play/p/P6ovfToM3zj) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Transform(m, func(k string, v int) (string, string) { + return k, strconv.Itoa(v) + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### IsDisjoint + +

验证两个map是否具有不同的key

+ +函数签名: + +```go +func IsDisjoint[K comparable, V any](mapA, mapB map[K]V) bool +``` + +示例:[运行](https://go.dev/play/p/N9qgYg_Ho6f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "d": 22, + } + + m3 := map[string]int{ + "a": 22, + } + + result1 := maputil.IsDisjoint(m1, m2) + result2 := maputil.IsDisjoint(m1, m3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### HasKey + +

检查map是否包含某个key。用于代替以下样板代码:

+ +函数签名: + +```go +func HasKey[K comparable, V any](m map[K]V, key K) bool +``` + +示例:[运行](https://go.dev/play/p/isZZHOsDhFc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + } + + result1 := maputil.HasKey(m, "a") + result2 := maputil.HasKey(m, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### MapToStruct + +

将map转成struct。

+ +函数签名: + +```go +func MapToStruct(m map[string]any, structObj any) error +``` + +示例:[运行](https://go.dev/play/p/7wYyVfX38Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + personReqMap := map[string]any{ + "name": "Nothin", + "max_age": 35, + "page": 1, + "pageSize": 10, + } + + type PersonReq struct { + Name string `json:"name"` + MaxAge int `json:"max_age"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + } + var personReq PersonReq + _ = maputil.MapToStruct(personReqMap, &personReq) + fmt.Println(personReq) + + // Output: + // {Nothin 35 1 10} +} +``` + +### ToSortedSlicesDefault + +

将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。

+ +函数签名: + +```go +func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) +``` + +示例:[运行](https://go.dev/play/p/43gEM2po-qy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + + keys, values := ToSortedSlicesDefault(m) + + fmt.Println(keys) + fmt.Println(values) + + // Output: + // [1 2 3] + // [a b c] +} +``` + +### ToSortedSlicesWithComparator + +

将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。

+ +函数签名: + +```go +func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) +``` + +示例:[运行](https://go.dev/play/p/0nlPo6YLdt3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[time.Time]string{ + time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today", + time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday", + time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow", + } + + keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool { + return a.Before(b) + }) + + m2 := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool { + return a > b + }) + + fmt.Println(keys2) + fmt.Println(values2) + + fmt.Println(keys1) + fmt.Println(values1) + + // Output: + // [3 2 1] + // [c b a] + // [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC] + // [yesterday today tomorrow] +} +``` + +### NewOrderedMap + +

创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。

+ +函数签名: + +```go +func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Set + +

设置给定的键值对。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Set(key K, value V) +``` + +示例:[运行](https://go.dev/play/p/Y4ZJ_oOc1FU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Get + +

返回给定键的值。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Get(key K) (V, bool) +``` + +示例:[运行](https://go.dev/play/p/Y4ZJ_oOc1FU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Delete + +

删除给定键的键值对。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Delete(key K) +``` + +示例:[运行](https://go.dev/play/p/5bIi4yaZ3K-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} +``` + +### OrderedMap_Clear + +

清空map数据。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Clear() +``` + +示例:[运行](https://go.dev/play/p/8LwoJyEfuFr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} +``` + +### OrderedMap_Front + +

返回第一个键值对。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Front() (struct { + Key K + Value V +}, bool) +``` + +示例:[运行](https://go.dev/play/p/ty57XSimpoe) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Back + +

返回最后一个键值对。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Back() (struct { + Key K + Value V +}, bool) +``` + +示例:[运行](https://go.dev/play/p/rQMjp1yQmpa) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + backElement, ok := om.Back() + fmt.Println(backElement) + fmt.Println(ok) + + // Output: + // {c 3} + // true +} +``` + +### OrderedMap_Range + +

为每个键值对调用给定的函数。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) +``` + +示例:[运行](https://go.dev/play/p/U-KpORhc7LZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} +``` + +### OrderedMap_Keys + +

按顺序返回键的切片。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Keys() []K +``` + +示例:[运行](https://go.dev/play/p/Vv_y9ExKclA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} +``` + +### OrderedMap_Values + +

按顺序返回值的切片。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Values() []V +``` + +示例:[运行](https://go.dev/play/p/TWj5n1-PUfx) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} +``` + +### OrderedMap_Elements + +

按顺序返回键值对。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Elements() []struct +``` + +示例:[运行](https://go.dev/play/p/4BHG4kKz6bB) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### OrderedMap_Len + +

返回键值对的数量。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Len() int +``` + +示例:[运行](https://go.dev/play/p/cLe6z2VX5N-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} +``` + +### OrderedMap_Contains + +

如果给定的键存在则返回true。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Contains(key K) bool +``` + +示例:[运行](https://go.dev/play/p/QuwqqnzwDNX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### OrderedMap_Iter + +

返回按顺序产生键值对的通道。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Iter() <-chan struct { + Key K + Value V +} +``` + +示例:[运行](https://go.dev/play/p/tlq2tdvicPt) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} +``` + +### OrderedMap_ReverseIter + +

返回以相反顺序产生键值对的通道。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) ReverseIter() <-chan struct { + Key K + Value V +} +``` + +示例:[运行](https://go.dev/play/p/8Q0ssg6hZzO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} +``` + +### OrderedMap_SortByKey + +

使用传入的比较函数排序map key。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) +``` + +示例:[运行](https://go.dev/play/p/N7hjD_alZPq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} +``` + +### OrderedMap_MarshalJSON + +

实现json.Marshaler接口。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) +``` + +示例:[运行](https://go.dev/play/p/C-wAwydIAC7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### OrderedMap_UnmarshalJSON + +

实现json.Unmarshaler接口。

+ +函数签名: + +```go +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error +``` + +示例:[运行](https://go.dev/play/p/8C3MvJ3-mut) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + data := []byte(`{"a":1,"b":2,"c":3}`) + + om.UnmarshalJSON(data) + + fmt.Println(om.Elements()) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### NewConcurrentMap + +

ConcurrentMap协程安全的map结构。

+ +函数签名: + +```go +// NewConcurrentMap create a ConcurrentMap with specific shard count. +func NewConcurrentMap[K comparable, V any](shardCount int) *ConcurrentMap[K, V] +``` + +示例:[运行](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + // create a ConcurrentMap whose key type is string, value type is int + cm := maputil.NewConcurrentMap[string, int](100) +} +``` + +### ConcurrentMap_Set + +

在map中设置key和value。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Set(key K, value V) +``` + +示例:[运行](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + wg2.Done() + }(j) + } + wg2.Wait() + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_Get + +

根据key获取value, 如果不存在key,返回零值。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Get(key K) (V, bool) +``` + +示例:[运行](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + wg2.Done() + }(j) + } + wg2.Wait() + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_GetOrSet + +

返回键的现有值(如果存在),否则,设置key并返回给定值。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) GetOrSet(key K, value V) (actual V, ok bool) +``` + +示例:[运行](https://go.dev/play/p/aDcDApOK01a) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg sync.WaitGroup + wg.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + val, ok := cm.GetOrSet(fmt.Sprintf("%d", n), n) + fmt.Println(val, ok) + wg.Done() + }(i) + } + wg.Wait() + + // output: (order may change) + // 1 false + // 3 false + // 2 false + // 0 false + // 4 false +} +``` + +### ConcurrentMap_Delete + +

删除key。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Delete(key K) +``` + +示例:[运行](https://go.dev/play/p/uTIJZYhpVMS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + cm.Delete(fmt.Sprintf("%d", n)) + wg2.Done() + }(i) + } + wg2.Wait() +} +``` + +### ConcurrentMap_GetAndDelete + +

获取key,然后删除。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) GetAndDelete(key K) (actual V, ok bool) +``` + +示例:[运行](https://go.dev/play/p/ZyxeIXSZUiM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //n, true + + _, ok = cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //false + + wg2.Done() + }(j) + } + wg2.Wait() +} +``` + +### ConcurrentMap_Has + +

验证是否包含key。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Has(key K) bool +``` + +示例:[运行](https://go.dev/play/p/C8L4ul9TVwf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + + for j := 0; j < 5; j++ { + go func(n int) { + ok := cm.Has(fmt.Sprintf("%d", n)) + fmt.Println(ok) // true + + wg2.Done() + }(j) + } + wg2.Wait() +} +``` + +### ConcurrentMap_Range + +

为map中每个键和值顺序调用迭代器。 如果iterator返回false,则停止迭代。

+ +函数签名: + +```go +func (cm *ConcurrentMap[K, V]) Range(iterator func(key K, value V) bool) +``` + +示例:[运行](https://go.dev/play/p/iqcy7P8P0Pr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + cm.Range(func(key string, value int) bool { + fmt.Println(value) + return true + }) +} +``` + +### GetOrSet + +

返回给定键的值,如果不存在则设置该值。

+ +函数签名: + +```go +func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V +``` + +示例:[运行](https://go.dev/play/p/IVQwO1OkEJC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + } + + result1 := maputil.GetOrSet(m, 1, "1") + result2 := maputil.GetOrSet(m, 2, "b") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // a + // b +} +``` + +### SortByKey + +

对传入的map根据key进行排序,返回排序后的map。

+ +函数签名: + +```go +func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) +``` + +示例:[运行](https://go.dev/play/p/PVdmBSnm6P_W) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + + result := maputil.SortByKey(m, func(a, b int) bool { + return a < b + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c 4:d] +} +``` + +### GetOrDefault + +

返回给定键的值,如果键不存在,则返回默认值。

+ +函数签名: + +```go +func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V +``` + +示例:[运行](https://go.dev/play/p/99QjSYSBdiM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + + result1 := maputil.GetOrDefault(m, 1, "default") + result2 := maputil.GetOrDefault(m, 6, "default") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // a + // default +} +``` + +### FindValuesBy + +

返回一个切片,包含满足给定谓词判断函数的map中的值。

+ +函数签名: + +```go +func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V +``` + +示例:[运行](https://go.dev/play/p/bvNwNBZDm6v) + +```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] +} +``` diff --git a/docs/api/packages/mathutil.md b/docs/api/packages/mathutil.md new file mode 100644 index 00000000..2a4f2205 --- /dev/null +++ b/docs/api/packages/mathutil.md @@ -0,0 +1,1301 @@ +# Mathutil + +mathutil 包实现了一些数学计算的函数. + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/mathutil" +) +``` + +
+ +## 目录 + +- [Average](#Average) +- [Exponent](#Exponent) +- [Fibonacci](#Fibonacci) +- [Factorial](#Factorial) +- [Max](#Max) +- [MaxBy](#MaxBy) +- [Min](#Min) +- [MinBy](#MaxBy) +- [Percent](#Percent) +- [RoundToFloat](#RoundToFloat) +- [RoundToString](#RoundToString) +- [T运行cRound](#T运行cRound) +- [CeilToFloat](#CeilToFloat) +- [CeilToString](#CeilToString) +- [FloorToFloat](#FloorToFloat) +- [FloorToString](#FloorToString) +- [Range](#Range) +- [RangeWithStep](#RangeWithStep) +- [AngleToRadian](#AngleToRadian) +- [RadianToAngle](#RadianToAngle) +- [PointDistance](#PointDistance) +- [IsPrime](#IsPrime) +- [GCD](#GCD) +- [LCM](#LCM) +- [Cos](#Cos) +- [Sin](#Sin) +- [Log](#Log) +- [Sum](#Sum) +- [Abs](#Abs) +- [Div](#Div) +- [Variance](#Variance) +- [StdDev](#StdDev) +- [Permutation](#Permutation) +- [Combination](#Combination) + +
+ +## 文档 + +### Average + +

计算平均数. 可能需要对结果调用RoundToFloat方法四舍五入

+ +函数签名: + +```go +func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 +``` + +示例:[运行](https://go.dev/play/p/HFd70x4DrMj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Average(1, 2) + + avg := mathutil.Average(1.2, 1.4) + result2 := mathutil.RoundToFloat(avg, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1.5 + // 1.3 +} +``` + +### Exponent + +

指数计算(x的n次方)

+ +函数签名: + +```go +func Exponent(x, n int64) int64 +``` + +示例:[运行](https://go.dev/play/p/Vv7LBwER-pz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Exponent(10, 0) + result2 := mathutil.Exponent(10, 1) + result3 := mathutil.Exponent(10, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 10 + // 100 +} +``` + +### Fibonacci + +

计算斐波那契数列的第n个数

+ +函数签名: + +```go +func Fibonacci(first, second, n int) int +``` + +示例:[运行](https://go.dev/play/p/IscseUNMuUc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Fibonacci(1, 1, 1) + result2 := mathutil.Fibonacci(1, 1, 2) + result3 := mathutil.Fibonacci(1, 1, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 1 + // 5 +} +``` + +### Factorial + +

计算阶乘

+ +函数签名: + +```go +func Factorial(x uint) uint +``` + +示例:[运行](https://go.dev/play/p/tt6LdOK67Nx) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Factorial(1) + result2 := mathutil.Factorial(2) + result3 := mathutil.Factorial(3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 6 +} +``` + +### Max + +

返回参数中的最大数

+ +函数签名: + +```go +func Max[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +示例:[运行](https://go.dev/play/p/cN8DHI0rTkH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Max(1, 2, 3) + result2 := mathutil.Max(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.4 +} +``` + +### MaxBy + +

使用给定的比较器函数返回切片的最大值

+ +函数签名: + +```go +func MaxBy[T any](slice []T, comparator func(T, T) bool) T +``` + +示例:[运行](https://go.dev/play/p/pbe2MT-7DV2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.MaxBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result2 := mathutil.MaxBy([]string{"abd", "abc", "ab"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result3 := mathutil.MaxBy([]string{}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abc + // abd + // +} +``` + +### Min + +

返回参数中的最小数

+ +函数签名: + +```go +func Min[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +示例:[运行](https://go.dev/play/p/pbe2MT-7DV2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Min(1, 2, 3) + result2 := mathutil.Min(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 1.1 +} +``` + +### MinBy + +

使用给定的比较器函数返回切片的最小值

+ +函数签名: + +```go +func MinBy[T any](slice []T, comparator func(T, T) bool) T +``` + +示例:[运行](https://go.dev/play/p/N9qgYg_Ho6f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.MinBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result2 := mathutil.MinBy([]string{"ab", "ac", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result3 := mathutil.MinBy([]string{}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // a + // ab + // +} +``` + +### Percent + +

计算百分比,保留n位小数

+ +函数签名: + +```go +func Percent(val, total float64, n int) float64 +``` + +示例:[运行](https://go.dev/play/p/s0NdFCtwuyd) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Percent(1, 2, 2) + result2 := mathutil.Percent(0.1, 0.3, 2) + result3 := mathutil.Percent(-30305, 408420, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 50 + // 33.33 + // -7.42 +} +``` + +### RoundToFloat + +

四舍五入,保留n位小数

+ +函数签名: + +```go +func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +示例:[运行](https://go.dev/play/p/ghyb528JRJL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RoundToFloat(0.124, 2) + result2 := mathutil.RoundToFloat(0.125, 2) + result3 := mathutil.RoundToFloat(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} +``` + +### RoundToString + +

四舍五入,保留n位小数,返回字符串

+ +函数签名: + +```go +func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +示例:[运行](https://go.dev/play/p/kZwpBRAcllO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RoundToString(0.124, 2) + result2 := mathutil.RoundToString(0.125, 2) + result3 := mathutil.RoundToString(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} +``` + +### TruncRound + +

截短n位小数(不进行四舍五入)

+ +函数签名: + +```go +func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T +``` + +示例:[运行](https://go.dev/play/p/aumarSHIGzP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.TruncRound(0.124, 2) + result2 := mathutil.TruncRound(0.125, 2) + result3 := mathutil.TruncRound(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.12 + // 0.125 +} +``` + +### CeilToFloat + +

向上舍入(进一法),保留n位小数。

+ +函数签名: + +```go +func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +示例:[运行](https://go.dev/play/p/8hOeSADZPCo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.CeilToFloat(3.14159, 1) + result2 := mathutil.CeilToFloat(3.14159, 2) + result3 := mathutil.CeilToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5 +} +``` + +### CeilToString + +

向上舍入(进一法),保留n位小数,返回字符串。

+ +函数签名: + +```go +func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +示例:[运行](https://go.dev/play/p/wy5bYEyUKKG) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.CeilToString(3.14159, 1) + result2 := mathutil.CeilToString(3.14159, 2) + result3 := mathutil.CeilToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5.0000 +} +``` + +### FloorToFloat + +

向下舍入(去尾法),保留n位小数。

+ +函数签名: + +```go +func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +示例:[运行](https://go.dev/play/p/vbCBrQHZEED) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.FloorToFloat(3.14159, 1) + result2 := mathutil.FloorToFloat(3.14159, 2) + result3 := mathutil.FloorToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5 +} +``` + +### FloorToString + +

向下舍入(去尾法),保留n位小数,返回字符串。

+ +函数签名: + +```go +func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +示例:[运行](https://go.dev/play/p/Qk9KPd2IdDb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.FloorToString(3.14159, 1) + result2 := mathutil.FloorToString(3.14159, 2) + result3 := mathutil.FloorToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5.0000 +} +``` + +### Range + +

根据指定的起始值和数量,创建一个数字切片。

+ +函数签名: + +```go +func Range[T constraints.Integer | constraints.Float](start T, count int) []T +``` + +示例:[运行](https://go.dev/play/p/9ke2opxa8ZP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Range(1, 4) + result2 := mathutil.Range(1, -4) + result3 := mathutil.Range(-4, 4) + result4 := mathutil.Range(1.0, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [-4 -3 -2 -1] + // [1 2 3 4] +} +``` + +### RangeWithStep + +

根据指定的起始值,结束值,步长,创建一个数字切片。

+ +函数签名: + +```go +func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T +``` + +示例:[运行](https://go.dev/play/p/akLWz0EqOSM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RangeWithStep(1, 4, 1) + result2 := mathutil.RangeWithStep(1, -1, 0) + result3 := mathutil.RangeWithStep(-4, 1, 2) + result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3] + // [] + // [-4 -2 0] + // [1 2.1 3.2] +} +``` + +### AngleToRadian + +

将角度值转为弧度值

+ +函数签名: + +```go +func AngleToRadian(angle float64) float64 +``` + +示例:[运行](https://go.dev/play/p/CIvlICqrHql) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.AngleToRadian(45) + result2 := mathutil.AngleToRadian(90) + result3 := mathutil.AngleToRadian(180) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.7853981633974483 + // 1.5707963267948966 + // 3.141592653589793 +} +``` + +### RadianToAngle + +

将弧度值转为角度值

+ +函数签名: + +```go +func RadianToAngle(radian float64) float64 +``` + +示例:[运行](https://go.dev/play/p/dQtmOTUOMgi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RadianToAngle(math.Pi) + result2 := mathutil.RadianToAngle(math.Pi / 2) + result3 := mathutil.RadianToAngle(math.Pi / 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 180 + // 90 + // 45 +} +``` + +### PointDistance + +

计算两个坐标点的距离

+ +函数签名: + +```go +func PointDistance(x1, y1, x2, y2 float64) float64 +``` + +示例:[运行](https://go.dev/play/p/RrG4JIaziM8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.PointDistance(1, 1, 4, 5) + + fmt.Println(result1) + + // Output: + // 5 +} +``` + +### IsPrime + +

判断质数。

+ +函数签名: + +```go +func IsPrime(n int) bool +``` + +示例:[运行](https://go.dev/play/p/Rdd8UTHZJ7u) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.IsPrime(-1) + result2 := mathutil.IsPrime(0) + result3 := mathutil.IsPrime(1) + result4 := mathutil.IsPrime(2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### GCD + +

计算最大公约数。

+ +函数签名: + +```go +func GCD[T constraints.Integer](integers ...T) T +``` + +示例:[运行](https://go.dev/play/p/CiEceLSoAKB) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.GCD(1, 1) + result2 := mathutil.GCD(1, -1) + result3 := mathutil.GCD(-1, 1) + result4 := mathutil.GCD(-1, -1) + result5 := mathutil.GCD(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // 1 + // -1 + // -1 + // 3 +} +``` + +### LCM + +

计算最小公倍数。

+ +函数签名: + +```go +func LCM[T constraints.Integer](integers ...T) T +``` + +示例:[运行](https://go.dev/play/p/EjcZxfY7G_g) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.LCM(1, 1) + result2 := mathutil.LCM(1, 2) + result3 := mathutil.LCM(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 18 +} +``` + +### Cos + +

计算弧度的余弦值

+ +函数签名: + +```go +func Cos(radian float64, precision ...int) float64 +``` + +示例:[运行](https://go.dev/play/p/Sm89LoIfvFq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Cos(0) + result2 := mathutil.Cos(90) + result3 := mathutil.Cos(180) + result4 := mathutil.Cos(math.Pi) + result5 := mathutil.Cos(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // -0.447 + // -0.598 + // -1 + // 0 +} +``` + +### Sin + +

计算弧度的正弦值

+ +函数签名: + +```go +func Sin(radian float64, precision ...int) float64 +``` + +示例:[运行](https://go.dev/play/p/TWMQlMywDsP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Sin(0) + result2 := mathutil.Sin(90) + result3 := mathutil.Sin(180) + result4 := mathutil.Sin(math.Pi) + result5 := mathutil.Sin(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 0 + // 0.894 + // -0.801 + // 0 + // 1 +} +``` + +### Log + +

计算以base为底n的对数。

+ +函数签名: + +```go +func Log(n, base float64) float64 +``` + +示例:[运行](https://go.dev/play/p/_d4bi8oyhat) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Log(8, 2) + result2 := mathutil.T运行cRound(mathutil.Log(5, 2), 2) + result3 := mathutil.T运行cRound(mathutil.Log(27, 3), 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3 + // 2.32 + // 3 +} +``` + +### Sum + +

求传入参数之和。

+ +函数签名: + +```go +func Sum[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +示例:[运行](https://go.dev/play/p/1To2ImAMJA7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Sum(1, 2) + result2 := mathutil.Sum(0.1, float64(1)) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.1 +} +``` + +### Abs + +

求绝对值。

+ +函数签名: + +```go +func Abs[T constraints.Integer | constraints.Float](x T) T +``` + +示例:[运行](https://go.dev/play/p/fsyBh1Os-1d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := Abs(-1) + result2 := Abs(-0.1) + result3 := Abs(float32(0.2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 0.1 + // 0.2 +} +``` + +### Div + +

除法运算。

+ +函数签名: + +```go +func Div[T constraints.Float | constraints.Integer](x T, y T) float64 +``` + +示例:[运行](https://go.dev/play/p/WLxDdGXXYat) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Div(9, 4) + result2 := mathutil.Div(1, 2) + result3 := mathutil.Div(0, 666) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2.25 + // 0.5 + // 0 +} +``` + +### Variance + +

计算方差。

+ +函数签名: + +```go +func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 +``` + +示例:[示例](https://go.dev/play/p/uHuV4YgXf8F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Variance([]int{1, 2, 3, 4, 5}) + result2 := mathutil.Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 2 + // 2.42 +} +``` + +### StdDev + +

计算标准差。

+ +函数签名: + +```go +func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 +``` + +示例:[运行](https://go.dev/play/p/FkNZDXvHD2l) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + 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) + + // Output: + // 1.41 + // 1.55 +} +``` + +### Permutation + +

计算排列数P(n, k)。

+ +函数签名: + +```go +func Permutation(n, k uint) uint +``` + +示例:[运行](https://go.dev/play/p/MgobwH_FOxj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Permutation(5, 3) + result2 := mathutil.Permutation(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 60 + // 120 +} +``` + +### Combination + +

计算组合数C(n, k)。

+ +函数签名: + +```go +func Combination(n, k uint) uint +``` + +示例:[运行](https://go.dev/play/p/ENFQRDQUFi9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Combination(5, 3) + result2 := mathutil.Combination(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1 +} +``` \ No newline at end of file diff --git a/docs/api/packages/netutil.md b/docs/api/packages/netutil.md new file mode 100644 index 00000000..7be8c18b --- /dev/null +++ b/docs/api/packages/netutil.md @@ -0,0 +1,1115 @@ +# Netutil + +netutil 网络包支持获取 ip 地址,发送 http 请求。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/netutil/net.go](https://github.com/duke-git/lancet/blob/main/netutil/net.go) + +- [https://github.com/duke-git/lancet/blob/main/netutil/http.go](https://github.com/duke-git/lancet/blob/main/netutil/http.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/netutil" +) +``` + +
+ +## 目录 + +- [ConvertMapToQueryString](#ConvertMapToQueryString) +- [EncodeUrl](#EncodeUrl) +- [GetInternalIp](#GetInternalIp) +- [GetIps](#GetIps) +- [GetMacAddrs](#GetMacAddrs) +- [GetPublicIpInfo](#GetPublicIpInfo) +- [GetRequestPublicIp](#GetRequestPublicIp) +- [IsPublicIP](#IsPublicIP) +- [IsInternalIP](#IsInternalIP) +- [HttpRequest](#HttpRequest) +- [HttpClient](#HttpClient) +- [SendRequest](#SendRequest) +- [DecodeResponse](#DecodeResponse) +- [StructToUrlValues](#StructToUrlValues) +- [HttpGetDeprecated](#HttpGet) +- [HttpDeleteDeprecated](#HttpDelete) +- [HttpPostDeprecated](#HttpPost) +- [HttpPutDeprecated](#HttpPut) +- [HttpPatchDeprecated](#HttpPatch) +- [ParseHttpResponse](#ParseHttpResponse) +- [DownloadFile](#DownloadFile) +- [UploadFile](#UploadFile) +- [IsPingConnected](#IsPingConnected) +- [IsTelnetConnected](#IsTelnetConnected) +- [BuildUrl](#BuildUrl) +- [AddQueryParams](#AddQueryParams) + +
+ +## 文档 + +### ConvertMapToQueryString + +

将map转换成http查询字符串.

+ +函数签名: + +```go +func ConvertMapToQueryString(param map[string]any) string +``` + +示例:[运行](https://go.dev/play/p/jnNt_qoSnRi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + var m = map[string]any{ + "c": 3, + "a": 1, + "b": 2, + } + qs := netutil.ConvertMapToQueryString(m) + + fmt.Println(qs) + + // Output: + // a=1&b=2&c=3 +} +``` + +### EncodeUrl + +

编码url query string的值

+ +函数签名: + +```go +func EncodeUrl(urlStr string) (string, error) +``` + +示例:[运行](https://go.dev/play/p/bsZ6BRC4uKI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + urlAddr := "http://www.lancet.com?a=1&b=[2]" + encodedUrl, err := netutil.EncodeUrl(urlAddr) + + if err != nil { + fmt.Println(err) + } + + fmt.Println(encodedUrl) + + // Output: + // http://www.lancet.com?a=1&b=%5B2%5D +} +``` + +### GetInternalIp + +

获取内部ip

+ +函数签名: + +```go +func GetInternalIp() string +``` + +示例:[运行](https://go.dev/play/p/fxnna_LLD9u) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + internalIp := netutil.GetInternalIp() + ip := net.ParseIP(internalIp) + + fmt.Println(ip) + + // Output: + // 192.168.1.9 +} +``` + +### GetIps + +

获取ipv4地址列表

+ +函数签名: + +```go +func GetIps() []string +``` + +示例:[运行](https://go.dev/play/p/NUFfcEmukx1) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ips := netutil.GetIps() + fmt.Println(ips) + + // Output: + // [192.168.1.9] +} +``` + +### GetMacAddrs + +

获取mac地址列

+ +函数签名: + +```go +func GetMacAddrs() []string { +``` + +示例:[运行](https://go.dev/play/p/Rq9UUBS_Xp1) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + macAddrs := netutil.GetMacAddrs() + fmt.Println(macAddrs) + + // Output: + // [18:31:bf:09:d1:56 76:ee:2a:e6:2e:0f 74:ee:2a:e6:2e:0f 74:ee:2a:e6:2e:0f] +} +``` + +### GetPublicIpInfo + +

获取公网ip信息

+ +函数签名: + +```go +func GetPublicIpInfo() (*PublicIpInfo, error) +type PublicIpInfo struct { + Status string `json:"status"` + Country string `json:"country"` + CountryCode string `json:"countryCode"` + Region string `json:"region"` + RegionName string `json:"regionName"` + City string `json:"city"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + Isp string `json:"isp"` + Org string `json:"org"` + As string `json:"as"` + Ip string `json:"query"` +} +``` + +示例:[运行](https://go.dev/play/p/YDxIfozsRHR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + publicIpInfo, err := netutil.GetPublicIpInfo() + if err != nil { + fmt.Println(err) + } + + fmt.Println(publicIpInfo) +} +``` + +### GetRequestPublicIp + +

获取http请求ip

+ +函数签名: + +```go +func GetRequestPublicIp(req *http.Request) string +``` + +示例:[运行](https://go.dev/play/p/kxU-YDc_eBo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip := "36.112.24.10" + + request := http.Request{ + Method: "GET", + Header: http.Header{ + "X-Forwarded-For": {ip}, + }, + } + publicIp := netutil.GetRequestPublicIp(&request) + + fmt.Println(publicIp) + + // Output: + // 36.112.24.10 +} +``` + +### IsPublicIP + +

判断ip是否是公共ip

+ +函数签名: + +```go +func IsPublicIP(IP net.IP) bool +``` + +示例:[运行](https://go.dev/play/p/nmktSQpJZnn) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip1 := netutil.IsPublicIP(net.ParseIP("127.0.0.1")) + ip2 := netutil.IsPublicIP(net.ParseIP("192.168.0.1")) + ip3 := netutil.IsPublicIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // false + // false + // true +} +``` + +### IsInternalIP + +

判断ip是否是局域网ip.

+ +函数签名: + +```go +func IsInternalIP(IP net.IP) bool +``` + +示例:[运行](https://go.dev/play/p/sYGhXbgO4Cb) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip1 := netutil.IsInternalIP(net.ParseIP("127.0.0.1")) + ip2 := netutil.IsInternalIP(net.ParseIP("192.168.0.1")) + ip3 := netutil.IsInternalIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // true + // true + // false +} +``` + +### HttpRequest + +

HttpRequest用于抽象HTTP请求实体的结构

+ +函数签名: + +```go +type HttpRequest struct { + RawURL string + Method string + Headers http.Header + QueryParams url.Values + FormData url.Values + Body []byte +} +``` + +示例:[运行](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + header := http.Header{} + header.Add("Content-Type", "multipart/form-data") + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "testItem") + + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos", + Method: "POST", + Headers: header, + FormData: postData, + } +} +``` + +### HttpClient + +

HttpClient是用于发送HTTP请求的结构体。它可以用一些配置参数或无配置实例化.

+ +函数签名: + +```go +type HttpClient struct { + *http.Client + TLS *tls.Config + Request *http.Request + Config HttpClientConfig +} + +type HttpClientConfig struct { + SSLEnabled bool + TLSConfig *tls.Config + Compressed bool + HandshakeTimeout time.Duration + ResponseTimeout time.Duration + Verbose bool +} + +func NewHttpClient() *HttpClient + +func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient + +``` + +示例:[运行](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + httpClientCfg := netutil.HttpClientConfig{ + SSLEnabled: true, + HandshakeTimeout:10 * time.Second + } + httpClient := netutil.NewHttpClientWithConfig(&httpClientCfg) +} +``` + +### SendRequest + +

HttpClient发送http请求

+ +函数签名: + +```go +func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, error) +``` + +示例:[运行](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := netutil.NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + return + } + + fmt.Println(todo.Id) + + // Output: + // 1 +} +``` + +### DecodeResponse + +

解析http响应体到目标结构体

+ +函数签名: + +```go +func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error +``` + +示例:[运行](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := netutil.NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + return + } + + fmt.Println(todo.Id) + + // Output: + // 1 +} +``` + +### StructToUrlValues + +

将结构体转为url values, 仅转化结构体导出字段并且包含`json` tag

+ +函数签名: + +```go +func StructToUrlValues(targetStruct any) url.Values +``` + +示例:[运行](https://go.dev/play/p/pFqMkM40w9z) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + type TodoQuery struct { + Id int `json:"id"` + UserId int `json:"userId"` + Name string `json:"name,omitempty"` + Status string + } + item := TodoQuery{ + Id: 1, + UserId: 123, + Name: "test", + Status: "completed", + } + queryValues := netutil.StructToUrlValues(item) + + fmt.Println(todoValues.Get("id")) + fmt.Println(todoValues.Get("userId")) + fmt.Println(todoValues.Get("name")) + fmt.Println(todoValues.Get("status")) + + // Output: + // 1 + // 123 + // test + // +} +``` + +### HttpGet + +

发送http get请求。

+ +> ⚠️ 本函数已弃用,使用`SendRequest`代替。 + +函数签名: + +```go +// params[0] http请求header,类型必须是http.Header或者map[string]string +// params[1] http查询字符串,类型必须是url.Values或者map[string]string +// params[2] post请求体,类型必须是[]byte +// params[3] http client,类型必须是http.Client +func HttpGet(url string, params ...any) (*http.Response, error) +``` + +示例: + +```go +package main + +import ( + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + + resp, err := netutil.HttpGet(url, header) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPost + +

发送http post请求。

+ +> ⚠️ 本函数已弃用,使用`SendRequest`代替。 + +函数签名: + +```go +// params[0] http请求header,类型必须是http.Header或者map[string]string +// params[1] http查询字符串,类型必须是url.Values或者map[string]string +// params[2] post请求体,类型必须是[]byte +// params[3] http client,类型必须是http.Client +func HttpPost(url string, params ...any) (*http.Response, error) +``` + +示例: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos" + header := map[string]string{ + "Content-Type": "application/x-www-form-urlencoded", + } + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "TestToDo") + + resp, err := netutil.HttpPost(apiUrl, header, nil, postData) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPut + +

发送http put请求。

+ +> ⚠️ 本函数已弃用,使用`SendRequest`代替。 + +函数签名: + +```go +// params[0] http请求header,类型必须是http.Header或者map[string]string +// params[1] http查询字符串,类型必须是url.Values或者map[string]string +// params[2] post请求体,类型必须是[]byte +// params[3] http client,类型必须是http.Client +func HttpPut(url string, params ...any) (*http.Response, error) +``` + +示例: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + } + todo := Todo{1, 1, "TestPutToDo"} + bodyParams, _ := json.Marshal(todo) + + resp, err := netutil.HttpPut(url, header, nil, bodyParams) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpDelete + +

发送http delete请求。

+ +> ⚠️ 本函数已弃用,使用`SendRequest`代替。 + +函数签名: + +```go +// params[0] http请求header,类型必须是http.Header或者map[string]string +// params[1] http查询字符串,类型必须是url.Values或者map[string]string +// params[2] post请求体,类型必须是[]byte +// params[3] http client,类型必须是http.Client +func HttpDelete(url string, params ...any) (*http.Response, error) +``` + +示例: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + resp, err := netutil.HttpDelete(url) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPatch + +

发送http patch请求。

+ +> ⚠️ 本函数已弃用,使用`SendRequest`代替。 + +函数签名: + +```go +// params[0] http请求header,类型必须是http.Header或者map[string]string +// params[1] http查询字符串,类型必须是url.Values或者map[string]string +// params[2] post请求体,类型必须是[]byte +// params[3] http client,类型必须是http.Client +func HttpPatch(url string, params ...any) (*http.Response, error) +``` + +示例: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + } + todo := Todo{1, 1, "TestPatchToDo"} + bodyParams, _ := json.Marshal(todo) + + resp, err := netutil.HttpPatch(url, header, nil, bodyParams) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### ParseHttpResponse + +

将http请求响应解码成特定struct值

+ +函数签名: + +```go +func ParseHttpResponse(resp *http.Response, obj any) error +``` + +示例: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + + resp, err := netutil.HttpGet(url, header) + if err != nil { + log.Fatal(err) + } + + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + toDoResp := &Todo{} + err = netutil.ParseHttpResponse(resp, toDoResp) + if err != nil { + log.Fatal(err) + } + + fmt.Println(toDoResp) +} +``` + +### DownloadFile + +

从指定的server地址下载文件。

+ +函数签名: + +```go +func DownloadFile(filepath string, url string) error +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + err := netutil.DownloadFile("./lancet_logo.jpg", "https://picx.zhimg.com/v2-fc82a4199749de9cfb71e32e54f489d3_720w.jpg?source=172ae18b") + + fmt.Println(err) +} +``` + +### UploadFile + +

将文件上传指定的server地址。

+ +函数签名: + +```go +func UploadFile(filepath string, server string) (bool, error) +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ok, err := netutil.UploadFile("./a.jpg", "http://www.xxx.com/bucket/test") + + fmt.Println(ok) + fmt.Println(err) +} +``` + +### IsPingConnected + +

检查能否ping通主机。

+ +函数签名: + +```go +func IsPingConnected(host string) bool +``` + +示例:[运行](https://go.dev/play/p/q8OzTijsA87) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + result1 := netutil.IsPingConnected("www.baidu.com") + result2 := netutil.IsPingConnected("www.!@#&&&.com") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsTelnetConnected + +

检查能否telnet到主机。

+ +函数签名: + +```go +func IsTelnetConnected(host string, port string) bool +``` + +示例:[运行](https://go.dev/play/p/yiLCGtQv_ZG) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + result1 := netutil.IsTelnetConnected("www.baidu.com", "80") + result2 := netutil.IsTelnetConnected("www.baidu.com", "123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### BuildUrl + +

创建url字符串。

+ +函数签名: + +```go +func BuildUrl(scheme, host, path string, query map[string][]string) (string, error) +``` + +示例:[运行](https://go.dev/play/p/JLXl1hZK7l4) + +```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 + // +} +``` + +### AddQueryParams + +

向url添加查询参数。

+ +函数签名: + +```go +func AddQueryParams(urlStr string, params map[string][]string) (string, error) +``` + +示例:[运行](https://go.dev/play/p/JLXl1hZK7l4) + +```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 + // +} +``` diff --git a/docs/api/packages/pointer.md b/docs/api/packages/pointer.md new file mode 100644 index 00000000..e6f1a062 --- /dev/null +++ b/docs/api/packages/pointer.md @@ -0,0 +1,227 @@ +# Pointer + +pointer 包支持一些指针类型的操作。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/pointer/pointer.go](https://github.com/duke-git/lancet/blob/main/pointer/pointer.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/pointer" +) +``` + +
+ +## 目录 + +- [Of](#Of) +- [Unwrap](#Unwrap) +- [ExtractPointer](#ExtractPointer) +- [UnwrapOr](#UnwrapOr) +- [UnwrapOrDefault](#UnwrapOrDefault) + +
+ +## 文档 + +### Of + +

返回传入参数的指针值。

+ +函数签名: + +```go +func Of[T any](v T) *T +``` + +示例:[运行](https://go.dev/play/p/HFd70x4DrMj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + result1 := pointer.Of(123) + result2 := pointer.Of("abc") + + fmt.Println(*result1) + fmt.Println(*result2) + + // Output: + // 123 + // abc +} +``` + +### Unwrap + +

返回传入指针指向的值。

+ +函数签名: + +```go +func Unwrap[T any](p *T) T +``` + +示例:[运行](https://go.dev/play/p/cgeu3g7cjWb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + result1 := pointer.Unwrap(&a) + result2 := pointer.Unwrap(&b) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 123 + // abc +} +``` + +### ExtractPointer + +

返回传入interface的底层值。

+ +函数签名: + +```go +func ExtractPointer(value any) any +``` + +示例:[运行](https://go.dev/play/p/D7HFjeWU2ZP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 1 + b := &a + c := &b + d := &c + + result := pointer.ExtractPointer(d) + + fmt.Println(result) + + // Output: + // 1 +} +``` + +### UnwrapOr + +

返回指针的值,如果指针为零值,则返回fallback。

+ +函数签名: + +```go +UnwrapOr[T any](p *T, fallback T) T +``` + +示例:[运行](https://go.dev/play/p/mmNaLC38W8C) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := pointer.UnwrapOr(&a, 456) + result2 := pointer.UnwrapOr(&b, "abc") + result3 := pointer.UnwrapOr(c, 456) + result4 := pointer.UnwrapOr(d, "def") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 456 + // def +} +``` + +### UnwrapOrDefault + +

返回指针的值,如果指针为零值,则返回相应零值。

+ +函数签名: + +```go +UnwrapOrDefault[T any](p *T) T +``` + +示例:[运行](https://go.dev/play/p/ZnGIHf8_o4E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := pointer.UnwrapOrDefault(&a) + result2 := pointer.UnwrapOrDefault(&b) + result3 := pointer.UnwrapOrDefault(c) + result4 := pointer.UnwrapOrDefault(d) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 0 + // +} +``` diff --git a/docs/api/packages/random.md b/docs/api/packages/random.md new file mode 100644 index 00000000..3c2aa83b --- /dev/null +++ b/docs/api/packages/random.md @@ -0,0 +1,551 @@ +# Random + +random 随机数生成器包,可以生成随机[]bytes, int, string。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/random" +) +``` + +
+ +## 目录 + +- [RandBytes](#RandBytes) +- [RandInt](#RandInt) +- [RandString](#RandString) +- [RandFromGivenSlice](#RandFromGivenSlice) +- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice) +- [RandUpper](#RandUpper) +- [RandLower](#RandLower) +- [RandNumeral](#RandNumeral) +- [RandNumeralOrLetter](#RandNumeralOrLetter) +- [RandSymbolChar](#RandSymbolChar) +- [UUIdV4](#UUIdV4) +- [RandIntSlice](#RandIntSlice) +- [RandUniqueIntSlice](#RandUniqueIntSlice) +- [RandFloat](#RandFloat) +- [RandFloats](#RandFloats) +- [RandStringSlice](#RandStringSlice) +- [RandBool](#RandBool) +- [RandBoolSlice](#RandBoolSlice) +- [RandNumberOfLength](#RandNumberOfLength) + + +
+ +## 文档 + +### RandBytes + +

生成随机字节切片

+ +函数签名: + +```go +func RandBytes(length int) []byte +``` + +示例:[运行](https://go.dev/play/p/EkiLESeXf8d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randBytes := random.RandBytes(4) + fmt.Println(randBytes) +} +``` + +### RandInt + +

生成随机int, 范围[min, max)

+ +函数签名: + +```go +func RandInt(min, max int) int +``` + +示例:[运行](https://go.dev/play/p/pXyyAAI5YxD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + rInt := random.RandInt(1, 10) + fmt.Println(rInt) +} +``` + +### RandString + +

生成给定长度的随机字符串,只包含字母(a-zA-Z)

+ +函数签名: + +```go +func RandString(length int) string +``` + +示例:[运行](https://go.dev/play/p/W2xvRUXA7Mi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandString(6) + fmt.Println(randStr) //pGWsze +} +``` + +### RandFromGivenSlice + +

从给定切片中随机生成元素。

+ +函数签名: + +```go +func RandFromGivenSlice[T any](slice []T) T +``` + +示例:[运行](https://go.dev/play/p/UrkWueF6yYo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + nicknames := []string{"张三", "李四", "王五", "赵六", "钱七"} + randElm := random.RandFromGivenSlice(nicknames) + fmt.Println(randElm) +} +``` + +### RandSliceFromGivenSlice + +

从给定切片中生成长度为 num 的随机切片。

+ +函数签名: + +```go +func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T +``` + +示例:[运行](https://go.dev/play/p/68UikN9d6VT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon","mango", "nectarine", "orange"} + + chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false) + + fmt.Println(chosen3goods) +} +``` + +### RandUpper + +

生成给定长度的随机大写字母字符串

+ +函数签名: + +```go +func RandUpper(length int) string +``` + +示例:[运行](https://go.dev/play/p/29QfOh0DVuh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandString(6) + fmt.Println(randStr) //PACWGF +} +``` + +### RandLower + +

生成给定长度的随机小写字母字符串

+ +函数签名: + +```go +func RandLower(length int) string +``` + +示例:[运行](https://go.dev/play/p/XJtZ471cmtI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandLower(6) + fmt.Println(randStr) //siqbew +} +``` + +### RandNumeral + +

生成给定长度的随机数字字符串

+ +函数签名: + +```go +func RandNumeral(length int) string +``` + +示例:[运行](https://go.dev/play/p/g4JWVpHsJcf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandNumeral(6) + fmt.Println(randStr) //035172 +} +``` + +### RandNumeralOrLetter + +

生成给定长度的随机字符串(数字+字母)

+ +函数签名: + +```go +func RandNumeralOrLetter(length int) string +``` + +示例:[运行](https://go.dev/play/p/19CEQvpx2jD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandNumeralOrLetter(6) + fmt.Println(randStr) //0aW7cQ +} +``` + +### RandSymbolChar + +

生成给定长度的随机符号字符串。

+ +函数签名: + +```go +func RandSymbolChar(length int) string +``` + +示例:[运行](https://go.dev/play/p/Im6ZJxAykOm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandSymbolChar(6) + fmt.Println(randStr) // 随机特殊字符字符串,例如: @#(_") +} +``` + +### UUIdV4 + +

生成UUID v4字符串

+ +函数签名: + +```go +func UUIdV4() (string, error) +``` + +示例:[运行](https://go.dev/play/p/_Z9SFmr28ft) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + uuid, err := random.UUIdV4() + if err != nil { + return + } + fmt.Println(uuid) +} +``` + +### RandIntSlice + +

生成一个特定长度的随机int切片,数值范围[min, max)。

+ +函数签名: + +```go +func RandIntSlice(length, min, max int) []int +``` + +示例:[运行](https://go.dev/play/p/GATTQ5xTEG8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandIntSlice(5, 0, 10) + fmt.Println(result) //[1 2 7 1 5] (random) +} +``` + +### RandUniqueIntSlice + +

生成一个特定长度的,数值不重复的随机int切片,数值范围[min, max)。

+ +函数签名: + +```go +func RandUniqueIntSlice(length, min, max int) []int +``` + +示例:[运行](https://go.dev/play/p/uBkRSOz73Ec) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandUniqueIntSlice(5, 0, 10) + fmt.Println(result) //[0 4 7 1 5] (random) +} +``` + +### RandFloat + +

生成一个随机float64数值,可以指定精度。数值范围[min, max)。

+ +函数签名: + +```go +func RandFloat(min, max float64, precision int) float64 +``` + +实例:[运行](https://go.dev/play/p/zbD_tuobJtr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + floatNumber := random.RandFloat(1.0, 5.0, 2) + fmt.Println(floatNumber) //2.14 (random number) +} +``` + +### RandFloats + +

生成一个特定长度的随机float64切片,可以指定数值精度。数值范围[min, max)。

+ +函数签名: + +```go +func RandFloats(n int, min, max float64, precision int) []float64 +``` + +实例:[运行](https://go.dev/play/p/I3yndUQ-rhh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + floatNumbers := random.RandFloats(5, 1.0, 5.0, 2) + fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random) +} +``` + +### RandStringSlice + +

生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。

+ +函数签名: + +```go +func RandStringSlice(charset string, sliceLen, strLen int) []string +``` + +实例:[运行](https://go.dev/play/p/2_-PiDv3tGn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + strs := random.RandStringSlice(random.Letters, 4, 6) + fmt.Println(strs) + + // output random string slice like below: + //[CooSMq RUFjDz FAeMPf heRyGv] +} +``` + +### RandBool + +

生成随机bool值(true or false)。

+ +函数签名: + +```go +func RandBool() bool +``` + +实例:[运行](https://go.dev/play/p/to6BLc26wBv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandBool() + fmt.Println(result) // true or false (random) +} +``` + +### RandBoolSlice + +

生成特定长度的随机bool slice。

+ +函数签名: + +```go +func RandBoolSlice(length int) []bool +``` + +实例:[运行](https://go.dev/play/p/o-VSjPjnILI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandBoolSlice(2) + fmt.Println(result) // [true false] (random) +} +``` +### RandNumberOfLength + +

生成指定长度的随机数。

+ +函数签名: + +```go +func RandNumberOfLength(len int) int +``` + +实例:[运行](https://go.dev/play/p/oyZbuV7bu7b) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + i := random.RandNumberOfLength(2) + fmt.Println(i) // 21 (random number of length 2) +} +``` \ No newline at end of file diff --git a/docs/api/packages/retry.md b/docs/api/packages/retry.md new file mode 100644 index 00000000..165bd737 --- /dev/null +++ b/docs/api/packages/retry.md @@ -0,0 +1,418 @@ +# Retry + +retry 重试执行函数直到函数运行成功或被 context cancel。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/retry/retry.go](https://github.com/duke-git/lancet/blob/main/retry/retry.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/retry" +) +``` + +
+ +## 目录 + +- [Context](#Context) +- [Retry](#Retry) +- [RetryFunc](#RetryFunc) +- [RetryDuration](#RetryDuration) +- [RetryTimes](#RetryTimes) +- [BackoffStrategy](#BackoffStrategy) +- [RetryWithCustomBackoff](#RetryWithCustomBackoff) +- [RetryWithLinearBackoff](#RetryWithLinearBackoff) +- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff) + +
+ + +## 文档 + +### Context + +

设置重试context参数

+ +函数签名: + +```go +func Context(ctx context.Context) +``` + +示例:[运行](https://go.dev/play/p/xnAOOXv9GkS) + +```go +import ( + "context" + "errors" + "fmt" + "lancet-demo/retry" + "time" +) + +func main() { + ctx, cancel := context.WithCancel(context.TODO()) + + number := 0 + increaseNumber := func() error { + number++ + if number > 3 { + cancel() + } + return errors.New("error occurs") + } + + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) + + retry.Retry(increaseNumber, + duration, + retry.Context(ctx), + ) + + fmt.Println(number) + + // Output: + // 4 +} +``` + +### RetryFunc + +

被重试执行的函数

+ +函数签名: + +```go +type RetryFunc func() error +``` + +示例:[运行](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + var increaseNumber retry.RetryFunc = func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) + + err := retry.Retry(increaseNumber, duration) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### RetryTimes + +

设置重试次数,默认5

+ +函数签名: + +```go +func RetryTimes(n uint) +``` + +示例:[运行](https://go.dev/play/p/ssfVeU2SwLO) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryTimes(2)) + if err != nil { + fmt.Println(err) + } + + // Output: + // function main.main.func1 run failed after 2 times retry +} +``` + +### Retry + +

重试执行函数retryFunc,直到函数运行成功,或被context停止

+ +函数签名: + +```go +func Retry(retryFunc RetryFunc, opts ...Option) error +``` + +示例:[运行](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) + + err := retry.Retry(increaseNumber, duration) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### BackoffStrategy + +

定义计算退避间隔的方法的接口。

+ +函数签名: + +```go +// BackoffStrategy is an interface that defines a method for calculating backoff intervals. +type BackoffStrategy interface { + // CalculateInterval returns the time.Duration after which the next retry attempt should be made. + CalculateInterval() time.Duration +} +``` + +示例: + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +type ExampleCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50})) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### RetryWithCustomBackoff + +

设置自定义退避策略。

+ +函数签名: + +```go +func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option +``` + +示例:[运行](https://go.dev/play/p/jIm_o2vb5Y4) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +type ExampleCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50})) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + + +### RetryWithLinearBackoff + +

设置线性策略退避。

+ +函数签名: + +```go +func RetryWithLinearBackoff(interval time.Duration) Option +``` + +示例:[运行](https://go.dev/play/p/PDet2ZQZwcB) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + + +### RetryWithExponentialWithJitterBackoff + +

设置指数策略退避。

+ +函数签名: + +```go +func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option +``` + +示例:[运行](https://go.dev/play/p/xp1avQmn16X) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` diff --git a/docs/api/packages/slice.md b/docs/api/packages/slice.md new file mode 100644 index 00000000..7787c3d5 --- /dev/null +++ b/docs/api/packages/slice.md @@ -0,0 +1,3164 @@ +# Slice + +slice 包包含操作切片的方法集合。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go) +- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/slice" +) +``` + +
+ +## 目录 + +- [AppendIfAbsent](#AppendIfAbsent) +- [Contain](#Contain) +- [ContainBy](#ContainBy) +- [ContainSubSlice](#ContainSubSlice) +- [Chunk](#Chunk) +- [Compact](#Compact) +- [Concat](#Concat) +- [Count](#Count) +- [CountBy](#CountBy) +- [Difference](#Difference) +- [DifferenceBy](#DifferenceBy) +- [DifferenceWith](#DifferenceWith) +- [DeleteAt](#DeleteAt) +- [DeleteRange](#DeleteRange) +- [Drop](#Drop) +- [DropRight](#DropRight) +- [DropWhile](#DropWhile) +- [DropRightWhile](#DropRightWhile) +- [Every](#Every) +- [Equal](#Equal) +- [EqualWith](#EqualWith) +- [EqualUnordered](#EqualUnordered) +- [Filter](#Filter) +- [FilterConcurrent](#FilterConcurrent) +- [Finddeprecated](#Find) +- [FindBy](#FindBy) +- [FindLastdeprecated](#FindLast) +- [FindLastBy](#FindLastBy) +- [Flatten](#Flatten) +- [FlattenDeep](#FlattenDeep) +- [ForEach](#ForEach) +- [ForEachConcurrent](#ForEachConcurrent) +- [ForEachWithBreak](#ForEachWithBreak) +- [GroupBy](#GroupBy) +- [GroupWith](#GroupWith) +- [IntSlicedeprecated](#IntSlice) +- [InterfaceSlicedeprecated](#InterfaceSlice) +- [Intersection](#Intersection) +- [InsertAt](#InsertAt) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [Map](#Map) +- [MapConcurrent](#MapConcurrent) +- [FilterMap](#FilterMap) +- [FlatMap](#FlatMap) +- [Merge](#Merge) +- [Reverse](#Reverse) +- [ReverseCopy](#ReverseCopy) +- [Reducedeprecated](#Reduce) +- [ReduceConcurrent](#ReduceConcurrent) +- [ReduceBy](#ReduceBy) +- [ReduceRight](#ReduceRight) +- [Replace](#Replace) +- [ReplaceAll](#ReplaceAll) +- [Repeat](#Repeat) +- [Shuffle](#Shuffle) +- [ShuffleCopy](#ShuffleCopy) +- [IsAscending](#IsAscending) +- [IsDescending](#IsDescending) +- [IsSorted](#IsSorted) +- [IsSortedByKey](#IsSortedByKey) +- [Sort](#Sort) +- [SortBy](#SortBy) +- [SortByField](#SortByField) +- [Some](#Some) +- [StringSlicedeprecated](#StringSlice) +- [SymmetricDifference](#SymmetricDifference) +- [ToSlice](#ToSlice) +- [ToSlicePointer](#ToSlicePointer) +- [Unique](#Unique) +- [UniqueBy](#UniqueBy) +- [UniqueByComparator](#UniqueByComparator) +- [UniqueByField](#UniqueByField) +- [UniqueByConcurrent](#UniqueByConcurrent) +- [Union](#Union) +- [UnionBy](#UnionBy) +- [UpdateAt](#UpdateAt) +- [Without](#Without) +- [KeyBy](#KeyBy) +- [Join](#Join) +- [Partition](#Partition) +- [SetToDefaultIf](#SetToDefaultIf) +- [Break](#Break) +- [RightPadding](#RightPadding) +- [LeftPadding](#LeftPadding) +- [Frequency](#Frequency) +- [JoinFunc](#JoinFunc) +- [ConcatBy](#ConcatBy) + + +
+ + +## 文档 + +### AppendIfAbsent + +

当前切片中不包含值时,将该值追加到切片中

+ +函数签名: + +```go +func AppendIfAbsent[T comparable](slice []T, item T) []T +``` + +示例:[运行](https://go.dev/play/p/GNdv7Jg2Taj) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.AppendIfAbsent([]string{"a", "b"}, "b") + result2 := slice.AppendIfAbsent([]string{"a", "b"}, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b] + // [a b c] +} +``` + +### Contain + +

判断slice是否包含value

+ +函数签名: + +```go +func Contain[T comparable](slice []T, target T) bool +``` + +示例:[运行](https://go.dev/play/p/_454yEHcNjf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Contain([]string{"a", "b", "c"}, "a") + result2 := slice.Contain([]int{1, 2, 3}, 4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### ContainBy + +

根据predicate函数判断切片是否包含某个值。

+ +函数签名: + +```go +func ContainBy[T any](slice []T, predicate func(item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/49tkHfX4GNc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type foo struct { + A string + B int + } + + array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}} + result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 }) + result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 }) + + array2 := []string{"a", "b", "c"} + result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" }) + result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // false + // true + // false +} +``` + +### ContainSubSlice + +

判断slice是否包含subslice

+ +函数签名: + +```go +func ContainSubSlice[T comparable](slice, subSlice []T) bool +``` + +示例:[运行](https://go.dev/play/p/bcuQ3UT6Sev) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"}) + result2 := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### Chunk + +

按照size参数均分slice

+ +函数签名: + +```go +func Chunk[T any](slice []T, size int) [][]T +``` + +示例:[运行](https://go.dev/play/p/b4Pou5j2L_C) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arr := []string{"a", "b", "c", "d", "e"} + + result1 := slice.Chunk(arr, 1) + result2 := slice.Chunk(arr, 2) + result3 := slice.Chunk(arr, 3) + result4 := slice.Chunk(arr, 4) + result5 := slice.Chunk(arr, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [[a] [b] [c] [d] [e]] + // [[a b] [c d] [e]] + // [[a b c] [d e]] + // [[a b c d] [e]] + // [[a b c d e]] +} +``` + +### Compact + +

去除slice中的假值(false values are false, nil, 0, "")

+ +函数签名: + +```go +func Compact[T comparable](slice []T) []T +``` + +示例:[运行](https://go.dev/play/p/pO5AnxEr3TK) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Compact([]int{0}) + result2 := slice.Compact([]int{0, 1, 2, 3}) + result3 := slice.Compact([]string{"", "a", "b", "0"}) + result4 := slice.Compact([]bool{false, true, true}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [] + // [1 2 3] + // [a b 0] + // [true true] +} +``` + +### Concat + +

创建一个新的切片,将传入的切片拼接起来返回。

+ +函数签名: + +```go +func Concat[T any](slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/gPt-q7zr5mk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Concat([]int{1, 2}, []int{3, 4}) + result2 := slice.Concat([]string{"a", "b"}, []string{"c"}, []string{"d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [1 2 3 4] + // [a b c d] +} +``` + +### Count + +

返回切片中指定元素的个数

+ +函数签名: + +```go +func Count[T comparable](slice []T, item T) int +``` + +示例:[运行](https://go.dev/play/p/Mj4oiEnQvRJ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 3, 4} + + result1 := slice.Count(nums, 1) + result2 := slice.Count(nums, 3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 2 +} +``` + +### CountBy + +

遍历切片,对每个元素执行函数predicate. 返回符合函数返回值为true的元素的个数.

+ +函数签名: + +```go +func CountBy[T any](slice []T, predicate func(index int, item T) bool) int +``` + +示例:[运行](https://go.dev/play/p/tHOccTMDZCC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.CountBy(nums, isEven) + + fmt.Println(result) + + // Output: + // 2 +} +``` + +### Difference + +

创建一个切片,其元素不包含在另一个给定切片中

+ +函数签名: + +```go +func Difference[T comparable](slice, comparedSlice []T) []T +``` + +示例:[运行](https://go.dev/play/p/VXvadzLzhDa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{4, 5, 6} + + result := slice.Difference(s1, s2) + + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### DifferenceBy + +

将两个slice中的每个元素调用iteratee函数,并比较它们的返回值,如果不相等返回在slice中对应的值

+ +函数签名: + +```go +func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(index int, item T) T) []T +``` + +示例:[运行](https://go.dev/play/p/DiivgwM5OnC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{3, 4, 5} + + addOne := func(i int, v int) int { + return v + 1 + } + + result := slice.DifferenceBy(s1, s2, addOne) + + fmt.Println(result) + + // Output: + // [1 2] +} +``` + +### DifferenceWith + +

接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定

+ +函数签名: + +```go +func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(value, otherValue T) bool) []T +``` + +示例:[运行](https://go.dev/play/p/v2U2deugKuV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{4, 5, 6, 7, 8} + + isDouble := func(v1, v2 int) bool { + return v2 == 2*v1 + } + + result := slice.DifferenceWith(s1, s2, isDouble) + + fmt.Println(result) + + // Output: + // [1 5] +} +``` + +### DeleteAt + +

删除切片中指定索引的元素(不修改原切片)。

+ +函数签名: + +```go +func DeleteAt[T any](slice []T, index int) []T +``` + +示例:[运行](https://go.dev/play/p/800B1dPBYyd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := slice.DeleteAt(chars, 0) + result2 := slice.DeleteAt(chars, 4) + result3 := slice.DeleteAt(chars, 5) + result4 := slice.DeleteAt(chars, 6) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [b c d e] + // [a b c d] + // [a b c d] + // [a b c d] + +} +``` + +### DeleteRange + +

删除切片中指定索引范围的元素(不修改原切片)。

+ +函数签名: + +```go +func DeleteRange[T any](slice []T, start, end int) []T +``` + +示例:[运行](https://go.dev/play/p/945HwiNrnle) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := DeleteRange(chars, 0, 0) + result2 := DeleteRange(chars, 0, 1) + result3 := DeleteRange(chars, 0, 3) + result4 := DeleteRange(chars, 0, 4) + result5 := DeleteRange(chars, 0, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c d e] + // [b c d e] + // [d e] + // [e] + // [] + +} +``` + +### Drop + +

从切片的头部删除n个元素。

+ +函数签名: + +```go +func Drop[T any](slice []T, n int) []T +``` + +示例:[运行](https://go.dev/play/p/jnPO2yQsT8H) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Drop([]string{"a", "b", "c"}, 0) + result2 := slice.Drop([]string{"a", "b", "c"}, 1) + result3 := slice.Drop([]string{"a", "b", "c"}, -1) + result4 := slice.Drop([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [b c] + // [a b c] + // [] +} +``` + +### DropRight + +

从切片的尾部删除n个元素。

+ +函数签名: + +```go +func DropRight[T any](slice []T, n int) []T +``` + +示例:[运行](https://go.dev/play/p/8bcXvywZezG) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.DropRight([]string{"a", "b", "c"}, 0) + result2 := slice.DropRight([]string{"a", "b", "c"}, 1) + result3 := slice.DropRight([]string{"a", "b", "c"}, -1) + result4 := slice.DropRight([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [a b] + // [a b c] + // [] +} +``` + +### DropWhile + +

从切片的头部删除n个元素,这个n个元素满足predicate函数返回true。

+ +函数签名: + +```go +func DropWhile[T any](slice []T, predicate func(item T) bool) []T +``` + +示例:[运行](https://go.dev/play/p/4rt252UV_qs) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.DropWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := slice.DropWhile(numbers, func(n int) bool { + return true + }) + result3 := slice.DropWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [2 3 4 5] + // [] + // [1 2 3 4 5] +} +``` + +### DropRightWhile + +

从切片的尾部删除n个元素,这个n个元素满足predicate函数返回true。

+ +函数签名: + +```go +func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T +``` + +示例:[运行](https://go.dev/play/p/6wyK3zMY56e) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 2, 3, 4, 5} + + result1 := slice.DropRightWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := slice.DropRightWhile(numbers, func(n int) bool { + return true + }) + result3 := slice.DropRightWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [1 2] + // [] + // [1 2 3 4 5] +} +``` + +### Every + +

如果切片中的所有值都通过谓词函数,则返回true。 函数签名应该是func(index int, value any) bool

+ +函数签名: + +```go +func Every[T any](slice []T, predicate func(index int, item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/R8U6Sl-j8cD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Every(nums, isEven) + + fmt.Println(result) + + // Output: + // false +} +``` + +### Equal + +

检查两个切片是否相等,相等条件:切片长度相同,元素顺序和值都相同

+ +函数签名: + +```go +func Equal[T comparable](slice1, slice2 []T) bool +``` + +示例:[运行](https://go.dev/play/p/WcRQJ37ifPa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3} + s2 := []int{1, 2, 3} + s3 := []int{1, 3, 2} + + result1 := slice.Equal(s1, s2) + result2 := slice.Equal(s1, s3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### EqualWith + +

检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数comparator,返回true

+ +函数签名: + +```go +func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) bool +``` + +示例:[运行](https://go.dev/play/p/b9iygtgsHI1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3} + s2 := []int{2, 4, 6} + + isDouble := func(a, b int) bool { + return b == a*2 + } + + result := slice.EqualWith(s1, s2, isDouble) + + fmt.Println(result) + + // Output: + // true +} +``` + +### EqualUnordered + +

检查两个切片是否相等,元素数量相同,值相等,不考虑元素顺序。

+ +函数签名: + +```go +func EqualUnordered[T comparable](slice1, slice2 []T) bool +``` + +示例:[运行](https://go.dev/play/p/n8fSc2w8ZgX) + +```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 +} +``` + +### Filter + +

返回切片中通过predicate函数真值测试的所有元素

+ +函数签名: + +```go +func Filter[T any](slice []T, predicate func(index int, item T) bool) []T +``` + +示例:[运行](https://go.dev/play/p/SdPna-7qK4T) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Filter(nums, isEven) + + fmt.Println(result) + + // Output: + // [2 4] +} +``` + +### FilterConcurrent + +

对slice并发执行filter操作。

+ +函数签名: + +```go +func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T +``` + +示例:[运行](https://go.dev/play/p/t_pkwerIRVx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.FilterConcurrent(nums, isEven, 2) + + fmt.Println(result) + + // Output: + // [2 4] +} +``` + +### Find + +

遍历slice的元素,返回第一个通过predicate函数真值测试的元素

+ +> ⚠️ 本函数已弃用,使用`FindBy`代替。 + +函数签名: + +```go +func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) +``` + +示例:[运行](https://go.dev/play/p/CBKeBoHVLgq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.Find(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 2 + // true +} +``` + +### FindBy + +

遍历slice的元素,返回第一个通过predicate函数真值测试的元素

+ +函数签名: + +```go +func FindBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) +``` + +示例:[运行](https://go.dev/play/p/n1lysBYl-GB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 2 + // true +} +``` + +### FindLast + +

遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。

+ +> ⚠️ 本函数已弃用,使用`FindLastBy`代替。 + +函数签名: + +```go +func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) +``` + +示例:[运行](https://go.dev/play/p/FFDPV_j7URd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindLast(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### FindLastBy + +

从遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。

+ +函数签名: + +```go +func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) +``` + +示例:[运行](https://go.dev/play/p/8iqomzyCl_s) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindLastBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### Flatten + +

将切片压平一层

+ +函数签名: + +```go +func Flatten(slice any) any +``` + +示例:[运行](https://go.dev/play/p/hYa3cBEevtm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := slice.Flatten(arrs) + + fmt.Println(result) + + // Output: + // [[a b] [c d]] +} +``` + +### FlattenDeep + +

flattens slice recursive.

+ +函数签名: + +```go +func FlattenDeep(slice any) any +``` + +示例:[运行](https://go.dev/play/p/yjYNHPyCFaF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := slice.FlattenDeep(arrs) + + fmt.Println(result) + + // Output: + // [a b c d] +} +``` + +### ForEach + +

遍历切片的元素并为每个元素调用iteratee函数

+ +函数签名: + +```go +func ForEach[T any](slice []T, iteratee func(index int, item T)) +``` + +示例:[运行](https://go.dev/play/p/DrPaa4YsHRF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + var result []int + addOne := func(_ int, v int) { + result = append(result, v+1) + } + + slice.ForEach(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} +``` + +### ForEachConcurrent + +

对slice并发执行foreach操作。

+ +函数签名: + +```go +func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) +``` + +示例:[运行](https://go.dev/play/p/kT4XW7DKVoV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8} + + result := make([]int, len(nums)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + slice.ForEachConcurrent(nums, addOne, 4) + + fmt.Println(result) + + // Output: + // [2 3 4 5 6 7 8 9] +} +``` + + +### ForEachWithBreak + +

遍历切片的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。

+ +函数签名: + +```go +func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) +``` + +示例:[运行](https://go.dev/play/p/qScs39f3D9W) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 2, 3, 4, 5} + + var sum int + + slice.ForEachWithBreak(numbers, func(_, n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + fmt.Println(sum) + + // Output: + // 6 +} +``` + +### GroupBy + +

迭代切片的元素,每个元素将按条件分组,返回两个切片

+ +函数签名: + +```go +func GroupBy[T any](slice []T, groupFn func(index int, item T) bool) ([]T, []T) +``` + +示例:[运行](https://go.dev/play/p/QVkPxzPR0iA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + even, odd := slice.GroupBy(nums, isEven) + + fmt.Println(even) + fmt.Println(odd) + + // Output: + // [2 4] + // [1 3 5] +} +``` + +### GroupWith + +

创建一个map,key是iteratee遍历slice中的每个元素返回的结果。 分组值的顺序是由他们出现在slice中的顺序确定的。每个键对应的值负责生成key的元素组成的数组。iteratee调用1个参数: (value)

+ +函数签名: + +```go +func GroupWith[T any, U comparable](slice []T, iteratee func(T) U) map[U][]T +``` + +示例:[运行](https://go.dev/play/p/ApCvMNTLO8a) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []float64{6.1, 4.2, 6.3} + + floor := func(num float64) float64 { + return math.Floor(num) + } + + result := slice.GroupWith(nums, floor) //map[float64][]float64 + + fmt.Println(result) + + // Output: + // map[4:[4.2] 6:[6.1 6.3]] +} +``` + +### IntSlice + +

将接口切片转换为int切片

+ +> ⚠️ 本函数已弃用,使用go1.18+泛型代替。 + +函数签名: + +```go +func IntSlice(slice any) []int +``` + +示例:[运行](https://go.dev/play/p/UQDj-on9TGN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []interface{}{1, 2, 3} + + result := slice.IntSlice(nums) //[]int{1, 2, 3} + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### InterfaceSlice + +

将值转换为接口切片

+ +> ⚠️ 本函数已弃用,使用go1.18+泛型代替。 + +函数签名: + +```go +func InterfaceSlice(slice any) []any +``` + +示例:[运行](https://go.dev/play/p/FdQXF0Vvqs-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c"} + + result := slice.InterfaceSlice(strs) //[]interface{}{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### Intersection + +

多个切片的交集

+ +函数签名: + +```go +func Intersection[T comparable](slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/anJXfB5wq_t) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{2, 3, 4} + + result := slice.Intersection(nums1, nums2) + + fmt.Println(result) + + // Output: + // [2 3] +} +``` + +### InsertAt + +

将元素插入到索引处的切片中

+ +函数签名: + +```go +func InsertAt[T any](slice []T, index int, value any) []T +``` + +示例:[运行](https://go.dev/play/p/hMLNxPEGJVE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.InsertAt([]string{"a", "b", "c"}, 0, "1") + result2 := slice.InsertAt([]string{"a", "b", "c"}, 1, "1") + result3 := slice.InsertAt([]string{"a", "b", "c"}, 2, "1") + result4 := slice.InsertAt([]string{"a", "b", "c"}, 3, "1") + result5 := slice.InsertAt([]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [1 a b c] + // [a 1 b c] + // [a b 1 c] + // [a b c 1] + // [1 2 3 a b c] +} +``` + +### IndexOf + +

返回在切片中找到值的第一个匹配项的索引,如果找不到值,则返回-1

+ +函数签名: + +```go +func IndexOf[T comparable](slice []T, item T) int +``` + +示例:[运行](https://go.dev/play/p/MRN1f0FpABb) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "a", "b", "c"} + + result1 := slice.IndexOf(strs, "a") + result2 := slice.IndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} +``` + +### LastIndexOf + +

返回在切片中找到最后一个值的索引,如果找不到该值,则返回-1

+ +函数签名: + +```go +func LastIndexOf[T comparable](slice []T, item T) int +``` + +示例:[运行](https://go.dev/play/p/DokM4cf1IKH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "a", "b", "c"} + + result1 := slice.LastIndexOf(strs, "a") + result2 := slice.LastIndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // -1 +} +``` + +### Map + +

对slice中的每个元素执行map函数以创建一个新切片。

+ +函数签名: + +```go +func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U +``` + +示例:[运行](https://go.dev/play/p/biaTefqPquw) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + addOne := func(_ int, v int) int { + return v + 1 + } + + result := slice.Map(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} +``` + +### MapConcurrent + +

对slice并发执行map操作。

+ +函数签名: + +```go +func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U +``` + +示例:[运行](https://go.dev/play/p/H1ehfPkPen0) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6} + + result := slice.MapConcurrent(nums, func(_, n int) int { + return n * n + }, 4) + + fmt.Println(result) + + // Output: + // [1 4 9 16 25 36] +} +``` + +### FilterMap + +

返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值:1,结果值。2,结果值是否应该被包含在返回的切片中。

+ +函数签名: + +```go +func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U +``` + +示例:[运行](https://go.dev/play/p/J94SZ_9MiIe) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + getEvenNumStr := func(i, num int) (string, bool) { + if num%2 == 0 { + return strconv.FormatInt(int64(num), 10), true + } + return "", false + } + + result := slice.FilterMap(nums, getEvenNumStr) + + fmt.Printf("%#v", result) + + // Output: + // []string{"2", "4"} +} +``` + +### FlatMap + +

将切片转换为其它类型切片。

+ +函数签名: + +```go +func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U +``` + +示例:[运行](https://go.dev/play/p/_QARWlWs1N_F) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4} + + result := slice.FlatMap(nums, func(i int, num int) []string { + s := "hi-" + strconv.FormatInt(int64(num), 10) + return []string{s} + }) + + fmt.Printf("%#v", result) + + // Output: + // []string{"hi-1", "hi-2", "hi-3", "hi-4"} +} +``` + +### Merge + +

合并多个切片(不会消除重复元素).

+ +> ⚠️ 本函数已弃用,使用`Concat`代替。 + +函数签名: + +```go +func Merge[T any](slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/lbjFp784r9N) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{3, 4} + + result := slice.Merge(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 2 3 3 4] +} +``` + +### Reverse + +

反转切片中的元素顺序

+ +函数签名: + +```go +func Reverse[T any](slice []T) +``` + +示例:[运行](https://go.dev/play/p/8uI8f1lwNrQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c", "d"} + + slice.Reverse(strs) + + fmt.Println(strs) + + // Output: + // [d c b a] +} +``` + +### ReverseCopy + +

反转切片中的元素顺序, 不改变原slice。

+ +函数签名: + +```go +func ReverseCopy[T any](slice []T) []T +``` + +示例:[运行](https://go.dev/play/p/c9arEaP7Cg-) + +```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] +} +``` + +### Reduce + +

将切片中的元素依次运行iteratee函数,返回运行结果。

+ +> ⚠️ 本函数已弃用,使用`ReduceBy`代替。 + +函数签名: + +```go +func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T +``` + +示例:[运行](https://go.dev/play/p/_RfXJJWIsIm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + sum := func(_ int, v1, v2 int) int { + return v1 + v2 + } + + result := slice.Reduce(nums, sum, 0) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### ReduceConcurrent + +

对切片元素执行并发reduce操作。

+ +函数签名: + +```go +func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T +``` + +示例:[运行](https://go.dev/play/p/Tjwe6OtaG07) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 1) + + fmt.Println(result) + + // Output: + // 55 +} +``` + +### ReduceBy + +

对切片元素执行reduce操作。

+ +函数签名: + +```go +func ReduceBy[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U +``` + +示例:[运行](https://go.dev/play/p/YKDpLi7gtee) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int { + return agg + item + }) + + result2 := slice.ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1234 +} +``` + +### ReduceRight + +

类似ReduceBy操作,迭代切片元素顺序从右至左。

+ +函数签名: + +```go +func ReduceRight[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U +``` + +示例:[运行](https://go.dev/play/p/qT9dZC03A1K) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ReduceRight([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result) + + // Output: + // 4321 +} +``` + +### Replace + +

返回切片的副本,其中前n个不重叠的old替换为new

+ +函数签名: + +```go +func Replace[T comparable](slice []T, old T, new T, n int) []T +``` + +示例:[运行](https://go.dev/play/p/P5mZp7IhOFo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c", "a"} + + result1 := slice.Replace(strs, "a", "x", 0) + result2 := slice.Replace(strs, "a", "x", 1) + result3 := slice.Replace(strs, "a", "x", 2) + result4 := slice.Replace(strs, "a", "x", 3) + result5 := slice.Replace(strs, "a", "x", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c a] + // [x b c a] + // [x b c x] + // [x b c x] + // [x b c x] +} +``` + +### ReplaceAll + +

返回切片的副本,将其中old全部替换为new

+ +函数签名: + +```go +func ReplaceAll[T comparable](slice []T, old T, new T) []T +``` + +示例:[运行](https://go.dev/play/p/CzqXMsuYUrx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ReplaceAll([]string{"a", "b", "c", "a"}, "a", "x") + + fmt.Println(result) + + // Output: + // [x b c x] +} +``` + +### Repeat + +

创建一个切片,包含n个传入的item

+ +函数签名: + +```go +func Repeat[T any](item T, n int) []T +``` + +示例:[运行](https://go.dev/play/p/1CbOmtgILUU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Repeat("a", 3) + + fmt.Println(result) + + // Output: + // [a a a] +} +``` + +### Shuffle + +

随机打乱切片中的元素顺序。

+ +函数签名: + +```go +func Shuffle[T any](slice []T) []T +``` + +示例:[运行](https://go.dev/play/p/YHvhnWGU3Ge) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + result := slice.Shuffle(nums) + + fmt.Println(res) + + // Output: + // [3 1 5 4 2] (random order) +} +``` + +### ShuffleCopy + +

随机打乱切片中的元素顺序, 不改变原切片。

+ +函数签名: + +```go +func ShuffleCopy[T any](slice []T) []T +``` + +示例:[运行](https://go.dev/play/p/vqDa-Gs1vT0) + +```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] +} +``` + +### IsAscending + +

检查切片元素是否按升序排列。

+ +函数签名: + +```go +func IsAscending[T constraints.Ordered](slice []T) bool +``` + +示例:[运行](https://go.dev/play/p/9CtsFjet4SH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsAscending([]int{1, 2, 3, 4, 5}) + result2 := slice.IsAscending([]int{5, 4, 3, 2, 1}) + result3 := slice.IsAscending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsDescending + +

检查切片元素是否按降序排列。

+ +函数签名: + +```go +func IsDescending[T constraints.Ordered](slice []T) bool +``` + +示例:[运行](https://go.dev/play/p/U_LljFXma14) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsDescending([]int{5, 4, 3, 2, 1}) + result2 := slice.IsDescending([]int{1, 2, 3, 4, 5}) + result3 := slice.IsDescending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsSorted + +

检查切片元素是否是有序的(升序或降序)。

+ +函数签名: + +```go +func IsSorted[T constraints.Ordered](slice []T) bool +``` + +示例:[运行](https://go.dev/play/p/nCE8wPLwSA-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsSorted([]int{5, 4, 3, 2, 1}) + result2 := slice.IsSorted([]int{1, 2, 3, 4, 5}) + result3 := slice.IsSorted([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsSortedByKey + +

通过iteratee函数,检查切片元素是否是有序的。

+ +函数签名: + +```go +func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool +``` + +示例:[运行](https://go.dev/play/p/tUoGB7DOHI4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int { + return len(s) + }) + result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int { + return len(s) + }) + result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int { + return len(s) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### Sort + +

对任何有序类型(数字或字符串)的切片进行排序,使用快速排序算法。 默认排序顺序为升序 (asc),如果需要降序,请将参数 `sortOrder` 设置为 `desc`。 Ordered类型:数字(所有整数浮点数)或字符串。

+ +函数签名: + +```go +func Sort[T constraints.Ordered](slice []T, sortOrder ...string) +``` + +示例:[运行](https://go.dev/play/p/V9AVjzf_4Fk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 4, 3, 2, 5} + + slice.Sort(numbers) + fmt.Println(numbers) // [1 2 3 4 5] + + slice.Sort(numbers, "desc") + fmt.Println(numbers) // [5 4 3 2 1] + + strings := []string{"a", "d", "c", "b", "e"} + + slice.Sort(strings) + fmt.Println(strings) //[a b c d e} + + slice.Sort(strings, "desc") + fmt.Println(strings) //[e d c b a] +} +``` + +### SortBy + +

按照less函数确定的升序规则对切片进行排序。排序不保证稳定性

+ +函数签名: + +```go +func SortBy[T any](slice []T, less func(a, b T) bool) +``` + +示例:[运行](https://go.dev/play/p/DAhLQSZEumm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 4, 3, 2, 5} + + slice.SortBy(numbers, func(a, b int) bool { + return a < b + }) + fmt.Println(numbers) // [1 2 3 4 5] + + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + slice.SortBy(users, func(a, b User) bool { + return a.Age < b.Age + }) + + fmt.Printf("sort users by age: %v", users) + + // output + // [{b 15} {a 21} {c 100}] +} +``` + +### SortByField + +

按字段对结构体切片进行排序。slice元素应为struct,排序字段field类型应为int、uint、string或bool。 默认排序类型是升序(asc),如果是降序,设置 sortType 为 desc

+ +函数签名: + +```go +func SortByField(slice any, field string, sortType ...string) error +``` + +示例:[运行](https://go.dev/play/p/fU1prOBP9p1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + err := slice.SortByField(users, "Age", "desc") + if err != nil { + return + } + + fmt.Println(users) + + // Output: + // [{c 100} {a 21} {b 15}] +} +``` + +### Some + +

如果列表中的任何值通过谓词函数,则返回true

+ +函数签名: + +```go +func Some[T any](slice []T, predicate func(index int, item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/4pO9Xf9NDGS) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Some(nums, isEven) + + fmt.Println(result) + + // Output: + // true +} +``` + +### StringSlice + +

将接口切片转换为字符串切片

+ +> ⚠️ 本函数已弃用,使用go1.18+泛型代替。 + +函数签名: + +```go +func StringSlice(slice any) []string +``` + +示例:[运行](https://go.dev/play/p/W0TZDWCPFcI) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []interface{}{"a", "b", "c"} + + result := slice.StringSlice(strs) //[]string{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### SymmetricDifference + +

返回一个切片,其中的元素存在于参数切片中,但不同时存储在于参数切片中(交集取反)

+ +函数签名: + +```go +func SymmetricDifference[T comparable](slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/1CbOmtgILUU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{1, 2, 4} + + result := slice.SymmetricDifference(nums1, nums2) + + fmt.Println(result) + + // Output: + // [3 4] +} +``` + +### ToSlice + +

将可变参数转为切片

+ +函数签名: + +```go +func ToSlice[T any](items ...T) []T +``` + +示例:[运行](https://go.dev/play/p/YzbzVq5kscN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ToSlice("a", "b", "c") + + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### ToSlicePointer + +

将可变参数转为指针切片

+ +函数签名: + +```go +func ToSlicePointer[T any](items ...T) []*T +``` + +示例:[运行](https://go.dev/play/p/gx4tr6_VXSF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + str1 := "a" + str2 := "b" + + result := slice.ToSlicePointer(str1, str2) + + expect := []*string{&str1, &str2} + + isEqual := reflect.DeepEqual(result, expect) + + fmt.Println(isEqual) + + // Output: + // true +} +``` + +### Unique + +

删除切片中的重复元素

+ +函数签名: + +```go +func Unique[T comparable](slice []T) []T +``` + +示例:[运行](https://go.dev/play/p/AXw0R3ZTE6a) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Unique([]string{"a", "a", "b"}) + fmt.Println(result) + + // Output: + // [a b] +} +``` + +### UniqueBy + +

根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。

+ +函数签名: + +```go +func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T +``` + +示例:[运行](https://go.dev/play/p/GY7JE4yikrl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6} + result := slice.UniqueBy(nums, func(val int) int { + return val % 3 + }) + + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### UniqueByComparator + +

使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。

+ +函数签名: + +```go +func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T +``` + +示例:[运行](https://go.dev/play/p/rwSacr-ZHsR) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool { + return item == other + }) + + caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool { + return strings.ToLower(item) == strings.ToLower(other) + }) + + fmt.Println(uniqueNums) + fmt.Println(caseInsensitiveStrings) + + // Output: + // [1 2 3 4 5 6] + // [apple banana cherry date] +} +``` + +### UniqueByConcurrent + +

并发的从输入切片中移除重复元素,结果保持元素的顺序。

+ +函数签名: + +```go +func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T +``` + +示例:[运行](https://go.dev/play/p/wXZ7LcYRMGL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7} + comparator := func(item int, other int) bool { return item == other } + + result := slice.UniqueByConcurrent(nums, comparator, 4) + + fmt.Println(result) + // Output: + // [1 2 3 4 5 6 7] +} +``` + +### UniqueByField + +

根据struct字段对struct切片去重复。

+ +函数签名: + +```go +func UniqueByField[T any](slice []T, field string) ([]T, error) +``` + +示例:[运行](https://go.dev/play/p/6cifcZSPIGu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + type User struct { + ID int `json:"id"` + Name string `json:"name"` + } + + users := []User{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "b"}, + {ID: 1, Name: "c"}, + } + + result, err := slice.UniqueByField(users, "ID") + if err != nil { + } + + fmt.Println(result) + + // Output: + // [{1 a} {2 b}] +} +``` + +### Union + +

合并多个切片

+ +函数签名: + +```go +func Union[T comparable](slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/hfXV1iRIZOf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 3, 4, 6} + nums2 := []int{1, 2, 5, 6} + + result := slice.Union(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 3 4 6 2 5] +} +``` + +### UnionBy + +

对切片的每个元素调用函数后,合并多个切片

+ +函数签名: + +```go +func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T +``` + +示例:[运行](https://go.dev/play/p/HGKHfxKQsFi) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4} + + divideTwo := func(n int) int { + return n / 2 + } + result := slice.UnionBy(divideTwo, nums) + + fmt.Println(result) + + // Output: + // [1 2 4] +} +``` + +### UpdateAt + +

更新索引处的切片元素。 如果index < 0或 index <= len(slice),将返回错误

+ +函数签名: + +```go +func UpdateAt[T any](slice []T, index int, value T) []T +``` + +示例:[运行](https://go.dev/play/p/f3mh2KloWVm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.UpdateAt([]string{"a", "b", "c"}, -1, "1") + result2 := slice.UpdateAt([]string{"a", "b", "c"}, 0, "1") + result3 := slice.UpdateAt([]string{"a", "b", "c"}, 1, "1") + result4 := slice.UpdateAt([]string{"a", "b", "c"}, 2, "1") + result5 := slice.UpdateAt([]string{"a", "b", "c"}, 3, "1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c] + // [1 b c] + // [a 1 c] + // [a b 1] + // [a b c] +} +``` + +### Without + +

创建一个不包括所有给定值的切片

+ +函数签名: + +```go +func Without[T comparable](slice []T, items ...T) []T +``` + +示例:[运行](https://go.dev/play/p/bwhEXEypThg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Without([]int{1, 2, 3, 4}, 1, 2) + + fmt.Println(result) + + // Output: + // [3 4] +} +``` + +### KeyBy + +

将切片每个元素调用函数后转为map。

+ +函数签名: + +```go +func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T +``` + +示例:[运行](https://go.dev/play/p/uXod2LWD1Kg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.KeyBy([]string{"a", "ab", "abc"}, func(str string) int { + return len(str) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:ab 3:abc] +} +``` + +### Join + +

用指定的分隔符链接切片元素。

+ +函数签名: + +```go +func Join[T any](s []T, separator string) string +``` + +示例:[运行](https://go.dev/play/p/huKzqwNDD7V) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + result1 := slice.Join(nums, ",") + result2 := slice.Join(nums, "-") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1,2,3,4,5 + // 1-2-3-4-5 +} +``` + +### Partition + +

根据给定的predicate判断函数分组切片元素。

+ +函数签名: + +```go +func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T +``` + +示例:[运行](https://go.dev/play/p/lkQ3Ri2NQhV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + result1 := slice.Partition(nums) + result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 }) + result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [[1 2 3 4 5]] + // [[2 4] [1 3 5]] + // [[1 2] [3 4] [5]] +} +``` + + +### Random + +

随机返回切片中元素以及下标, 当切片长度为0时返回下标-1

+ +函数签名: + +```go +func Random[T any](slice []T) (val T, idx int) +``` + +示例:[运行](https://go.dev/play/p/UzpGQptWppw) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + val, idx := slice.Random(nums) + if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) { + fmt.Println("okk") + } + // Output: + // okk +} +``` + +### SetToDefaultIf + +

根据给定给定的predicate判定函数来修改切片中的元素。对于满足的元素,将其替换为指定的默认值,同时保持元素在切片中的位置不变。函数返回修改后的切片以及被修改的元素个数。

+ +函数签名: + +```go +func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) +``` + +示例:[运行](https://go.dev/play/p/9AXGlPRC0-A) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "a", "c", "d", "a"} + modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s }) + + fmt.Println(modifiedStrs) + fmt.Println(count) + + // Output: + // [ b c d ] + // 3 +} +``` + +### Break + +

根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。

+ +函数签名: + +```go +func Break[T any](values []T, predicate func(T) bool) ([]T, []T) +``` + +示例:[运行](https://go.dev/play/p/yLYcBTyeQIz) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + even := func(n int) bool { return n%2 == 0 } + + resultEven, resultAfterFirstEven := slice.Break(nums, even) + + fmt.Println(resultEven) + fmt.Println(resultAfterFirstEven) + + // Output: + // [1] + // [2 3 4 5] +} +``` + +### RightPadding + +

在切片的右部添加元素。

+ +函数签名: + +```go +func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T +``` + +示例:[运行](https://go.dev/play/p/0_2rlLEMBXL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + padded := slice.RightPadding(nums, 0, 3) + fmt.Println(padded) + // Output: + // [1 2 3 4 5 0 0 0] +} +``` + +### LeftPadding + +

在切片的左部添加元素。

+ +函数签名: + +```go +func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T +``` + +示例:[运行](https://go.dev/play/p/jlQVoelLl2k) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + padded := slice.LeftPadding(nums, 0, 3) + fmt.Println(padded) + // Output: + // [0 0 0 1 2 3 4 5] +} +``` + +### Frequency + +

计算切片中每个元素出现的频率。

+ +函数签名: + +```go +func Frequency[T comparable](slice []T) map[T]int +``` + +示例:[运行](https://go.dev/play/p/CW3UVNdUZOq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "b", "c", "c", "c"} + result := slice.Frequency(strs) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### JoinFunc + +

将切片元素用给定的分隔符连接成一个单一的字符串。

+ +函数签名: + +```go +func JoinFunc[T any](slice []T, sep string, transform func(T) T) string +``` + +示例:[运行](https://go.dev/play/p/55ib3SB5fM2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string { + return strings.ToUpper(s) + }) + + fmt.Println(result) + + // Output: + // A, B, C +} +``` + +### ConcatBy + +

将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。

+ +函数签名: + +```go +func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T +``` + +示例:[运行](https://go.dev/play/p/6QcUpcY4UMW) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type Person struct { + Name string + Age int + } + + people := []Person{ + {Name: "Alice", Age: 30}, + {Name: "Bob", Age: 25}, + {Name: "Charlie", Age: 35}, + } + + sep := Person{Name: " | ", Age: 0} + + personConnector := func(a, b Person) Person { + return Person{Name: a.Name + b.Name, Age: a.Age + b.Age} + } + + result := slice.ConcatBy(people, sep, personConnector) + + fmt.Println(result.Name) + fmt.Println(result.Age) + + // Output: + // Alice | Bob | Charlie + // 90 +} +``` \ No newline at end of file diff --git a/docs/api/packages/stream.md b/docs/api/packages/stream.md new file mode 100644 index 00000000..ea4a4097 --- /dev/null +++ b/docs/api/packages/stream.md @@ -0,0 +1,1008 @@ +# Stream + +Stream流,该包仅验证简单stream实现,功能有限。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/stream/stream.go](https://github.com/duke-git/lancet/blob/main/stream/stream.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/stream" +) +``` + +
+ +## 目录 + +- [Of](#Of) +- [FromSlice](#FromSlice) +- [FromChannel](#FromChannel) +- [FromRange](#FromRange) +- [Generate](#Generate) +- [Concat](#Concat) +- [Distinct](#Distinct) +- [Filter](#Filter) +- [Map](#Map) +- [Peek](#Peek) +- [Skip](#Skip) +- [Limit](#Limit) +- [Reverse](#Reverse) +- [Range](#Range) +- [Sorted](#Sorted) +- [ForEach](#ForEach) +- [Reduce](#Reduce) +- [FindFirst](#FindFirst) +- [FindLast](#FindLast) +- [Max](#Max) +- [Min](#Min) +- [AllMatch](#AllMatch) +- [AnyMatch](#AnyMatch) +- [NoneMatch](#NoneMatch) +- [Count](#Count) +- [ToSlice](#ToSlice) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) + +
+ +## 文档 + +### Of + +

创建元素为指定值的stream。

+ +函数签名: + +```go +func Of[T any](elems ...T) stream[T] +``` + +示例:[运行](https://go.dev/play/p/jI6_iZZuVFE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.Of(1, 2, 3) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromSlice + +

从切片创建stream。

+ +函数签名: + +```go +func FromSlice[T any](source []T) stream[T] +``` + +示例:[运行](https://go.dev/play/p/wywTO0XZtI4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3}) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromChannel + +

从通道创建stream。

+ +函数签名: + +```go +func FromChannel[T any](source <-chan T) stream[T] +``` + +示例:[运行](https://go.dev/play/p/9TZYugGMhXZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + ch := make(chan int) + go func() { + for i := 1; i < 4; i++ { + ch <- i + } + close(ch) + }() + + s := stream.FromChannel(ch) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromRange + +

指定一个范围创建stream, 范围两端点值都包括在内。

+ +函数签名: + +```go +func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] +``` + +示例:[运行](https://go.dev/play/p/9Ex1-zcg-B-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromRange(1, 5, 1) + + data := s.ToSlice() + fmt.Println(data) + + // Output: + // [1 2 3 4 5] +} +``` + +### Generate + +

创建一个stream,其中每个元素都由提供的生成器函数生成

+ +函数签名: + +```go +func Generate[T any](generator func() func() (item T, ok bool)) stream[T] +``` + +示例:[运行](https://go.dev/play/p/rkOWL1yA3j9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + n := 0 + max := 4 + + generator := func() func() (int, bool) { + return func() (int, bool) { + n++ + return n, n < max + } + } + + s := stream.Generate(generator) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### Concat + +

创建一个延迟连接stream,其元素是第一个stream的所有元素,后跟第二个stream的全部元素。

+ +函数签名: + +```go +func Concat[T any](a, b stream[T]) stream[T] +``` + +示例:[运行](https://go.dev/play/p/HM4OlYk_OUC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s1 := stream.FromSlice([]int{1, 2, 3}) + s2 := stream.FromSlice([]int{4, 5, 6}) + + s := Concat(s1, s2) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### Distinct + +

创建并返回一个stream,用于删除重复的项。 支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Distinct() stream[T] +``` + +示例:[运行](https://go.dev/play/p/eGkOSrm64cB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 2, 3, 3, 3}) + distinct := original.Distinct() + + data1 := original.ToSlice() + data2 := distinct.ToSlice() + + fmt.Println(data1) + fmt.Println(data2) + + // Output: + // [1 2 2 3 3 3] + // [1 2 3] +} +``` + +### Filter + +

返回一个通过判定函数的stream 支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Filter(predicate func(item T) bool) stream[T] +``` + +示例:[运行](https://go.dev/play/p/MFlSANo-buc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4, 5}) + + isEven := func(n int) bool { + return n%2 == 0 + } + + even := original.Filter(isEven) + + fmt.Println(even.ToSlice()) + + // Output: + // [2 4] +} +``` + +### Map + +

返回一个stream,该stream由将给定函数应用于源stream元素的元素组成。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Map(mapper func(item T) T) stream[T] +``` + +示例:[运行](https://go.dev/play/p/OtNQUImdYko) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + addOne := func(n int) int { + return n + 1 + } + + increament := original.Map(addOne) + + fmt.Println(increament.ToSlice()) + + // Output: + // [2 3 4] +} +``` + +### Peek + +

返回一个由源stream的元素组成的stream,并在从生成的stream中消耗元素时对每个元素执行所提供的操作。 支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Peek(consumer func(item T)) stream[T] +``` + +示例:[运行](https://go.dev/play/p/u1VNzHs6cb2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + data := []string{} + peekStream := original.Peek(func(n int) { + data = append(data, fmt.Sprint("value", n)) + }) + + fmt.Println(original.ToSlice()) + fmt.Println(peekStream.ToSlice()) + fmt.Println(data) + + // Output: + // [1 2 3] + // [1 2 3] + // [value1 value2 value3] +} +``` + +### Skip + +

在丢弃stream的前n个元素后,返回由源stream的其余元素组成的stream。如果此stream包含的元素少于n个,则将返回一个空stream。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Skip(n int) stream[T] +``` + +示例:[运行](https://go.dev/play/p/fNdHbqjahum) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Skip(-1) + s2 := original.Skip(0) + s3 := original.Skip(1) + s4 := original.Skip(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [2 3 4] + // [] +} +``` + +### Limit + +

返回由源stream的元素组成的stream,该stream被截断为长度不超过maxSize。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Limit(maxSize int) stream[T] +``` + +示例:[运行](https://go.dev/play/p/qsO4aniDcGf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Limit(-1) + s2 := original.Limit(0) + s3 := original.Limit(1) + s4 := original.Limit(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [] + // [1] + // [1 2 3 4] +} +``` + +### Reverse + +

返回元素与源stream的顺序相反的stream。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Reverse() stream[T] +``` + +示例:[运行](https://go.dev/play/p/A8_zkJnLHm4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + reverse := original.Reverse() + + fmt.Println(reverse.ToSlice()) + + // Output: + // [3 2 1] +} +``` + +### Range + +

返回一个stream,该stream的元素在从源stream的开始(包含)到结束(排除)的范围内。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Range(start, end int) stream[T] +``` + +示例:[运行](https://go.dev/play/p/indZY5V2f4j) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + s1 := original.Range(0, 0) + s2 := original.Range(0, 1) + s3 := original.Range(0, 3) + s4 := original.Range(1, 2) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [1] + // [1 2 3] + // [2] +} +``` + +### Sorted + +

返回一个stream,该stream由源stream的元素组成,并根据提供的less函数进行排序。支持链式操作

+ +函数签名: + +```go +func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] +``` + +示例:[运行](https://go.dev/play/p/XXtng5uonFj) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + sorted := original.Sorted(func(a, b int) bool { return a < b }) + + fmt.Println(original.ToSlice()) + fmt.Println(sorted.ToSlice()) + + // Output: + // [4 2 1 3] + // [1 2 3 4] +} +``` + +### ForEach + +

对stream的每个元素执行一个操作。

+ +函数签名: + +```go +func (s stream[T]) ForEach(action func(item T)) +``` + +示例:[运行](https://go.dev/play/p/Dsm0fPqcidk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result := 0 + original.ForEach(func(item int) { + result += item + }) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### Reduce + +

使用关联累加函数对stream的元素执行reduce操作,并reduce操作结果(如果有)。

+ +函数签名: + +```go +func (s stream[T]) Reduce(initial T, accumulator func(a, b T) T) T +``` + +示例:[运行](https://go.dev/play/p/6uzZjq_DJLU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result := original.Reduce(0, func(a, b int) int { + return a + b + }) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### FindFirst + +

返回此stream的第一个元素和true,如果stream为空,则返回零值和false。

+ +函数签名: + +```go +func (s stream[T]) FindFirst() (T, bool) +``` + +示例:[运行](https://go.dev/play/p/9xEf0-6C1e3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result, ok := original.FindFirst() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### FindLast + +

返回此stream最后一个元素和true,如果stream为空,则返回零值和false。

+ +函数签名: + +```go +func (s stream[T]) FindLast() (T, bool) +``` + +示例:[运行](https://go.dev/play/p/WZD2rDAW-2h) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{3, 2, 1}) + + result, ok := original.FindLast() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### Max + +

根据提供的less函数返回stream的最大元素。less 函数: a > b

+ +函数签名: + +```go +func (s stream[T]) Max(less func(a, b T) bool) (T, bool) +``` + +示例:[运行](https://go.dev/play/p/fm-1KOPtGzn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + max, ok := original.Max(func(a, b int) bool { return a > b }) + + fmt.Println(max) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### Min + +

根据提供的less函数返回stream的最小元素。less函数: a < b

+ +函数签名: + +```go +func (s stream[T]) Min(less func(a, b T) bool) (T, bool) +``` + +示例:[运行](https://go.dev/play/p/vZfIDgGNRe_0) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + min, ok := original.Min(func(a, b int) bool { return a < b }) + + fmt.Println(min) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### AllMatch + +

判断stream的所有元素是否全部匹配指定判定函数。

+ +函数签名: + +```go +func (s stream[T]) AllMatch(predicate func(item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/V5TBpVRs-Cx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.AllMatch(func(item int) bool { + return item > 0 + }) + + result2 := original.AllMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### AnyMatch + +

判断stream是否包含匹配指定判定函数的元素。

+ +函数签名: + +```go +func (s stream[T]) AnyMatch(predicate func(item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/PTCnWn4OxSn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.AnyMatch(func(item int) bool { + return item > 1 + }) + + result2 := original.AnyMatch(func(item int) bool { + return item > 3 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### NoneMatch + +

判断stream的元素是否全部不匹配指定的判定函数。

+ +函数签名: + +```go +func (s stream[T]) NoneMatch(predicate func(item T) bool) bool +``` + +示例:[运行](https://go.dev/play/p/iWS64pL1oo3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.NoneMatch(func(item int) bool { + return item > 3 + }) + + result2 := original.NoneMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### Count + +

返回stream中元素的数量。

+ +函数签名: + +```go +func (s stream[T]) Count() int +``` + +示例:[运行](https://go.dev/play/p/r3koY6y_Xo-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s1 := stream.FromSlice([]int{1, 2, 3}) + s2 := stream.FromSlice([]int{}) + + fmt.Println(s1.Count()) + fmt.Println(s2.Count()) + + // Output: + // 3 + // 0 +} +``` + +### ToSlice + +

返回stream中的元素切片。

+ +函数签名: + +```go +func (s stream[T]) ToSlice() []T +``` + +示例:[运行](https://go.dev/play/p/jI6_iZZuVFE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.Of(1, 2, 3) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### IndexOf + +

返回在stream中找到值的第一个匹配项的索引,如果找不到值,则返回-1。

+ +函数签名: + +```go +func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int +``` + +示例:[运行](https://go.dev/play/p/tBV5Nc-XDX2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3, 2}) + + result1 := s.IndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.IndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 1 +} +``` + +### LastIndexOf + +

返回在stream中找到值的最后一个匹配项的索引,如果找不到值,则返回-1。

+ +函数签名: + +```go +func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int +``` + +示例:[运行](https://go.dev/play/p/CjeoNw2eac_G) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3, 2}) + + result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 3 +} +``` \ No newline at end of file diff --git a/docs/api/packages/struct.md b/docs/api/packages/struct.md new file mode 100644 index 00000000..8555796c --- /dev/null +++ b/docs/api/packages/struct.md @@ -0,0 +1,576 @@ +# Structs + +structs 包封装了一个抽象的`Struct`结构体,提供了操作`struct`的相关函数 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/structs/struct.go](https://github.com/duke-git/lancet/blob/main/structs/struct.go) + +- [https://github.com/duke-git/lancet/blob/main/structs/field.go](https://github.com/duke-git/lancet/blob/main/structs/field.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/structs" +) +``` + +
+ +## 目录: + +- [New](#New) +- [ToMap](#ToMap) +- [Fields](#Fields) +- [Field](#Field) +- [IsStruct](#IsStruct) +- [Tag](#Tag) +- [Name](#Name) +- [Value](#Value) +- [Kind](#Kind) +- [IsEmbedded](#IsEmbedded) +- [IsExported](#IsExported) +- [IsZero](#IsZero) +- [IsSlice](#IsSlice) +- [IsTargetType](#IsTargetType) + +
+ +## API 文档: + +### New + +

`Struct`结构体的构造函数

+ +函数签名: + +```go +func New(value any, tagName ...string) *Struct +``` + +示例: + +```go +package main + +import ( + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + // to do something +} +``` + +### ToMap + +

将一个合法的struct对象转换为map[string]any

+ +函数签名: + +```go +func (s *Struct) ToMap() (map[string]any, error) +``` + +除此之外,提供一个便捷的静态方法 ToMap + +```go +func ToMap(v any) (map[string]any, error) +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s1 := structs.New(p1) + m1, _ := s1.ToMap() + + fmt.Println(m1) + + // 如果不需要Struct更多的方法,可以直接使用ToMap + m2, _ := structs.ToMap(p1) + + fmt.Println(m2) + + // Output: + // map[name:11] + // map[name:11] +} +``` + +### Fields + +

获取一个struct对象的属性列表

+ +函数签名: + +```go +func (s *Struct) Fields() []*Field +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + fields := s.Fields() + + fmt.Println(len(fields)) + + // Output: + // 1 +} +``` + +### Field + +

根据属性名获取一个struct对象的属性

+ +函数签名: + +```go +func (s *Struct) Field(name string) *Field +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + f := s.Field("Name") + + fmt.Println(f.Value()) + + // Output: + // 11 +} +``` + +### IsStruct + +

判断是否为一个合法的struct对象

+ +函数签名: + +```go +func (s *Struct) IsStruct() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + + fmt.Println(s.IsStruct()) + + // Output: + // true +} +``` + +### Tag + +

获取`Field`的`Tag`,默认的tag key是json

+ +函数签名: + +```go +func (f *Field) Tag() *Tag +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := structs.New(p1) + n, _ := s.Field("Name") + tag := n.Tag() + + fmt.Println(tag.Name) + + // Output: + // name +} +``` + +### Value + +

获取`Field`属性的值

+ +函数签名: + +```go +func (f *Field) Value() any +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := structs.New(p1) + n, _ := s.Field("Name") + + fmt.Println(n.Value()) + + // Output: + // 111 +} +``` + +### IsEmbedded + +

判断属性是否为嵌入

+ +函数签名: + +```go +func (f *Field) IsEmbedded() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + } + type Child struct { + Parent + Age int + } + c1 := &Child{} + c1.Name = "111" + c1.Age = 11 + + s := structs.New(c1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.IsEmbedded()) + fmt.Println(a.IsEmbedded()) + + // Output: + // true + // false +} +``` + +### IsExported + +

判断属性是否导出

+ +函数签名: + +```go +func (f *Field) IsExported() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + age int + } + p1 := &Parent{Name: "11", age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("age") + + fmt.Println(n.IsExported()) + fmt.Println(a.IsExported()) + + // Output: + // true + // false +} +``` + +### IsZero + +

判断属性是否为零值

+ +函数签名: + +```go +func (f *Field) IsZero() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.IsZero()) + fmt.Println(a.IsZero()) + + // Output: + // true + // false +} +``` + +### Name + +

获取属性名

+ +函数签名: + +```go +func (f *Field) Name() string +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.Name()) + fmt.Println(a.Name()) + + // Output: + // Name + // Age +} +``` + +### Kind + +

获取属性Kind

+ +函数签名: + +```go +func (f *Field) Kind() reflect.Kind +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.Kind()) + fmt.Println(a.Kind()) + + // Output: + // string + // int +} +``` + +### IsSlice + +

判断属性是否是切片

+ +函数签名: + +```go +func (f *Field) IsSlice() bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{arr: []int{1, 2, 3}} + s := structs.New(p1) + a, _ := s.Field("arr") + + fmt.Println(a.IsSlice()) + + // Output: + // true +} +``` + +### IsTargetType + +

判断属性是否是目标类型

+ +函数签名: + +```go +func (f *Field) IsTargetType(targetType reflect.Kind) bool +``` + +示例: + +```go +package main + +import ( + "fmt" + "reflect" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{arr: []int{1, 2, 3}} + 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: + // true + // true +} +``` \ No newline at end of file diff --git a/docs/api/packages/strutil.md b/docs/api/packages/strutil.md new file mode 100644 index 00000000..b5d370be --- /dev/null +++ b/docs/api/packages/strutil.md @@ -0,0 +1,1791 @@ +# Strutil + +strutil 包含处理字符串的相关函数。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/strutil/string.go](https://github.com/duke-git/lancet/blob/main/strutil/string.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/strutil" +) +``` + +
+ +## 目录 + +- [After](#After) +- [AfterLast](#AfterLast) +- [Before](#Before) +- [BeforeLast](#BeforeLast) +- [CamelCase](#CamelCase) +- [Capitalize](#Capitalize) +- [IsString](#IsString) +- [KebabCase](#KebabCase) +- [UpperKebabCase](#UpperKebabCase) +- [LowerFirst](#LowerFirst) +- [UpperFirst](#UpperFirst) +- [Pad](#Pad) +- [PadEnd](#PadEnd) +- [PadStart](#PadStart) +- [Reverse](#Reverse) +- [SnakeCase](#SnakeCase) +- [UpperSnakeCase](#UpperSnakeCase) +- [SplitEx](#SplitEx) +- [Substring](#Substring) +- [Wrap](#Wrap) +- [Unwrap](#Unwrap) +- [SplitWords](#SplitWords) +- [WordCount](#WordCount) +- [RemoveNonPrintable](#RemoveNonPrintable) +- [StringToBytes](#StringToBytes) +- [BytesToString](#BytesToString) +- [IsBlank](#IsBlank) +- [IsNotBlank](#IsNotBlank) +- [HasPrefixAny](#HasPrefixAny) +- [HasSuffixAny](#HasSuffixAny) +- [IndexOffset](#IndexOffset) +- [ReplaceWithMap](#ReplaceWithMap) +- [Trim](#Trim) +- [SplitAndTrim](#SplitAndTrim) +- [HideString](#HideString) +- [ContainsAll](#ContainsAll) +- [ContainsAny](#ContainsAny) +- [RemoveWhiteSpace](#RemoveWhiteSpace) +- [SubInBetween](#SubInBetween) +- [HammingDistance](#HammingDistance) +- [Concat](#Concat) +- [Ellipsis](#Ellipsis) +- [Shuffle](#Shuffle) +- [Rotate](#Rotate) +- [TemplateReplace](#TemplateReplace) +- [RegexMatchAllGroups](#RegexMatchAllGroups) +- [ExtractContent](#ExtractContent) +- [FindAllOccurrences](#FindAllOccurrences) + + +
+ +## 文档 + +### After + +

返回源字符串中特定字符串首次出现时的位置之后的子字符串。

+ +函数签名: + +```go +func After(s, char string) string +``` + +示例:[运行](https://go.dev/play/p/RbCOQqCDA7m) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.After("foo", "") + result2 := strutil.After("foo", "foo") + result3 := strutil.After("foo/bar", "foo") + result4 := strutil.After("foo/bar", "/") + result5 := strutil.After("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // + // /bar + // bar + // bar/baz +} +``` + +### AfterLast + +

返回源字符串中指定字符串最后一次出现时的位置之后的子字符串。

+ +函数签名: + +```go +func AfterLast(s, char string) string +``` + +示例:[运行](https://go.dev/play/p/1TegARrb8Yn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.AfterLast("foo", "") + result2 := strutil.AfterLast("foo", "foo") + result3 := strutil.AfterLast("foo/bar", "/") + result4 := strutil.AfterLast("foo/bar/baz", "/") + result5 := strutil.AfterLast("foo/bar/foo/baz", "foo") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // + // bar + // baz + // /baz +} +``` + +### Before + +

返回源字符串中指定字符串第一次出现时的位置之前的子字符串。

+ +函数签名: + +```go +func Before(s, char string) string +``` + +示例:[运行](https://go.dev/play/p/JAWTZDS4F5w) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Before("foo", "") + result2 := strutil.Before("foo", "foo") + result3 := strutil.Before("foo/bar", "/") + result4 := strutil.Before("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // + // foo + // foo +} +``` + +### BeforeLast + +

返回源字符串中指定字符串最后一次出现时的位置之前的子字符串。

+ +函数签名: + +```go +func BeforeLast(s, char string) string +``` + +示例:[运行](https://go.dev/play/p/pJfXXAoG_Te) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.BeforeLast("foo", "") + result2 := strutil.BeforeLast("foo", "foo") + result3 := strutil.BeforeLast("foo/bar", "/") + result4 := strutil.BeforeLast("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // + // foo + // foo/bar +} +``` + +### CamelCase + +

将字符串转换为驼峰式字符串, 非字母和数字会被忽略。

+ +函数签名: + +```go +func CamelCase(s string) string +``` + +示例:[运行](https://go.dev/play/p/9eXP3tn2tUy) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foobar", "&FOO:BAR$BAZ", "$foo%", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.CamelCase(v) + fmt.Println(s) + } + + // Output: + // + // foobar + // fooBarBaz + // foo + // foo11Bar +} +``` + +### KebabCase + +

将字符串转换为kebab-case, 非字母和数字会被忽略。

+ +函数签名: + +```go +func KebabCase(s string) string +``` + +示例:[运行](https://go.dev/play/p/dcZM9Oahw-Y) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.KebabCase(v) + fmt.Println(s) + } + + // Output: + // + // foo-bar + // foo-bar + // foobar + // foo-1-1-bar +} +``` + +### UpperKebabCase + +

将字符串转换为大写KEBAB-CASE, 非字母和数字会被忽略。

+ +函数签名: + +```go +func UpperKebabCase(s string) string +``` + +示例:[运行](https://go.dev/play/p/zDyKNneyQXk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.UpperKebabCase(v) + fmt.Println(s) + } + + // Output: + // + // FOO-BAR + // FOO-BAR + // FOO-BAR + // FOO-1-1-BAR +} +``` + +### Capitalize + +

将字符串的第一个字符转换为大写。

+ +函数签名: + +```go +func Capitalize(s string) string +``` + +示例:[运行](https://go.dev/play/p/2OAjgbmAqHZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "Foo", "_foo", "fooBar", "foo-bar"} + + for _, v := range strings { + s := strutil.Capitalize(v) + fmt.Println(s) + } + + // Output: + // + // Foo + // _foo + // Foobar + // Foo-bar +} +``` + +### IsString + +

判断传入参数的数据类型是否为字符串。

+ +函数签名: + +```go +func IsString(v any) bool +``` + +示例:[运行](https://go.dev/play/p/IOgq7oF9ERm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsString("") + result2 := strutil.IsString("a") + result3 := strutil.IsString(1) + result4 := strutil.IsString(true) + result5 := strutil.IsString([]string{"a"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // false + // false + // false +} +``` + +### LowerFirst + +

将字符串的第一个字符转换为小写。

+ +函数签名: + +```go +func LowerFirst(s string) string +``` + +示例:[运行](https://go.dev/play/p/CbzAyZmtJwL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "bar", "BAr", "Bar大"} + + for _, v := range strings { + s := strutil.LowerFirst(v) + fmt.Println(s) + } + + // Output: + // + // bar + // bAr + // bar大 +} +``` + +### UpperFirst + +

将字符串的第一个字符转换为大写形式。

+ +函数签名: + +```go +func UpperFirst(s string) string +``` + +示例:[运行](https://go.dev/play/p/sBbBxRbs8MM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "bar", "BAr", "bar大"} + + for _, v := range strings { + s := strutil.UpperFirst(v) + fmt.Println(s) + } + + // Output: + // + // Bar + // BAr + // Bar大 +} +``` + +### Pad + +

如果字符串长度短于size,则在左右两侧填充字符串。

+ +函数签名: + +```go +func Pad(source string, size int, padStr string) string +``` + +示例:[运行](https://go.dev/play/p/NzImQq-VF8q) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Pad("foo", 1, "bar") + result2 := strutil.Pad("foo", 2, "bar") + result3 := strutil.Pad("foo", 3, "bar") + result4 := strutil.Pad("foo", 4, "bar") + result5 := strutil.Pad("foo", 5, "bar") + result6 := strutil.Pad("foo", 6, "bar") + result7 := strutil.Pad("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + // Output: + // foo + // foo + // foo + // foob + // bfoob + // bfooba + // bafooba +} +``` + +### PadEnd + +

如果字符串长度短于size,则在右侧填充字符串。

+ +函数签名: + +```go +func PadEnd(source string, size int, padStr string) string +``` + +示例:[运行](https://go.dev/play/p/9xP8rN0vz--) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.PadEnd("foo", 1, "bar") + result2 := strutil.PadEnd("foo", 2, "bar") + result3 := strutil.PadEnd("foo", 3, "bar") + result4 := strutil.PadEnd("foo", 4, "bar") + result5 := strutil.PadEnd("foo", 5, "bar") + result6 := strutil.PadEnd("foo", 6, "bar") + result7 := strutil.PadEnd("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // foo + // foo + // foo + // foob + // fooba + // foobar + // foobarb +} +``` + +### PadStart + +

如果字符串长度短于size,则在左侧填充字符串。

+ +函数签名: + +```go +func PadStart(source string, size int, padStr string) string +``` + +示例:[运行](https://go.dev/play/p/xpTfzArDfvT) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.PadStart("foo", 1, "bar") + result2 := strutil.PadStart("foo", 2, "bar") + result3 := strutil.PadStart("foo", 3, "bar") + result4 := strutil.PadStart("foo", 4, "bar") + result5 := strutil.PadStart("foo", 5, "bar") + result6 := strutil.PadStart("foo", 6, "bar") + result7 := strutil.PadStart("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // foo + // foo + // foo + // bfoo + // bafoo + // barfoo + // barbfoo +} +``` + +### Reverse + +

返回字符顺序与给定字符串相反的字符串。

+ +函数签名: + +```go +func Reverse(s string) string +``` + +示例:[运行](https://go.dev/play/p/adfwalJiecD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + s := "foo" + rs := strutil.Reverse(s) + + fmt.Println(s) + fmt.Println(rs) + + // Output: + // foo + // oof +} +``` + +### SnakeCase + +

将字符串转换为snake_case形式, 非字母和数字会被忽略。

+ +函数签名: + +```go +func SnakeCase(s string) string +``` + +示例:[运行](https://go.dev/play/p/tgzQG11qBuN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.SnakeCase(v) + fmt.Println(s) + } + + // Output: + // + // foo_bar + // foo_bar + // foobar + // foo_1_1_bar +} +``` + +### UpperSnakeCase + +

将字符串转换为大写SNAKE_CASE形式, 非字母和数字会被忽略。

+ +函数签名: + +```go +func UpperSnakeCase(s string) string +``` + +示例:[运行](https://go.dev/play/p/4COPHpnLx38) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.UpperSnakeCase(v) + fmt.Println(s) + } + + // Output: + // + // FOO_BAR + // FOO_BAR + // FOO_BAR + // FOO_1_1_BAR +} +``` + +### SplitEx + +

分割字符串为切片,removeEmptyString参数指定是否去除空字符串。

+ +函数签名: + +```go +func SplitEx(s, sep string, removeEmptyString bool) []string +``` + +示例:[运行](https://go.dev/play/p/Us-ySSbWh-3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.SplitEx(" a b c ", "", true) + + result2 := strutil.SplitEx(" a b c ", " ", false) + result3 := strutil.SplitEx(" a b c ", " ", true) + + result4 := strutil.SplitEx("a = b = c = ", " = ", false) + result5 := strutil.SplitEx("a = b = c = ", " = ", true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [] + // [ a b c ] + // [a b c] + // [a b c ] +} +``` + +### Substring + +

根据指定的位置和长度截取字符串。

+ +函数签名: + +```go +func Substring(s string, offset int, length uint) string +``` + +示例:[运行](https://go.dev/play/p/q3sM6ehnPDp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Substring("abcde", 1, 3) + result2 := strutil.Substring("abcde", 1, 5) + result3 := strutil.Substring("abcde", -1, 3) + result4 := strutil.Substring("abcde", -2, 2) + result5 := strutil.Substring("abcde", -2, 3) + result6 := strutil.Substring("你好,欢迎你", 0, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // bcd + // bcde + // e + // de + // de + // 你好 +} +``` + +### Wrap + +

用另一个字符串包裹一个字符串。

+ +函数签名: + +```go +func Wrap(str string, wrapWith string) string +``` + +示例:[运行](https://go.dev/play/p/KoZOlZDDt9y) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Wrap("foo", "") + result2 := strutil.Wrap("foo", "*") + result3 := strutil.Wrap("'foo'", "'") + result4 := strutil.Wrap("", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // *foo* + // ''foo'' + // +} +``` + +### Unwrap + +

用另一个字符串解开包裹一个字符串。

+ +函数签名: + +```go +func Unwrap(str string, wrapToken string) string +``` + +示例:[运行](https://go.dev/play/p/Ec2q4BzCpG-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Unwrap("foo", "") + result2 := strutil.Unwrap("*foo*", "*") + result3 := strutil.Unwrap("*foo", "*") + result4 := strutil.Unwrap("foo*", "*") + result5 := strutil.Unwrap("**foo**", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // foo + // *foo + // foo* + // *foo* +} +``` + +### SplitWords + +

将字符串拆分为单词,只支持字母字符单词。

+ +函数签名: + +```go +func SplitWords(s string) []string +``` + +示例:[运行](https://go.dev/play/p/KLiX4WiysMM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.SplitWords("a word") + result2 := strutil.SplitWords("I'am a programmer") + result3 := strutil.SplitWords("Bonjour, je suis programmeur") + result4 := strutil.SplitWords("a -b-c' 'd'e") + result5 := strutil.SplitWords("你好,我是一名码农") + result6 := strutil.SplitWords("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // [a word] + // [I'am a programmer] + // [Bonjour je suis programmeur] + // [a b-c' d'e] + // [] + // [] +} +``` + +### WordCount + +

返回有意义单词的数量,只支持字母字符单词。

+ +函数签名: + +```go +func WordCount(s string) int +``` + +示例:[运行](https://go.dev/play/p/bj7_odx3vRf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.WordCount("a word") + result2 := strutil.WordCount("I'am a programmer") + result3 := strutil.WordCount("Bonjour, je suis programmeur") + result4 := strutil.WordCount("a -b-c' 'd'e") + result5 := strutil.WordCount("你好,我是一名码农") + result6 := strutil.WordCount("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // 2 + // 3 + // 4 + // 3 + // 0 + // 0 +} +``` + +### RemoveNonPrintable + +

删除字符串中不可打印的字符。

+ +函数签名: + +```go +func RemoveNonPrintable(str string) string +``` + +示例:[运行](https://go.dev/play/p/og47F5x_jTZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.RemoveNonPrintable("hello\u00a0 \u200bworld\n") + result2 := strutil.RemoveNonPrintable("你好😄") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // hello world + // 你好😄 +} +``` + +### StringToBytes + +

在不分配内存的情况下将字符串转换为字节片。

+ +函数签名: + +```go +func StringToBytes(str string) (b []byte) +``` + +示例:[运行](https://go.dev/play/p/7OyFBrf9AxA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.StringToBytes("abc") + result2 := reflect.DeepEqual(result1, []byte{'a', 'b', 'c'}) + + fmt.Println(result1) + fmt.Println(result2) + // Output: + // [97 98 99] + // true +} +``` + +### BytesToString + +

在不分配内存的情况下将字节切片转换为字符串。

+ +函数签名: + +```go +func BytesToString(bytes []byte) string +``` + +示例:[运行](https://go.dev/play/p/6c68HRvJecH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + bytes := []byte{'a', 'b', 'c'} + result := strutil.BytesToString(bytes) + + fmt.Println(result) + + // Output: + // abc +} +``` + +### IsBlank + +

检查字符串是否为空格或空。

+ +函数签名: + +```go +func IsBlank(str string) bool +``` + +示例:[运行](https://go.dev/play/p/6zXRH_c0Qd3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsBlank("") + result2 := strutil.IsBlank("\t\v\f\n") + result3 := strutil.IsBlank(" 中文") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsNotBlank + +

Checks if a string is not whitespace or not empty.

+ +函数签名: + +```go +func IsNotBlank(str string) bool +``` + +示例:[运行](https://go.dev/play/p/e_oJW0RAquA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsNotBlank("") + result2 := strutil.IsNotBlank(" ") + result3 := strutil.IsNotBlank("\t\v\f\n") + result4 := strutil.IsNotBlank(" 中文") + result5 := strutil.IsNotBlank(" world ") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // false + // false + // false + // true + // true +} +``` + +### HasPrefixAny + +

检查字符串是否以指定字符串数组中的任何一个开头。

+ +函数签名: + +```go +func HasPrefixAny(str string, prefixes []string) bool +``` + +示例:[运行](https://go.dev/play/p/8UUTl2C5slo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.HasPrefixAny("foo bar", []string{"fo", "xyz", "hello"}) + result2 := strutil.HasPrefixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### HasSuffixAny + +

检查字符串是否以指定字符串数组中的任何一个结尾。

+ +函数签名: + +```go +func HasSuffixAny(str string, suffixes []string) bool +``` + +示例:[运行](https://go.dev/play/p/sKWpCQdOVkx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.HasSuffixAny("foo bar", []string{"bar", "xyz", "hello"}) + result2 := strutil.HasSuffixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IndexOffset + +

将字符串偏移idxFrom后,返回字符串中第一个 substr 实例的索引,如果字符串中不存在 substr,则返回 -1。

+ +函数签名: + +```go +func IndexOffset(str string, substr string, idxFrom int) int +``` + +示例:[运行](https://go.dev/play/p/qZo4lV2fomB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "foo bar hello world" + + result1 := strutil.IndexOffset(str, "o", 5) + result2 := strutil.IndexOffset(str, "o", 0) + result3 := strutil.IndexOffset(str, "d", len(str)-1) + result4 := strutil.IndexOffset(str, "d", len(str)) + result5 := strutil.IndexOffset(str, "f", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 12 + // 1 + // 18 + // -1 + // -1 +} +``` + +### ReplaceWithMap + +

返回`str`的副本,以无序的方式被map替换,区分大小写。

+ +函数签名: + +```go +func ReplaceWithMap(str string, replaces map[string]string) string +``` + +示例:[运行](https://go.dev/play/p/h3t7CNj2Vvu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "ac ab ab ac" + replaces := map[string]string{ + "a": "1", + "b": "2", + } + + result := strutil.ReplaceWithMap(str, replaces) + + fmt.Println(result) + // Output: + // 1c 12 12 1c +} +``` + +### Trim + +

从字符串的开头和结尾去除空格(或其他字符)。 可选参数 characterMask 指定额外的剥离字符。

+ +函数签名: + +```go +func Trim(str string, characterMask ...string) string +``` + +示例:[运行](https://go.dev/play/p/Y0ilP0NRV3j) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Trim("\nabcd") + + str := "$ ab cd $ " + + result2 := strutil.Trim(str) + result3 := strutil.Trim(str, "$") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abcd + // $ ab cd $ + // ab cd +} +``` + +### SplitAndTrim + +

将字符串str按字符串delimiter拆分为一个切片,并对该数组的每个元素调用Trim。忽略Trim后为空的元素。

+ +函数签名: + +```go +func SplitAndTrim(str, delimiter string, characterMask ...string) []string +``` + +示例:[运行](https://go.dev/play/p/ZNL6o4SkYQ7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := " a,b, c,d,$1 " + + result1 := strutil.SplitAndTrim(str, ",") + result2 := strutil.SplitAndTrim(str, ",", "$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b c d $1] + // [a b c d 1] +} +``` + +### HideString + +

使用参数`replaceChar`隐藏源字符串中的一些字符。替换范围是 origin[start : end]。

+ +函数签名: + +```go +func HideString(origin string, start, end int, replaceChar string) string +``` + +示例:[运行](https://go.dev/play/p/pzbaIVCTreZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "13242658976" + + result1 := strutil.HideString(str, 3, 3, "*") + result2 := strutil.HideString(str, 3, 4, "*") + result3 := strutil.HideString(str, 3, 7, "*") + result4 := strutil.HideString(str, 7, 11, "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 13242658976 + // 132*2658976 + // 132****8976 + // 1324265**** +} +``` + +### ContainsAll + +

判断字符串是否包括全部给定的子字符串切片。

+ +函数签名: + +```go +func ContainsAll(str string, substrs []string) bool +``` + +示例:[运行](https://go.dev/play/p/KECtK2Os4zq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "hello world" + + result1 := strutil.ContainsAll(str, []string{"hello", "world"}) + result2 := strutil.ContainsAll(str, []string{"hello", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### ContainsAny + +

判断字符串是否包括给定的子字符串切片中任意一个子字符串。

+ +函数签名: + +```go +func ContainsAny(str string, substrs []string) bool +``` + +示例:[运行](https://go.dev/play/p/dZGSSMB3LXE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "hello world" + + result1 := strutil.ContainsAny(str, []string{"hello", "world"}) + result2 := strutil.ContainsAny(str, []string{"hello", "abc"}) + result3 := strutil.ContainsAny(str, []string{"123", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### RemoveWhiteSpace + +

删除字符串中的空格,当设置repalceAll为true时,删除全部空格,为false时,替换多个空格为1个空格。

+ +函数签名: + +```go +func RemoveWhiteSpace(str string, repalceAll bool) string +``` + +示例:[运行](https://go.dev/play/p/HzLC9vsTwkf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := " hello \r\n \t world" + + result1 := strutil.RemoveWhiteSpace(str, true) + result2 := strutil.RemoveWhiteSpace(str, false) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // helloworld + // hello world +} +``` + +### SubInBetween + +

获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。

+ +函数签名: + +```go +func SubInBetween(str string, start string, end string) string +``` + +示例:[运行](https://go.dev/play/p/EDbaRvjeNsv) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "abcde" + + result1 := strutil.SubInBetween(str, "", "de") + result2 := strutil.SubInBetween(str, "a", "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // abc + // bc +} +``` + +### HammingDistance + +

计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。

+ +函数签名: + +```go +func HammingDistance(a, b string) (int, error) +``` + +示例:[运行](https://go.dev/play/p/glNdQEA9HUi) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + + result1, _ := strutil.HammingDistance("de", "de") + result2, _ := strutil.HammingDistance("a", "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} + +``` +### Concat + +

拼接字符串。length是拼接后字符串的长度,如果不确定则传0或负数。

+ +函数签名: + +```go +func Concat(length int, str ...string) string +``` + +示例:[运行](https://go.dev/play/p/gD52SZHr4Kp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Concat(12, "Hello", " ", "World", "!") + result2 := strutil.Concat(11, "Go", " ", "Language") + result3 := strutil.Concat(0, "An apple a ", "day,", "keeps the", " doctor away") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello World! + // Go Language + // An apple a day,keeps the doctor away +} +``` + +### Ellipsis + +

将字符串截断到指定长度,并在末尾添加省略号。

+ +函数签名: + +```go +func Ellipsis(str string, length int) string +``` + +示例:[运行](https://go.dev/play/p/i1vbdQiQVRR) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Ellipsis("hello world", 5) + result2 := strutil.Ellipsis("你好,世界!", 2) + result3 := strutil.Ellipsis("😀😃😄😁😆", 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // hello... + // 你好... + // 😀😃😄... +} +``` + +### Shuffle + +

打乱给定字符串中的字符顺序。

+ +函数签名: + +```go +func Shuffle(str string) string +``` + +示例:[运行](https://go.dev/play/p/iStFwBwyGY7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result := strutil.Shuffle("hello") + fmt.Println(result) //olelh (random order) +} +``` + +### Rotate + +

按指定的字符数旋转字符串。

+ +函数签名: + +```go +func Rotate(str string, shift int) string +``` + +示例:[运行](https://go.dev/play/p/Kf03iOeT5bd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Rotate("Hello", 0) + result2 := strutil.Rotate("Hello", 1) + result3 := strutil.Rotate("Hello", 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello + // oHell + // loHel +} +``` + +### TemplateReplace + +

将模板字符串中的占位符替换为map中的相应值。占位符括在花括号中,例如 {key}。例如,模板字符串为“Hello, {name}!”,map为{"name": "world"},结果将为“Hello, world!”。

+ +函数签名: + +```go +func TemplateReplace(template string, data map[string]string) string +``` + +示例:[运行](https://go.dev/play/p/cXSuFvyZqv9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + template := `Hello, my name is {name}, I'm {age} years old.` + data := map[string]string{ + "name": "Bob", + "age": "20", + } + + result := strutil.TemplateReplace(template, data) + + fmt.Println(result) + + // Output: + // Hello, my name is Bob, I'm 20 years old. +} +``` + +### RegexMatchAllGroups + +

使用正则表达式匹配字符串中的所有子组并返回结果。

+ +函数签名: + +```go +func RegexMatchAllGroups(pattern, str string) [][]string +``` + +示例:[运行](https://go.dev/play/p/JZiu0RXpgN-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + pattern := `(\w+\.+\w+)@(\w+)\.(\w+)` + str := "Emails: john.doe@example.com and jane.doe@example.com" + + result := strutil.RegexMatchAllGroups(pattern, str) + + fmt.Println(result[0]) + fmt.Println(result[1]) + + // Output: + // [john.doe@example.com john.doe example com] + // [jane.doe@example.com jane.doe example com] +} +``` + +### ExtractContent + +

提取两个标记之间的内容。

+ +函数签名: + +```go +func ExtractContent(s, start, end string) []string +``` + +示例:[运行](https://go.dev/play/p/Ay9UIk7Rum9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + html := `content1aacontent2bbcontent1` + + result := strutil.ExtractContent(html, "", "") + + fmt.Println(result) + + // Output: + // [content1 content2 content1] +} +``` + +### FindAllOccurrences + +

返回子字符串在字符串中所有出现的位置。

+ +函数签名: + +```go +func FindAllOccurrences(str, substr string) []int +``` + +示例:[运行](https://go.dev/play/p/uvyA6azGLB1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result := strutil.FindAllOccurrences("ababab", "ab") + + fmt.Println(result) + + // Output: + // [0 2 4] +} +``` \ No newline at end of file diff --git a/docs/api/packages/system.md b/docs/api/packages/system.md new file mode 100644 index 00000000..937a8f0c --- /dev/null +++ b/docs/api/packages/system.md @@ -0,0 +1,444 @@ +# System + +system 包含 os, 运行time, shell command 相关函数。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/system/os.go](https://github.com/duke-git/lancet/blob/main/system/os.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/system" +) +``` + +
+ +## 目录 + +- [IsWindows](#IsWindows) +- [IsLinux](#IsLinux) +- [IsMac](#IsMac) +- [GetOsEnv](#GetOsEnv) +- [SetOsEnv](#SetOsEnv) +- [RemoveOsEnv](#RemoveOsEnv) +- [CompareOsEnv](#CompareOsEnv) +- [ExecCommand](#ExecCommand) +- [GetOsBits](#GetOsBits) +- [StartProcess](#StartProcess) +- [StopProcess](#StopProcess) +- [KillProcess](#KillProcess) +- [GetProcessInfo](#GetProcessInfo) + + +
+ +## 文档 + +### IsWindows + +

检查当前操作系统是否是windows

+ +函数签名: + +```go +func IsWindows() bool +``` + +示例:[运行](https://go.dev/play/p/zIflQgZNuxD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsWindows := system.IsWindows() + fmt.Println(isOsWindows) +} +``` + +### IsLinux + +

检查当前操作系统是否是linux

+ +函数签名:[运行](https://go.dev/play/p/zIflQgZNuxD) + +```go +func IsLinux() bool +``` + +示例: + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsLinux := system.IsLinux() + fmt.Println(isOsLinux) +} +``` + +### IsMac + +

检查当前操作系统是否是macos

+ +函数签名: + +```go +func IsMac() bool +``` + +示例:[运行](https://go.dev/play/p/Mg4Hjtyq7Zc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsMac := system.IsMac() + fmt.Println(isOsMac) +} +``` + +### GetOsEnv + +

获取key命名的环境变量的值

+ +函数签名: + +```go +func GetOsEnv(key string) string +``` + +示例:[运行](https://go.dev/play/p/D88OYVCyjO-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + result := system.GetOsEnv("foo") + + fmt.Println(err) + fmt.Println(result) + // Output: + // + // abc +} +``` + +### SetOsEnv + +

设置由key命名的环境变量的值

+ +函数签名: + +```go +func SetOsEnv(key, value string) error +``` + +示例:[运行](https://go.dev/play/p/D88OYVCyjO-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + result := system.GetOsEnv("foo") + + fmt.Println(err) + fmt.Println(result) + // Output: + // + // abc +} +``` + +### RemoveOsEnv + +

删除单个环境变量

+ +函数签名: + +```go +func RemoveOsEnv(key string) error +``` + +示例:[运行](https://go.dev/play/p/fqyq4b3xUFQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err1 := system.SetOsEnv("foo", "abc") + result1 := GetOsEnv("foo") + + err2 := system.RemoveOsEnv("foo") + result2 := GetOsEnv("foo") + + fmt.Println(err1) + fmt.Println(err2) + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // + // + // abc + // +} +``` + +### CompareOsEnv + +

获取key命名的环境变量值并与compareEnv进行比较

+ +函数签名: + +```go +func CompareOsEnv(key, comparedEnv string) bool +``` + +示例:[运行](https://go.dev/play/p/BciHrKYOHbp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + if err != nil { + return + } + + result := system.CompareOsEnv("foo", "abc") + + fmt.Println(result) + + // Output: + // true +} +``` + +### ExecCommand + +

执行shell命令,返回命令的stdout和stderr字符串,如果出现错误,则返回错误。参数`command`是一个完整的命令字符串,如ls-a(linux),dir(windows),ping 127.0.0.1。在linux中,使用/bin/bash-c执行命令,在windows中,使用powershell.exe执行命令。 +函数的第二个参数是cmd选项控制参数,类型是func(*exec.Cmd),可以通过这个参数设置cmd属性。

+ +函数签名: + +```go +type ( + Option func(*exec.Cmd) +) +func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error) +``` + +示例:[运行](https://go.dev/play/p/n-2fLyZef-4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + // linux or mac + stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) { + cmd.Dir = "/tmp" + }) + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + assert.Equal("", stderr) + + // windows + stdout, stderr, err = system.ExecCommand("dir") + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + + // error command + stdout, stderr, err = system.ExecCommand("abc") + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + if err != nil { + fmt.Println(err.Error()) + } +} +``` + +### GetOsBits + +

获取当前操作系统位数,返回32或64

+ +函数签名: + +```go +func GetOsBits() int +``` + +示例:[运行](https://go.dev/play/p/ml-_XH3gJbW) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + osBit := system.GetOsBits() + fmt.Println(osBit) // 32 or 64 +} +``` + +### StartProcess + +

创建进程。

+ +函数签名: + +```go +func StartProcess(command string, args ...string) (int, error) +``` + +示例:[运行](https://go.dev/play/p/5GVol6ryS_X) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "2") + if err != nil { + return + } + + fmt.Println(pid) +} +``` + +### StopProcess + +

停止进程。

+ +函数签名: + +```go +func StopProcess(pid int) error +``` + +示例:[运行](https://go.dev/play/p/jJZhRYGGcmD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "10") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = system.StopProcess(pid) + + fmt.Println(err) + + // Output: + // +} +``` + +### KillProcess + +

杀掉进程。

+ +函数签名: + +```go +func KillProcess(pid int) error +``` + +示例:[运行](https://go.dev/play/p/XKmvV-ExBWa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "10") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = system.KillProcess(pid) + + fmt.Println(err) + + // Output: + // +} +``` + +### GetProcessInfo + +

根据进程id获取进程信息。

+ +函数签名: + +```go +func GetProcessInfo(pid int) (*ProcessInfo, error) +``` + +示例:[运行](https://go.dev/play/p/NQDVywEYYx7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("ls", "-a") + if err != nil { + return + } + + processInfo, err := system.GetProcessInfo(pid) + if err != nil { + return + } + + fmt.Println(processInfo) +} +``` \ No newline at end of file diff --git a/docs/api/packages/tuple.md b/docs/api/packages/tuple.md new file mode 100644 index 00000000..5c50e614 --- /dev/null +++ b/docs/api/packages/tuple.md @@ -0,0 +1,1198 @@ +# Tuple + +tuple包实现一个元组数据类型。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/tuple/tuple.go](https://github.com/duke-git/lancet/blob/main/tuple/tuple.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/pointer" +) +``` + +
+ +## 目录 + +- [Tuple2](#Tuple2) +- [Tuple2_Unbox](#Tuple2_Unbox) +- [Zip2](#Zip2) +- [Unzip2](#Unzip2) +- [Tuple3](#Tuple3) +- [Tuple3_Unbox](#Tuple3_Unbox) +- [Zip3](#Zip3) +- [Unzip3](#Unzip3) +- [Tuple4](#Tuple4) +- [Tuple4_Unbox](#Tuple4_Unbox) +- [Zip4](#Zip4) +- [Unzip4](#Unzip4) +- [Tuple5](#Tuple5) +- [Tuple5_Unbox](#Tuple5_Unbox) +- [Zip5](#Zip5) +- [Unzip5](#Unzip5) +- [Tuple6](#Tuple6) +- [Tuple6_Unbox](#Tuple6_Unbox) +- [Zip6](#Zip6) +- [Unzip6](#Unzip6) +- [Tuple7](#Tuple7) +- [Tuple7_Unbox](#Tuple7_Unbox) +- [Zip7](#Zip7) +- [Unzip7](#Unzip7) +- [Tuple8](#TTuple8uple6) +- [Tuple8_Unbox](#Tuple8_Unbox) +- [Zip8](#Zip8) +- [Unzip8](#Unzip8) +- [Tuple9](#Tuple9) +- [Tuple9_Unbox](#Tuple9_Unbox) +- [Zip9](#Zip9) +- [Unzip9](#Unzip9) +- [Tuple10](#Tuple10) +- [Tuple10_Unbox](#Tuple10_Unbox) +- [Zip10](#Zip10) +- [Unzip10](#Unzip10) + +
+ + +## 文档 + +### Tuple2 + +

2元元组

+ +函数签名: + +```go +type Tuple2[A any, B any] struct { + FieldA A + FieldB B +} + +func NewTuple2[A any, B any](a A, b B) Tuple2[A, B] +``` + +示例:[运行](https://go.dev/play/p/3sHVqBQpLYN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple2(1, 0.1) + fmt.Printf("%v %v", t.FieldA, t.FieldB) + + // Output: 1 0.1 +} +``` + +### Tuple2_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple2[A, B]) Unbox() (A, B) +``` + +示例:[运行](https://go.dev/play/p/0fD1qfCVwjm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple2(1, 0.1) + v1, v2 := t.Unbox() + fmt.Printf("%v %v", v1, v2) + + // Output: 1 0.1 +} +``` + +### Zip2 + +

创建一个Tuple2元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] +``` + +示例:[运行](https://go.dev/play/p/4ncWJJ77Xio) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip2([]int{1}, []float64{0.1}) + fmt.Println(result) + + // Output: [{1 0.1}] +} +``` + +### Unzip2 + +

根据传入的Tuple2切片,创建一组和Tuple2元素相对应的切片。

+ +函数签名: + +```go +func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) +``` + +示例:[运行](https://go.dev/play/p/KBecr60feXb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2 := tuple.Unzip2([]tuple.Tuple2[int, float64]{{FieldA: 1, FieldB: 0.1}}) + + fmt.Printf("%v %v", v1, v2) + + // Output: [1] [0.1] +} +``` + +### Tuple3 + +

3元元组。

+ +函数签名: + +```go +type Tuple3[A any, B any, C any] struct { + FieldA A + FieldB B + FieldC C +} + +func NewTuple3[A any, B any, C any](a A, b B c C) Tuple3[A, B, C] +``` + +示例:[运行](https://go.dev/play/p/FtH2sdCLlCf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple3(1, 0.1, "a") + fmt.Printf("%v %v %v", t.FieldA, t.FieldB, t.FieldC) + + // Output: 1 0.1 a +} +``` + +### Tuple3_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple3[A, B, C]) Unbox() (A, B, C) +``` + +示例:[运行](https://go.dev/play/p/YojLy-id1BS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple2(1, 0.1, "a") + v1, v2, v3 := t.Unbox() + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: 1 0.1 a +} +``` + +### Zip3 + +

创建一个Tuple3元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] +``` + +示例:[运行](https://go.dev/play/p/97NgmsTILfu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip3([]int{1}, []float64{0.1}, []string{"a"}) + fmt.Println(result) + + // Output: [{1 0.1 a}] +} +``` + +### Unzip3 + +

根据传入的Tuple3切片,创建一组和Tuple3元素相对应的切片。

+ +函数签名: + +```go +func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) +``` + +示例:[运行](https://go.dev/play/p/bba4cpAa7KO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3 := tuple.Unzip3([]tuple.Tuple3[int, float64, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a"}, + }) + + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: [1] [0.1] [a] +} +``` + +### Tuple4 + +

4元元组。

+ +函数签名: + +```go +type Tuple4[A any, B any, C any, D any] struct { + FieldA A + FieldB B + FieldC C + FieldD D +} + +func NewTuple4[A any, B any, C any, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] +``` + +示例:[运行](https://go.dev/play/p/D2EqDz096tk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple4(1, 0.1, "a", true) + fmt.Printf("%v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD) + + // Output: 1 0.1 a true +} +``` + +### Tuple4_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple4[A, B, C, D]) Unbox() (A, B, C, D) +``` + +示例:[运行](https://go.dev/play/p/ACj9YuACGgW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple4(1, 0.1, "a", true) + v1, v2, v3, v4 := t.Unbox() + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: 1 0.1 a true +} +``` + +### Zip4 + +

创建一个Tuple4元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] +``` + +示例:[运行](https://go.dev/play/p/PEmTYVK5hL4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip4([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}) + fmt.Println(result) + + // Output: [{1 0.1 a true}] +} +``` + +### Unzip4 + +

根据传入的Tuple4切片,创建一组和Tuple4元素相对应的切片。

+ +函数签名: + +```go +func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) +``` + +示例:[运行](https://go.dev/play/p/rb8z4gyYSRN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4 := tuple.Unzip4([]tuple.Tuple4[int, float64, string, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true}, + }) + + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: [1] [0.1] [a] [true] +} +``` + +### Tuple5 + +

5元元组。

+ +函数签名: + +```go +type Tuple5[A any, B any, C any, D any, E any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E +} + +func NewTuple5[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] +``` + +示例:[运行](https://go.dev/play/p/2WndmVxPg-r) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple5(1, 0.1, "a", true, 2) + fmt.Printf("%v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE) + + // Output: 1 0.1 a true 2 +} +``` + +### Tuple5_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple5[A, B, C, D, E]) Unbox() (A, B, C, D, E) +``` + +示例:[运行](https://go.dev/play/p/GyIyZHjCvoS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple5(1, 0.1, "a", true, 2) + v1, v2, v3, v4, v5 := t.Unbox() + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: 1 0.1 a true 2 +} +``` + +### Zip5 + +

创建一个Tuple5元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip5[A any, B any, C any, D any, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] +``` + +示例:[运行](https://go.dev/play/p/fCAAJLMfBIP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip5([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2}] +} +``` + +### Unzip5 + +

根据传入的Tuple5切片,创建一组和Tuple5元素相对应的切片。

+ +函数签名: + +```go +func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) +``` + +示例:[运行](https://go.dev/play/p/gyl6vKfhqPb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5 := tuple.Unzip5([]tuple.Tuple5[int, float64, string, bool, int]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2}, + }) + + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: [1] [0.1] [a] [true] [2] +} +``` + +### Tuple6 + +

6元元组。

+ +函数签名: + +```go +type Tuple6[A any, B any, C any, D any, E any, F any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F +} + +func NewTuple6[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] +``` + +示例:[运行](https://go.dev/play/p/VjqcCwEJZbs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple6(1, 0.1, "a", true, 2, 2.2) + fmt.Printf("%v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF) + + // Output: 1 0.1 a true 2 2.2 +} +``` + +### Tuple6_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple6[A, B, C, D, E, F]) Unbox() (A, B, C, D, E, F) +``` + +示例:[运行](https://go.dev/play/p/FjIHV7lpxmW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple6(1, 0.1, "a", true, 2, 2.2) + v1, v2, v3, v4, v5, v6 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: 1 0.1 a true 2 2.2 +} +``` + +### Zip6 + +

创建一个Tuple6元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip6[A any, B any, C any, D any, E any, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] +``` + +示例:[运行](https://go.dev/play/p/oWPrnUYuFHo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip6([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2}] +} +``` + +### Unzip6 + +

根据传入的Tuple6切片,创建一组和Tuple6元素相对应的切片。

+ +函数签名: + +```go +func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) +``` + +示例:[运行](https://go.dev/play/p/l41XFqCyh5E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6 := tuple.Unzip6([]tuple.Tuple6[int, float64, string, bool, int, float32]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2}, + }) + + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: [1] [0.1] [a] [true] [2] [2.2] +} +``` + +### Tuple7 + +

7元元组。

+ +函数签名: + +```go +type Tuple7[A any, B any, C any, D any, E any, F any, G any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G +} + +func NewTuple7[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] +``` + +示例:[运行](https://go.dev/play/p/dzAgv_Ezub9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + fmt.Printf("%v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG) + + // Output: 1 0.1 a true 2 2.2 b +} +``` + +### Tuple7_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple7[A, B, C, D, E, F, G]) Unbox() (A, B, C, D, E, F, G) +``` + +示例:[运行](https://go.dev/play/p/R9I8qeDk0zs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + v1, v2, v3, v4, v5, v6, v7 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: 1 0.1 a true 2 2.2 b +} +``` + +### Zip7 + +

创建一个Tuple7元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip7[A any, B any, C any, D any, E any, F any, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] +``` + +示例:[运行](https://go.dev/play/p/WUJuo897Egf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip7([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b}] +} +``` + +### Unzip7 + +

根据传入的Tuple7切片,创建一组和Tuple7元素相对应的切片。

+ +函数签名: + +```go +func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) +``` + +示例:[运行](https://go.dev/play/p/hws_P1Fr2j3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7 := tuple.Unzip7([]tuple.Tuple7[int, float64, string, bool, int, float32, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] +} +``` + +### Tuple8 + +

8元元组。

+ +函数签名: + +```go +type Tuple8[A any, B any, C any, D any, E any, F any, G any, H any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H +} + +func NewTuple8[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H) Tuple8[A, B, C, D, E, F, G, H] +``` + +示例:[运行](https://go.dev/play/p/YA9S0rz3dRz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + fmt.Printf("%v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH) + + // Output: 1 0.1 a true 2 2.2 b c +} +``` + +### Tuple8_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple8[A, B, C, D, E, F, G, H]) Unbox() (A, B, C, D, E, F, G, H) +``` + +示例:[运行](https://go.dev/play/p/PRxLBBb4SMl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + v1, v2, v3, v4, v5, v6, v7, v8 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: 1 0.1 a true 2 2.2 b c +} +``` + +### Zip8 + +

创建一个Tuple8元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip8[A any, B any, C any, D any, E any, F any, G any, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] +``` + +示例:[运行](https://go.dev/play/p/8V9jWkuJfaQ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip8([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c}] +} +``` + +### Unzip8 + +

根据传入的Tuple8切片,创建一组和Tuple8元素相对应的切片。

+ +函数签名: + +```go +func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) +``` + +示例:[运行](https://go.dev/play/p/1SndOwGsZB4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8 := tuple.Unzip8([]tuple.Tuple8[int, float64, string, bool, int, float32, string, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] +} +``` + +### Tuple9 + +

9元元组。

+ +函数签名: + +```go + +type Tuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I +} + +func NewTuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I) Tuple9[A, B, C, D, E, F, G, H, I] + +``` + +示例:[运行](https://go.dev/play/p/yS2NGGtZpQr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + fmt.Printf("%v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} +``` + +### Tuple9_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unbox() (A, B, C, D, E, F, G, H, I) +``` + +示例:[运行](https://go.dev/play/p/oFJFGTAuOa8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + v1, v2, v3, v4, v5, v6, v7, v8, v9 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} +``` + +### Zip9 + +

创建一个Tuple9元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] +``` + +示例:[运行](https://go.dev/play/p/cgsL15QYnfz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip9([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3}] +} +``` + +### Unzip9 + +

根据传入的Tuple9切片,创建一组和Tuple9元素相对应的切片。

+ +函数签名: + +```go +func Unzip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) +``` + +示例:[运行](https://go.dev/play/p/91-BU_KURSA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8, v9 := tuple.Unzip9([]tuple.Tuple9[int, float64, string, bool, int, float32, string, string, int64]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] +} +``` + +### Tuple10 + +

10元元组。

+ +函数签名: + +```go + +type Tuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I + FieldJ J +} + +func NewTuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a A, b B, c C, d D, e E, f F, g G, h H, i I, j J) Tuple10[A, B, C, D, E, F, G, H, I, J] + +``` + +示例:[运行](https://go.dev/play/p/799qqZg0hUv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + type foo struct { + A string + } + t := tuple.NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI, t.FieldJ) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} +``` + +### Tuple10_Unbox + +

返回元组的字段值。

+ +函数签名: + +```go +func (t Tuple10[A, B, C, D, E, F, G, H, I, J]) Unbox() (A, B, C, D, E, F, G, H, I, J) +``` + +示例:[运行](https://go.dev/play/p/qfyx3x_X0Cu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + type foo struct { + A string + } + t := tuple.NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} +``` + +### Zip10 + +

创建一个Tuple10元组切片, 其中元组的元素和传入切片元素相对应。

+ +函数签名: + +```go +func Zip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, j []J) []Tuple10[A, B, C, D, E, F, G, H, I, J] +``` + +示例:[运行](https://go.dev/play/p/YSR-2cXnrY4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip10([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}, []bool{false}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3 false}] +} +``` + +### Unzip10 + +

根据传入的Tuple10切片,创建一组和Tuple10元素相对应的切片。

+ +函数签名: + +```go +func Unzip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](tuples []Tuple10[A, B, C, D, E, F, G, H, I, J]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I, []J) +``` + +示例:[运行](https://go.dev/play/p/-taQB6Wfre_z) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := tuple.Unzip10([]tuple.Tuple10[int, float64, string, bool, int, float32, string, string, int64, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3, FieldJ: false}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] [false] +} +``` diff --git a/docs/api/packages/validator.md b/docs/api/packages/validator.md new file mode 100644 index 00000000..b354ce05 --- /dev/null +++ b/docs/api/packages/validator.md @@ -0,0 +1,1569 @@ +# Validator + +validator 验证器包,包含常用字符串格式验证函数。 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/validator/validator.go](https://github.com/duke-git/lancet/blob/main/validator/validator.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/validator" +) +``` + +
+ +## 目录: + +- [ContainChinese](#ContainChinese) +- [ContainLetter](#ContainLetter) +- [ContainLower](#ContainLower) +- [ContainUpper](#ContainUpper) +- [IsAlpha](#IsAlpha) +- [IsAllUpper](#IsAllUpper) +- [IsAllLower](#IsAllLower) +- [IsASCII](#IsASCII) +- [IsBase64](#IsBase64) +- [IsChineseMobile](#IsChineseMobile) +- [IsChineseIdNum](#IsChineseIdNum) +- [IsChinesePhone](#IsChinesePhone) +- [IsCreditCard](#IsCreditCard) +- [IsDns](#IsDns) +- [IsEmail](#IsEmail) +- [IsEmptyString](#IsEmptyString) +- [IsInt](#IsInt) +- [IsFloat](#IsFloat) +- [IsNumber](#IsNumber) +- [IsIntStr](#IsIntStr) +- [IsFloatStr](#IsFloatStr) +- [IsNumberStr](#IsNumberStr) +- [IsJSON](#IsJSON) +- [IsRegexMatch](#IsRegexMatch) +- [IsIp](#IsIp) +- [IsIpV4](#IsIpV4) +- [IsIpV6](#IsIpV6) +- [IsIpPort](#IsIpPort) +- [IsStrongPassword](#IsStrongPassword) +- [IsUrl](#IsUrl) +- [IsWeakPassword](#IsWeakPassword) +- [IsZeroValue](#IsZeroValue) +- [IsGBK](#IsGBK) +- [IsPrintable](#IsPrintable) +- [IsBin](#IsBin) +- [IsHex](#IsHex) +- [IsBase64URL](#IsBase64URL) +- [IsJWT](#IsJWT) +- [IsVisa](#IsVisa) +- [IsMasterCard](#IsMasterCard) +- [IsAmericanExpress](#IsAmericanExpress) +- [IsUnionPay](#IsUnionPay) +- [IsChinaUnionPay](#IsChinaUnionPay) + +
+ +## 文档 + +### ContainChinese + +

验证字符串是否包含中文字符。

+ +函数签名: + +```go +func ContainChinese(s string) bool +``` + +示例:[运行](https://go.dev/play/p/7DpU0uElYeM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainChinese("你好") + result2 := validator.ContainChinese("你好hello") + result3 := validator.ContainChinese("hello") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### ContainLetter + +

验证字符串是否包含至少一个英文字母。

+ +函数签名: + +```go +func ContainLetter(str string) bool +``` + +示例:[运行](https://go.dev/play/p/lqFD04Yyewp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainLetter("你好") + result2 := validator.ContainLetter("&@#$%^&*") + result3 := validator.ContainLetter("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // false + // false + // true +} +``` + +### ContainLower + +

验证字符串是否包含至少一个英文小写字母。

+ +函数签名: + +```go +func ContainLower(str string) bool +``` + +示例:[运行](https://go.dev/play/p/Srqi1ItvnAA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainLower("abc") + result2 := validator.ContainLower("aBC") + result3 := validator.ContainLower("ABC") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### ContainUpper + +

验证字符串是否包含至少一个英文大写字母。

+ +函数签名: + +```go +func ContainUpper(str string) bool +``` + +示例:[运行](https://go.dev/play/p/CmWeBEk27-z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainUpper("ABC") + result2 := validator.ContainUpper("abC") + result3 := validator.ContainUpper("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsAlpha + +

验证字符串是否只包含英文字母。

+ +函数签名: + +```go +func IsAlpha(s string) bool +``` + +示例:[运行](https://go.dev/play/p/7Q5sGOz2izQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAlpha("abc") + result2 := validator.IsAlpha("ab1") + result3 := validator.IsAlpha("") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsAllUpper + +

验证字符串是否全是大写英文字母。

+ +函数签名: + +```go +func IsAllUpper(str string) bool +``` + +示例:[运行](https://go.dev/play/p/ZHctgeK1n4Z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAllUpper("ABC") + result2 := validator.IsAllUpper("ABc") + result3 := validator.IsAllUpper("AB1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsAllLower + +

验证字符串是否全是小写英文字母。

+ +函数签名: + +```go +func IsAllLower(str string) bool +``` + +示例:[运行](https://go.dev/play/p/GjqCnOfV6cM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAllLower("abc") + result2 := validator.IsAllLower("abC") + result3 := validator.IsAllLower("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsASCII + +

验证字符串全部为ASCII字符。

+ +函数签名: + +```go +func IsASCII(str string) bool +``` + +示例:[运行](https://go.dev/play/p/hfQNPLX0jNa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsASCII("ABC") + result2 := validator.IsASCII("123") + result3 := validator.IsASCII("") + result4 := validator.IsASCII("😄") + result5 := validator.IsASCII("你好") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // false + // false +} +``` + +### IsBase64 + +

验证字符串是否是base64编码。

+ +函数签名: + +```go +func IsBase64(base64 string) bool +``` + +示例:[运行](https://go.dev/play/p/sWHEySAt6hl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBase64("aGVsbG8=") + result2 := validator.IsBase64("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChineseMobile + +

验证字符串是否是中国手机号码。

+ +函数签名: + +```go +func IsChineseMobile(mobileNum string) bool +``` + +示例:[运行](https://go.dev/play/p/GPYUlGTOqe3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChineseMobile("13263527980") + result2 := validator.IsChineseMobile("434324324") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChineseIdNum + +

验证字符串是否是中国身份证号码。

+ +函数签名: + +```go +func IsChineseIdNum(id string) bool +``` + +示例:[运行](https://go.dev/play/p/d8EWhl2UGDF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChineseIdNum("210911192105130715") + result2 := validator.IsChineseIdNum("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChinesePhone + +

验证字符串是否是中国电话座机号码。

+ +函数签名: + +```go +func IsChinesePhone(phone string) bool +``` + +示例:[运行](https://go.dev/play/p/RUD_-7YZJ3I) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChinesePhone("010-32116675") + result2 := validator.IsChinesePhone("123-87562") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsCreditCard + +

验证字符串是否是信用卡号码。

+ +函数签名: + +```go +func IsCreditCard(creditCart string) bool +``` + +示例:[运行](https://go.dev/play/p/sNwwL6B0-v4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsCreditCard("4111111111111111") + result2 := validator.IsCreditCard("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsDns + +

验证字符串是否是有效dns。

+ +函数签名: + +```go +func IsDns(dns string) bool +``` + +示例:[运行](https://go.dev/play/p/jlYApVLLGTZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsDns("abc.com") + result2 := validator.IsDns("a.b.com") + result3 := validator.IsDns("http://abc.com") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsEmail + +

验证字符串是否是有效电子邮件地址。

+ +函数签名: + +```go +func IsEmail(email string) bool +``` + +示例:[运行](https://go.dev/play/p/Os9VaFlT33G) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsEmail("abc@xyz.com") + result2 := validator.IsEmail("a.b@@com") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsEmptyString + +

验证字符串是否是空字符串。

+ +函数签名: + +```go +func IsEmptyString(s string) bool +``` + +示例:[运行](https://go.dev/play/p/dpzgUjFnBCX) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsEmptyString("") + result2 := validator.IsEmptyString(" ") + result3 := validator.IsEmptyString("\t") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsInt + +

验证参数是否是整数(int, unit)。

+ +函数签名: + +```go +func IsInt(v any) bool +``` + +示例:[运行](https://go.dev/play/p/eFoIHbgzl-z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsInt("") + result2 := validator.IsInt("3") + result3 := validator.IsInt(0.1) + result4 := validator.IsInt(0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### IsFloat + +

验证参数是否是浮点数(float32, float34)。

+ +函数签名: + +```go +func IsFloat(v any) bool +``` + +示例:[运行](https://go.dev/play/p/vsyG-sxr99_Z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsFloat("") + result2 := validator.IsFloat("3") + result3 := validator.IsFloat(0) + result4 := validator.IsFloat(0.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### IsNumber + +

验证参数是否是数字(integer or float)。

+ +函数签名: + +```go +func IsNumber(v any) bool +``` + +示例:[运行](https://go.dev/play/p/mdJHOAvtsvF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsNumber("") + result2 := validator.IsNumber("3") + result3 := validator.IsNumber(0.1) + result4 := validator.IsNumber(0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // true + // true +} +``` + +### IsIntStr + +

验证字符串是否是可以转换为整数。

+ +函数签名: + +```go +func IsIntStr(s string) bool +``` + +示例:[运行](https://go.dev/play/p/jQRtFv-a0Rk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIntStr("+3") + result2 := validator.IsIntStr("-3") + result3 := validator.IsIntStr("3.") + result4 := validator.IsIntStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsFloatStr + +

验证字符串是否是可以转换为浮点数。

+ +函数签名: + +```go +func IsFloatStr(s string) bool +``` + +示例:[运行](https://go.dev/play/p/LOYwS_Oyl7U) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsFloatStr("3.") + result2 := validator.IsFloatStr("+3.") + result3 := validator.IsFloatStr("12") + result4 := validator.IsFloatStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### IsNumberStr + +

验证字符串是否是可以转换为数字。

+ +函数签名: + +```go +func IsNumberStr(s string) bool +``` + +示例:[运行](https://go.dev/play/p/LzaKocSV79u) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsNumberStr("3.") + result2 := validator.IsNumberStr("+3.") + result3 := validator.IsNumberStr("+3e2") + result4 := validator.IsNumberStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### IsAlphaNumeric + +

验证字符串是字母或数字。

+ +函数签名: + +```go +func IsAlphaNumeric(s string) bool +``` + +示例:[运行](https://go.dev/play/p/RHeESLrLg9c) + +```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 +} +``` + +### IsJSON + +

验证字符串是否是有效json。

+ +函数签名: + +```go +func IsJSON(str string) bool +``` + +示例:[运行](https://go.dev/play/p/8Kip1Itjiil) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsJSON("{}") + result2 := validator.IsJSON("{\"name\": \"test\"}") + result3 := validator.IsJSON("") + result4 := validator.IsJSON("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsRegexMatch + +

验证字符串是否可以匹配正则表达式。

+ +函数签名: + +```go +func IsRegexMatch(s, regex string) bool +``` + +示例:[运行](https://go.dev/play/p/z_XeZo_litG) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsRegexMatch("abc", `^[a-zA-Z]+$`) + result2 := validator.IsRegexMatch("ab1", `^[a-zA-Z]+$`) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsIp + +

验证字符串是否是ip地址。

+ +函数签名: + +```go +func IsIp(ipstr string) bool +``` + +示例:[运行](https://go.dev/play/p/FgcplDvmxoD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIp("127.0.0.1") + result2 := validator.IsIp("::0:0:0:0:0:0:1") + result3 := validator.IsIp("127.0.0") + result4 := validator.IsIp("::0:0:0:0:") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsIpV4 + +

验证字符串是否是ipv4地址。

+ +函数签名: + +```go +func IsIpV4(ipstr string) bool +``` + +示例:[运行](https://go.dev/play/p/zBGT99EjaIu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIpV4("127.0.0.1") + result2 := validator.IsIpV4("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsIpV6 + +

验证字符串是否是ipv6地址。

+ +函数签名: + +```go +func IsIpV6(ipstr string) bool +``` + +示例:[运行](https://go.dev/play/p/AHA0r0AzIdC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIpV6("127.0.0.1") + result2 := validator.IsIpV6("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} +``` + +### IsIpPort + +

检查字符串是否是ip:port格式。

+ +函数签名: + +```go +func IsIpPort(str string) bool +``` + +示例:[运行](https://go.dev/play/p/xUmls_b9L29) + +```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 +} +``` + +### IsStrongPassword + +

验证字符串是否是强密码:(alpha(lower+upper) + number + special chars(!@#$%^&*()?><))。

+ +函数签名: + +```go +func IsStrongPassword(password string, length int) bool +``` + +示例:[运行](https://go.dev/play/p/QHdVcSQ3uDg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsStrongPassword("abcABC", 6) + result2 := validator.IsStrongPassword("abcABC123@#$", 10) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} +``` + +### IsUrl + +

验证字符串是否是url。

+ +函数签名: + +```go +func IsUrl(str string) bool +``` + +示例:[运行](https://go.dev/play/p/pbJGa7F98Ka) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsUrl("abc.com") + result2 := validator.IsUrl("http://abc.com") + result3 := validator.IsUrl("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsWeakPassword + +

验证字符串是否是弱密码:(only letter or only number or letter + number) +。

+ +函数签名: + +```go +func IsWeakPassword(password string, length int) bool +``` + +示例:[运行](https://go.dev/play/p/wqakscZH5gH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsWeakPassword("abcABC") + result2 := validator.IsWeakPassword("abc123@#$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsZeroValue + +

判断传入的参数值是否为零值。

+ +函数签名: + +```go +func IsZeroValue(value any) bool +``` + +示例:[运行](https://go.dev/play/p/UMrwaDCi_t4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsZeroValue("") + result2 := validator.IsZeroValue(0) + result3 := validator.IsZeroValue("abc") + result4 := validator.IsZeroValue(1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsGBK + +

检查数据编码是否为gbk(汉字内部代码扩展规范)。该函数的实现取决于双字节是否在gbk的编码范围内,而utf-8编码格式的每个字节都在gbk编码范围内。因此,应该首先调用utf8.valid检查它是否是utf-8编码,然后调用IsGBK检查gbk编码。如示例所示。

+ +函数签名: + +```go +func IsGBK(data []byte) bool +``` + +示例:[运行](https://go.dev/play/p/E2nt3unlmzP) + +```go +import ( + "fmt" + "golang.org/x/text/encoding/simplifiedchinese" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + str := "你好" + gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str)) + + result := validator.IsGBK(gbkData) + + fmt.Println(result) + + // Output: + // true +} +``` + +### IsPrintable + +

检查字符串是否全部为可打印字符。

+ +函数签名: + +```go +func IsPrintable(str string) bool +``` + +示例:[运行](https://go.dev/play/p/Pe1FE2gdtTP) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsPrintable("ABC") + result2 := validator.IsPrintable("{id: 123}") + result3 := validator.IsPrintable("") + result4 := validator.IsPrintable("😄") + result5 := validator.IsPrintable("\u0000") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // true + // false +} +``` + +### IsBin + +

检查字符串是否是有效的二进制数。

+ +函数签名: + +```go +func IsBin(v string) bool +``` + +示例:[运行](https://go.dev/play/p/ogPeg2XJH4P) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBin("0101") + result2 := validator.IsBin("0b1101") + result3 := validator.IsBin("b1101") + result4 := validator.IsBin("1201") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsHex + +

检查字符串是否是有效的十六进制数。

+ +函数签名: + +```go +func IsHex(v string) bool +``` + +示例:[运行](https://go.dev/play/p/M2qpHbEwmm7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsHex("0xabcde") + result2 := validator.IsHex("0XABCDE") + result3 := validator.IsHex("cdfeg") + result4 := validator.IsHex("0xcdfeg") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsBase64URL + +

检查字符串是否是有效的base64 url。

+ +函数签名: + +```go +func IsBase64URL(v string) bool +``` + +示例:[运行](https://go.dev/play/p/vhl4mr8GZ6S) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ") + result2 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ==") + result3 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ=") + result4 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ===") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsJWT + +

检查字符串是否是有效的JSON Web Token (JWT)。

+ +函数签名: + +```go +func IsJWT(v string) bool +``` + +示例:[运行](https://go.dev/play/p/R6Op7heJbKI) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910") + result2 := validator.IsJWT("abc") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsVisa + +

检查字符串是否是有效的visa卡号。

+ +函数签名: + +```go +func IsVisa(v string) bool +``` + +示例:[运行](https://go.dev/play/p/SdS2keOyJsl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsVisa("4111111111111111") + result2 := validator.IsVisa("123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsMasterCard + +

检查字符串是否是有效的MasterCard卡号。

+ +函数签名: + +```go +func IsMasterCard(v string) bool +``` + +示例:[运行](https://go.dev/play/p/CwWBFRrG27b) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsMasterCard("5425233430109903") + result2 := validator.IsMasterCard("4111111111111111") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsAmericanExpress + +

检查字符串是否是有效的American Express卡号。

+ +函数签名: + +```go +func IsAmericanExpress(v string) bool +``` + +示例:[运行](https://go.dev/play/p/HIDFpcOdpkd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAmericanExpress("342883359122187") + result2 := validator.IsAmericanExpress("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsUnionPay + +

检查字符串是否是有效的美国银联卡号。

+ +函数签名: + +```go +func IsUnionPay(v string) bool +``` + +示例:[运行](https://go.dev/play/p/CUHPEwEITDf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsUnionPay("6221263430109903") + result2 := validator.IsUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChinaUnionPay + +

检查字符串是否是有效的中国银联卡号。

+ +函数签名: + +```go +func IsChinaUnionPay(v string) bool +``` + +示例:[运行](https://go.dev/play/p/yafpdxLiymu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChinaUnionPay("6250941006528599") + result2 := validator.IsChinaUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` diff --git a/docs/api/packages/xerror.md b/docs/api/packages/xerror.md new file mode 100644 index 00000000..d17df7a5 --- /dev/null +++ b/docs/api/packages/xerror.md @@ -0,0 +1,543 @@ +# Xerror + +xerror 错误处理逻辑封装 + +
+ +## 源码: + +- [https://github.com/duke-git/lancet/blob/main/xerror/xerror.go](https://github.com/duke-git/lancet/blob/main/xerror/xerror.go) + +
+ +## 用法: + +```go +import ( + "github.com/duke-git/lancet/v2/xerror" +) +``` + +
+ +## 目录 + +- [New](#New) +- [Wrap](#Wrap) +- [Unwrap](#Unwrap) +- [XError_Wrap](#XError_Wrap) +- [XError_Unwrap](#XError_Unwrap) +- [XError_With](#XError_With) +- [XError_Is](#XError_Is) +- [XError_Id](#XError_Id) +- [XError_Values](#XError_Values) +- [XError_StackTrace](#XError_StackTrace) +- [XError_Info](#XError_Info) +- [XError_Error](#XError_Error) +- [TryUnwrap](#TryUnwrap) +- [TryCatch](#TryCatch) + +
+ + +## 文档 + +### New + +

创建XError对象实例。

+ +函数签名: + +```go +type XError struct { + id string + message string + stack *stack + cause error + values map[string]any +} + +func New(format string, args ...any) *XError +``` + +示例:[运行](https://go.dev/play/p/w4oWZts7q7f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + fmt.Println(err.Error()) + + // Output: + // error +} +``` + +### Wrap + +

根据error对象创建XError对象实例,可添加message。

+ +函数签名: + +```go +func Wrap(cause error, message ...any) *XError +``` + +示例:[运行](https://go.dev/play/p/5385qT2dCi4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("wrong password") + wrapErr := xerror.Wrap(err, "error") + + fmt.Println(wrapErr.Error()) + + // Output: + // error: wrong password +} +``` + +### Unwrap + +

从error对象中解构出XError。

+ +函数签名: + +```go +func Unwrap(err error) *XError +``` + +示例:[运行](https://go.dev/play/p/LKMLep723tu) + +```go +package main + +import ( + "fmt" + "github.com/pkg/errors" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + wrapErr := errors.Wrap(err1, "oops") + + err := xerror.Unwrap(wrapErr) + + values := err.Values() + fmt.Println(values["level"]) + + // Output: + // high +} +``` + +### XError_Wrap + +

创建新的XError对象并将消息和id复制到新的对象中。

+ +函数签名: + +```go +func (e *XError) Wrap(cause error) *XError +``` + +示例:[运行](https://go.dev/play/p/RpjJ5u5sc97) + +```go +package main + +import ( + "fmt" + "errors" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + fmt.Println(err2.Error()) + + // Output: + // error: invalid username +} +``` + +### XError_Unwrap + +

解构XEerror为error对象。适配github.com/pkg/errors。

+ +函数签名: + +```go +func (e *XError) Unwrap() error +``` + +示例:[运行](https://go.dev/play/p/VUXJ8BST4c6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + err := err2.Unwrap() + + fmt.Println(err.Error()) + + // Output: + // invalid username +} +``` + +### XError_With + +

添加与XError对象的键和值。

+ +函数签名: + +```go +func (e *XError) With(key string, value any) *XError +``` + +示例:[运行](https://go.dev/play/p/ow8UISXX_Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} +``` + +### XError_Id + +

设置XError对象的id。

+ +函数签名: + +```go +func (e *XError) Id(id string) *XError +``` + +示例:[运行](https://go.dev/play/p/X6HBlsy58U9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").Id("e001") + err2 := xerror.New("error").Id("e001") + err3 := xerror.New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} +``` + +### XError_Is + +

检查目标error是否为XError,两个错误中的error.id是否匹配。

+ +函数签名: + +```go +func (e *XError) Is(target error) bool +``` + +示例:[运行](https://go.dev/play/p/X6HBlsy58U9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").Id("e001") + err2 := xerror.New("error").Id("e001") + err3 := xerror.New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} +``` + +### XError_Values + +

返回由With设置的键和值的映射。将合并所有XError键和值。

+ +函数签名: + +```go +func (e *XError) Values() map[string]any +``` + +示例:[运行](https://go.dev/play/p/ow8UISXX_Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} +``` + + +### XError_StackTrace + +

返回与pkg/error兼容的堆栈信息。

+ +函数签名: + +```go +func (e *XError) StackTrace() StackTrace +``` + +示例:[运行](https://go.dev/play/p/6FAvSQpa7pc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + + stacks := err.Stacks() + + fmt.Println(stacks[0].Func) + fmt.Println(stacks[0].Line) + + containFile := strings.Contains(stacks[0].File, "xxx.go") + fmt.Println(containFile) +} +``` + + +### XError_Info + +

返回可打印的XError对象信息。

+ +函数签名: + +```go +func (e *XError) Info() *errInfo +``` + +示例:[运行](https://go.dev/play/p/1ZX0ME1F-Jb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + cause := errors.New("error") + err := xerror.Wrap(cause, "invalid username").Id("e001").With("level", "high") + + errInfo := err.Info() + + fmt.Println(errInfo.Id) + fmt.Println(errInfo.Cause) + fmt.Println(errInfo.Values["level"]) + fmt.Println(errInfo.Message) + + // Output: + // e001 + // error + // high + // invalid username +} +``` + + +### XError_Error + +

实现标准库的error接口。

+ +函数签名: + +```go +func (e *XError) Error() string +``` + +示例:[运行](https://go.dev/play/p/w4oWZts7q7f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + fmt.Println(err.Error()) + + // Output: + // error +} +``` + +### TryUnwrap + +

检查error, 如果err为nil则展开,则它返回一个有效值,如果err不是nil则TryUnwrap使用err发生panic。

+ +函数签名: + +```go +func TryUnwrap[T any](val T, err error) T +``` + +示例:[运行](https://go.dev/play/p/acyZVkNZEeW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + result1 := xerror.TryUnwrap(strconv.Atoi("42")) + fmt.Println(result1) + + _, err := strconv.Atoi("4o2") + defer func() { + v := recover() + result2 := reflect.DeepEqual(err.Error(), v.(*strconv.NumError).Error()) + fmt.Println(result2) + }() + + xerror.TryUnwrap(strconv.Atoi("4o2")) + + // Output: + // 42 + // true +} +``` + +### TryCatch + +

简单实现的java风格异常处理(try-catch-finally)。try catch不符合go错误处理风格,谨慎使用。

+ +函数签名: + +```go +func NewTryCatch(ctx context.Context) *TryCatch + +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch + +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch + +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch + +func (tc *TryCatch) Do() +``` + +示例:[运行](https://go.dev/play/p/D5Mdb0mRj0P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + calledFinally := false + calledCatch := false + + tc := xerror.NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error in try block") + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +} +``` \ No newline at end of file diff --git a/docs/convertor.md b/docs/convertor.md deleted file mode 100644 index e11f1c16..00000000 --- a/docs/convertor.md +++ /dev/null @@ -1,349 +0,0 @@ -# Convertor -Package convertor contains some functions for data type convertion. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/convertor" -) -``` - -
- -## Index -- [ColorHexToRGB](#ColorHexToRGB) -- [ColorRGBToHex](#ColorRGBToHex) -- [ToBool](#ToBool) -- [ToBytes](#ToBytes) -- [ToChar](#ToChar) -- [ToInt](#ToInt) -- [ToJson](#ToJson) -- [ToString](#ToString) -- [StructToMap](#StructToMap) - -
- -## Documentation - - - -### ColorHexToRGB -

Convert color hex to color rgb.

- -Signature: - -```go -func ColorHexToRGB(colorHex string) (red, green, blue int) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - colorHex := "#003366" - r, g, b := ColorHexToRGB(colorHex) - fmt.Println(r, g, b) //0,51,102 -} -``` - - - -### ColorRGBToHex - -

Convert color rgb to color hex.

- -Signature: - -```go -func ColorRGBToHex(red, green, blue int) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - r := 0 - g := 51 - b := 102 - colorHex := ColorRGBToHex(r, g, b) - - fmt.Println(colorHex) //#003366 -} -``` - - - -### ToBool - -

Convert string to a boolean value. Use strconv.ParseBool

- -Signature: - -```go -func ToBool(s string) (bool, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v1, _ := convertor.ToBool("1") - fmt.Println(v1) //true - - v2, _ := convertor.ToBool("true") - fmt.Println(v2) //true - - v3, _ := convertor.ToBool("True") - fmt.Println(v3) //true - - v4, _ := convertor.ToBool("123") - fmt.Println(v4) //false -} -``` - - - -### ToBytes - -

Convert interface to byte slice.

- -Signature: - -```go -func ToBytes(data interface{}) ([]byte, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - bytesData, err := convertor.ToBytes("0") - if err != nil { - fmt.Println(err) - } - fmt.Println(bytesData) //[]bytes{3, 4, 0, 0} -} -``` - - - -### ToChar - -

Convert string to char slice.

- -Signature: - -```go -func ToChar(s string) []string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - chars := convertor.ToChar("") - fmt.Println(chars) //[]string{""} - - chars = convertor.ToChar("abc") - fmt.Println(chars) //[]string{"a", "b", "c"} - - chars = convertor.ToChar("1 2#3") - fmt.Println(chars) //[]string{"1", " ", "2", "#", "3"} -} -``` - - - -### ToFloat - -

Convert interface to a float64 value. If param is a invalid floatable, will return 0 and error.

- -Signature: - -```go -func ToFloat(value interface{}) (float64, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v, err := convertor.ToFloat("") - if err != nil { - fmt.Println(err) //strconv.ParseFloat: parsing "": invalid syntax - } - fmt.Println(v) //0 - - v, _ = convertor.ToFloat("-.11") - fmt.Println(v) //-0.11 -} -``` - - - -### ToInt - -

Convert interface to a int64 value. If param is a invalid intable, will return 0 and error.

- -Signature: - -```go -func ToInt(value interface{}) (int64, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v, err := convertor.ToInt("") - if err != nil { - fmt.Println(err) //strconv.ParseInt: parsing "": invalid syntax - } - fmt.Println(v) //0 - - v, _ = convertor.ToFloat(1.12) - fmt.Println(v) //1 -} -``` - - - -### ToJson - -

Convert interface to json string. If param can't be converted, will return "" and error.

- -Signature: - -```go -func ToJson(value interface{}) (string, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - var aMap = map[string]int{"a": 1, "b": 2, "c": 3} - jsonStr, _ := convertor.ToJson(aMap) - fmt.Printf("%q", jsonStr) //"{\"a\":1,\"b\":2,\"c\":3}" -} -``` - - - -### ToString - -

Convert interface to string.

- -Signature: - -```go -func ToString(value interface{}) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - fmt.Printf("%q", convertor.ToString(1)) //"1" - fmt.Printf("%q", convertor.ToString(1.1)) //"1.1" - fmt.Printf("%q", convertor.ToString([]int{1, 2, 3})) //"[1,2,3]" -} -``` - - - -### StructToMap - -

Convert struct to map, only convert exported field, struct field tag `json` should be set.

- -Signature: - -```go -func StructToMap(value interface{}) (map[string]interface{}, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - type People struct { - Name string `json:"name"` - age int - } - p := People{ - "test", - 100, - } - pm, _ := convertor.StructToMap(p) - - fmt.Printf("type: %T, value: %s", pm, pm) //type: map[string]interface {}, value: map[name:test] -} -``` \ No newline at end of file diff --git a/docs/convertor_zh-CN.md b/docs/convertor_zh-CN.md deleted file mode 100644 index f22877d4..00000000 --- a/docs/convertor_zh-CN.md +++ /dev/null @@ -1,351 +0,0 @@ -# Convertor -convertor转换器包支持一些常见的数据类型转换 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go) - -
- -## 用法: - -```go -import ( - "github.com/duke-git/lancet/convertor" -) -``` - -
- -## 目录 - -- [ColorHexToRGB](#ColorHexToRGB) -- [ColorRGBToHex](#ColorRGBToHex) -- [ToBool](#ToBool) -- [ToBytes](#ToBytes) -- [ToChar](#ToChar) -- [ToInt](#ToInt) -- [ToJson](#ToJson) -- [ToString](#ToString) -- [StructToMap](#StructToMap) - -
- -## 文档 - - - -### ColorHexToRGB -

颜色值十六进制转rgb

- -函数签名: - -```go -func ColorHexToRGB(colorHex string) (red, green, blue int) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - colorHex := "#003366" - r, g, b := ColorHexToRGB(colorHex) - fmt.Println(r, g, b) //0,51,102 -} -``` - - - -### ColorRGBToHex - -

颜色值rgb转十六进制

- -函数签名: - -```go -func ColorRGBToHex(red, green, blue int) string -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - r := 0 - g := 51 - b := 102 - colorHex := ColorRGBToHex(r, g, b) - - fmt.Println(colorHex) //#003366 -} -``` - - - -### ToBool - -

字符串转布尔类型,使用strconv.ParseBool

- -函数签名: - -```go -func ToBool(s string) (bool, error) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v1, _ := convertor.ToBool("1") - fmt.Println(v1) //true - - v2, _ := convertor.ToBool("true") - fmt.Println(v2) //true - - v3, _ := convertor.ToBool("True") - fmt.Println(v3) //true - - v4, _ := convertor.ToBool("123") - fmt.Println(v4) //false -} -``` - - - -### ToBytes - -

interface转字节切片.

- -函数签名: - -```go -func ToBytes(data interface{}) ([]byte, error) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - bytesData, err := convertor.ToBytes("0") - if err != nil { - fmt.Println(err) - } - fmt.Println(bytesData) //[]bytes{3, 4, 0, 0} -} -``` - - - -### ToChar - -

字符串转字符切片

- -函数签名: - -```go -func ToChar(s string) []string -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - chars := convertor.ToChar("") - fmt.Println(chars) //[]string{""} - - chars = convertor.ToChar("abc") - fmt.Println(chars) //[]string{"a", "b", "c"} - - chars = convertor.ToChar("1 2#3") - fmt.Println(chars) //[]string{"1", " ", "2", "#", "3"} -} -``` - - - -### ToFloat - -

将interface转成float64类型,如果参数无法转换,会返回0和error

- -函数签名: - -```go -func ToFloat(value interface{}) (float64, error) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v, err := convertor.ToFloat("") - if err != nil { - fmt.Println(err) //strconv.ParseFloat: parsing "": invalid syntax - } - fmt.Println(v) //0 - - v, _ = convertor.ToFloat("-.11") - fmt.Println(v) //-0.11 -} -``` - - - -### ToInt - -

将interface转成intt64类型,如果参数无法转换,会返回0和error

- -函数签名: - -```go -func ToInt(value interface{}) (int64, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - v, err := convertor.ToInt("") - if err != nil { - fmt.Println(err) //strconv.ParseInt: parsing "": invalid syntax - } - fmt.Println(v) //0 - - v, _ = convertor.ToFloat(1.12) - fmt.Println(v) //1 -} -``` - - - -### ToJson - -

将interface转成json字符串,如果参数无法转换,会返回""和error

- -函数签名: - -```go -func ToJson(value interface{}) (string, error) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - var aMap = map[string]int{"a": 1, "b": 2, "c": 3} - jsonStr, _ := convertor.ToJson(aMap) - fmt.Printf("%q", jsonStr) //"{\"a\":1,\"b\":2,\"c\":3}" -} -``` - - - -### ToString - -

将interface转成字符串

- -函数签名: - -```go -func ToString(value interface{}) string -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - fmt.Printf("%q", convertor.ToString(1)) //"1" - fmt.Printf("%q", convertor.ToString(1.1)) //"1.1" - fmt.Printf("%q", convertor.ToString([]int{1, 2, 3})) //"[1,2,3]" -} -``` - - - -### StructToMap - -

将struct转成map,只会转换struct中可导出的字段。struct中导出字段需要设置json tag标记

- -函数签名: - -```go -func StructToMap(value interface{}) (map[string]interface{}, error) -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/convertor" -) - -func main() { - type People struct { - Name string `json:"name"` - age int - } - p := People{ - "test", - 100, - } - pm, _ := convertor.StructToMap(p) - - fmt.Printf("type: %T, value: %s", pm, pm) //type: map[string]interface {}, value: map[name:test] -} -``` \ No newline at end of file diff --git a/docs/cryptor.md b/docs/cryptor.md deleted file mode 100644 index 5b98e726..00000000 --- a/docs/cryptor.md +++ /dev/null @@ -1,1033 +0,0 @@ -# Cryptor -Package cryptor contains some functions for data encryption and decryption. Support base64, md5, hmac, aes, des, rsa. - -
- -## Source: - -- [https://github.com/duke-git/lancet/blob/main/cryptor/aes.go](https://github.com/duke-git/lancet/blob/main/cryptor/aes.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/des.go](https://github.com/duke-git/lancet/blob/main/cryptor/des.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/basic.go](https://github.com/duke-git/lancet/blob/main/cryptor/basic.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/rsa.go](https://github.com/duke-git/lancet/blob/main/cryptor/rsa.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/cryptor" -) -``` - -
- -## Index - -- [AesEcbEncrypt](#AesEcbEncrypt) -- [AesEcbDecrypt](#AesEcbDecrypt) -- [AesCbcEncrypt](#AesCbcEncrypt) -- [AesCbcDecrypt](#AesCbcDecrypt) -- [AesCtrCrypt](#AesCtrCrypt) -- [AesCfbEncrypt](#AesCfbEncrypt) -- [AesCfbDecrypt](#AesCfbDecrypt) -- [AesOfbEncrypt](#AesOfbEncrypt) -- [AesOfbDecrypt](#AesOfbDecrypt) -- [Base64StdEncode](#Base64StdEncode) -- [Base64StdDecode](#Base64StdDecode) -- [DesEcbEncrypt](#DesEcbEncrypt) -- [DesEcbDecrypt](#DesEcbDecrypt) -- [DesCbcEncrypt](#DesCbcEncrypt) -- [DesCbcDecrypt](#DesCbcDecrypt) -- [DesCtrCrypt](#DesCtrCrypt) -- [DesCfbEncrypt](#DesCfbEncrypt) -- [DesCfbDecrypt](#DesCfbDecrypt) -- [DesOfbEncrypt](#DesOfbEncrypt) -- [DesOfbDecrypt](#DesOfbDecrypt) -- [HmacMd5](#HmacMd5) -- [HmacSha1](#HmacSha1) -- [HmacSha256](#HmacSha256) -- [HmacSha512](#HmacSha512) - -- [Md5String](#Md5String) -- [Md5File](#Md5File) -- [Sha1](#Sha1) -- [Sha256](#Sha256) -- [Sha512](#Sha512) -- [GenerateRsaKey](#GenerateRsaKey) -- [RsaEncrypt](#RsaEncrypt) -- [RsaDecrypt](#RsaDecrypt) - - -
- -## Documentation - - - -### AesEcbEncrypt - -

Encrypt data with key use AES ECB algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesEcbEncrypt(data, key []byte) []byte -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### AesEcbDecrypt - -

Decrypt data with key use AES ECB algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesEcbDecrypt(encrypted, key []byte) []byte -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCbcEncrypt - -

Encrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesCbcEncrypt(data, key []byte) []byte -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### AesCbcDecrypt - -

Decrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesCbcDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCtrCrypt - -

Encrypt or decrypt data with key use AES CTR algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesCtrCrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCtrCrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCtrCrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCfbEncrypt - -

Encrypt data with key use AES CFB algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesCfbEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### AesCfbDecrypt - -

Decrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesCfbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesOfbEncrypt - -

Enecrypt data with key use AES OFB algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesOfbEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### AesOfbDecrypt - -

Decrypt data with key use AES OFB algorithm. Length of `key` param should be 16, 24 or 32.

- -Signature: - -```go -func AesOfbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesOfbDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### Base64StdEncode - -

Encode string with base64 encoding.

- -Signature: - -```go -func Base64StdEncode(s string) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - base64Str := cryptor.Base64StdEncode("hello world") - fmt.Println(base64Str) //aGVsbG8gd29ybGQ= -} -``` - - - -### Base64StdDecode - -

Decode a base64 encoded string.

- -Signature: - -```go -func Base64StdDecode(s string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - str := cryptor.Base64StdDecode("aGVsbG8gd29ybGQ=") - fmt.Println(str) //hello world -} -``` - - - -### DesEcbEncrypt - -

Encrypt data with key use DES ECB algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesEcbEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### DesEcbDecrypt - -

Decrypt data with key use DES ECB algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesEcbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesEcbEncrypt([]byte(data), []byt(key) - decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCbcEncrypt - -

Encrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesCbcEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCbcEncrypt([]byte(data), []byt(key) - - fmt.Println(string(encrypted)) -} -``` - - - -### DesCbcDecrypt - -

Decrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesCbcDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCbcEncrypt([]byte(data), []byt(key) - decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCtrCrypt - -

Encrypt or decrypt data with key use DES CTR algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesCtrCrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCtrCrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesCtrCrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCfbEncrypt - -

Encrypt data with key use DES CFB algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesCfbEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCfbEncrypt([]byte(data), []byt(key) - fmt.Println(string(encrypted)) -} -``` - - - -### DesCfbDecrypt - -

Decrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesCfbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCfbEncrypt([]byte(data), []byt(key) - decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesOfbEncrypt - -

Enecrypt data with key use DES OFB algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesOfbEncrypt(data, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### DesOfbDecrypt - -

Decrypt data with key use DES OFB algorithm. Length of `key` param should be 8.

- -Signature: - -```go -func DesOfbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### HmacMd5 - -

Get the md5 hmac hash of string.

- -Signature: - -```go -func HmacMd5(data, key string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacMd5("hello world", "12345")) - fmt.Println(s) //5f4c9faaff0a1ad3007d9ddc06abe36d -} -``` - - - -### HmacSha1 - -

Get the sha1 hmac hash of string.

- -Signature: - -```go -func HmacSha1(data, key string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha1("hello world", "12345")) - fmt.Println(s) //3826f812255d8683f051ee97346d1359234d5dbd -} -``` - - - -### HmacSha256 - -

Get the sha256 hmac hash of string

- -Signature: - -```go -func HmacSha256(data, key string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha256("hello world", "12345")) - fmt.Println(s) //9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8 -} -``` - - - -### HmacSha512 - -

Get the sha512 hmac hash of string.

- -Signature: - -```go -func HmacSha512(data, key string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha512("hello world", "12345")) - fmt.Println(s) - //5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175 -} -``` - - - -### Md5String - -

Get the md5 value of string.

- -Signature: - -```go -func Md5String(s string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Md5String("hello")) - fmt.Println(s) //5d41402abc4b2a76b9719d911017c592 -} -``` - - - -### Md5File - -

Get the md5 value of file.

- -Signature: - -```go -func Md5File(filepath string) (string, error) -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Md5File("./main.go")) - fmt.Println(s) -} -``` - - - -### Sha1 - -

Get the sha1 value of string.

- -Signature: - -```go -func Sha1(data string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha1("hello world")) - fmt.Println(s) //2aae6c35c94fcfb415dbe95f408b9ce91ee846ed -} -``` - - - -### Sha256 - -

Get the sha256 value of string.

- -Signature: - -```go -func Sha256(data string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha256("hello world")) - fmt.Println(s) //b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 -} -``` - - - -### Sha512 - -

Get the sha512 value of string.

- -Signature: - -```go -func Sha512(data string) string -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha512("hello world")) - fmt.Println(s) //309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f -} -``` - - - -### GenerateRsaKey - -

Create the rsa public and private key file in current directory.

- -Signature: - -```go -func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### RsaEncrypt - -

Encrypt data with public key file useing ras algorithm.

- -Signature: - -```go -func RsaEncrypt(data []byte, pubKeyFileName string) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } - - data := []byte("hello world") - encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") - decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### RsaDecrypt - -

Decrypt data with private key file useing ras algorithm.

- -Signature: - -```go -func RsaDecrypt(data []byte, privateKeyFileName string) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } - - data := []byte("hello world") - encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") - decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") - - fmt.Println(string(decrypted)) //hello world -} -``` - - - diff --git a/docs/cryptor_zh-CN.md b/docs/cryptor_zh-CN.md deleted file mode 100644 index 6847bcb4..00000000 --- a/docs/cryptor_zh-CN.md +++ /dev/null @@ -1,1028 +0,0 @@ -# Cryptor -cryptor加密包支持数据加密和解密,获取md5,hash值。支持base64, md5, hmac, aes, des, rsa。 - -
- -## 源码: - -- [https://github.com/duke-git/lancet/blob/main/cryptor/aes.go](https://github.com/duke-git/lancet/blob/main/cryptor/aes.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/des.go](https://github.com/duke-git/lancet/blob/main/cryptor/des.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/basic.go](https://github.com/duke-git/lancet/blob/main/cryptor/basic.go) -- [https://github.com/duke-git/lancet/blob/main/cryptor/rsa.go](https://github.com/duke-git/lancet/blob/main/cryptor/rsa.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/cryptor" -) -``` - -
- -## 目录 -- [AesEcbEncrypt](#AesEcbEncrypt) -- [AesEcbDecrypt](#AesEcbDecrypt) -- [AesCbcEncrypt](#AesCbcEncrypt) -- [AesCbcDecrypt](#AesCbcDecrypt) -- [AesCtrCrypt](#AesCtrCrypt) -- [AesCfbEncrypt](#AesCfbEncrypt) -- [AesCfbDecrypt](#AesCfbDecrypt) -- [AesOfbEncrypt](#AesOfbEncrypt) -- [AesOfbDecrypt](#AesOfbDecrypt) -- [Base64StdEncode](#Base64StdEncode) -- [Base64StdDecode](#Base64StdDecode) - -- [DesEcbEncrypt](#DesEcbEncrypt) -- [DesEcbDecrypt](#DesEcbDecrypt) -- [DesCbcEncrypt](#DesCbcEncrypt) -- [DesCbcDecrypt](#DesCbcDecrypt) -- [DesCtrCrypt](#DesCtrCrypt) -- [DesCfbEncrypt](#DesCfbEncrypt) -- [DesCfbDecrypt](#DesCfbDecrypt) -- [DesOfbEncrypt](#DesOfbEncrypt) -- [DesOfbDecrypt](#DesOfbDecrypt) - -- [HmacMd5](#HmacMd5) -- [HmacSha1](#HmacSha1) -- [HmacSha256](#HmacSha256) -- [HmacSha512](#HmacSha512) -- [Md5String](#Md5String) -- [Md5File](#Md5File) -- [Sha1](#Sha1) -- [Sha256](#Sha256) -- [Sha512](#Sha512) -- [GenerateRsaKey](#GenerateRsaKey) -- [RsaEncrypt](#RsaEncrypt) -- [RsaDecrypt](#RsaDecrypt) - - -
- -## 文档 - - - -### AesEcbEncrypt - -

使用AES ECB算法模式加密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesEcbEncrypt(data, key []byte) []byte -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### AesEcbDecrypt - -

使用AES ECB算法模式解密数据. 参数`key`的长度是16, 24 or 32。 - -函数签名: - -```go -func AesEcbDecrypt(encrypted, key []byte) []byte -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCbcEncrypt - -

使用AES CBC算法模式加密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesCbcEncrypt(data, key []byte) []byte -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### AesCbcDecrypt - -

使用AES CBC算法模式解密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesCbcDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCtrCrypt - -

使用AES CTR算法模式加密/解密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesCtrCrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCtrCrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCtrCrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesCfbEncrypt - -

使用AES CFB算法模式加密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesCfbEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### AesCfbDecrypt - -

使用AES CFB算法模式解密数据. 参数`key`的长度是16, 24 or 32。

- -函数签名: - -```go -func AesCfbDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### AesOfbEncrypt - -

使用AES OFB算法模式加密数据. 参数`key`的长度是16, 24 or 32

- -函数签名: - -```go -func AesOfbEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### AesOfbDecrypt - -

使用AES OFB算法模式解密数据. 参数`key`的长度是16, 24 or 32

- -函数签名: - -```go -func AesOfbDecrypt(encrypted, key []byte) []byte -``` - -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefghijklmnop" - encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.AesOfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### Base64StdEncode - -

将字符串base64编码

- -函数签名: - -```go -func Base64StdEncode(s string) string -``` -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - base64Str := cryptor.Base64StdEncode("hello world") - fmt.Println(base64Str) //aGVsbG8gd29ybGQ= -} -``` - - - -### Base64StdDecode - -

解码base64字符串

- -函数签名: - -```go -func Base64StdDecode(s string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - str := cryptor.Base64StdDecode("aGVsbG8gd29ybGQ=") - fmt.Println(str) //hello world -} -``` - - - -### DesEcbEncrypt - -

使用DES ECB算法模式加密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesEcbEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### DesEcbDecrypt - -

使用DES ECB算法模式解密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesEcbDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCbcEncrypt - -

使用DES CBC算法模式加密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesCbcEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) - - fmt.Println(string(encrypted)) -} -``` - - - -### DesCbcDecrypt - -

使用DES CBC算法模式解密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesCbcDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCtrCrypt - -

使用DES CTR算法模式加密/解密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesCtrCrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCtrCrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesCtrCrypt(encrypted, []byte(key)) - - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesCfbEncrypt - -

使用DES CFB算法模式加密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesCfbEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### DesCfbDecrypt - -

使用DES CFB算法模式解密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesCfbDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### DesOfbEncrypt - -

使用DES OFB算法模式加密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesOfbEncrypt(data, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) - fmt.Println(string(encrypted)) -} -``` - - - -### DesOfbDecrypt - -

使用DES OFB算法模式解密数据. 参数`key`的长度是8

- -函数签名: - -```go -func DesOfbDecrypt(encrypted, key []byte) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - data := "hello world" - key := "abcdefgh" - encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) - decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### HmacMd5 - -

获取字符串md5 hmac值

- -函数签名: - -```go -func HmacMd5(data, key string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacMd5("hello world", "12345")) - fmt.Println(s) //5f4c9faaff0a1ad3007d9ddc06abe36d -} -``` - - - -### HmacSha1 - -

获取字符串sha1 hmac值

- -函数签名: - -```go -func HmacSha1(data, key string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha1("hello world", "12345")) - fmt.Println(s) //3826f812255d8683f051ee97346d1359234d5dbd -} -``` - - - -### HmacSha256 - -

获取字符串sha256 hmac值

- -函数签名: - -```go -func HmacSha256(data, key string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha256("hello world", "12345")) - fmt.Println(s) //9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8 -} -``` - - - -### HmacSha512 - -

获取字符串sha512 hmac值

- -函数签名: - -```go -func HmacSha512(data, key string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.HmacSha512("hello world", "12345")) - fmt.Println(s) - //5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175 -} -``` - - - -### Md5String - -

获取字符串md5值

- -函数签名: - -```go -func Md5String(s string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Md5String("hello")) - fmt.Println(s) //5d41402abc4b2a76b9719d911017c592 -} -``` - - - -### Md5File - -

获取文件md5值.

- -函数签名: - -```go -func Md5File(filepath string) (string, error) -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Md5File("./main.go")) - fmt.Println(s) -} -``` - - - -### Sha1 - -

获取字符串sha1值

- -函数签名: - -```go -func Sha1(data string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha1("hello world")) - fmt.Println(s) //2aae6c35c94fcfb415dbe95f408b9ce91ee846ed -} -``` - - - -### Sha256 - -

获取字符串sha256值

- -函数签名: - -```go -func Sha256(data string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha256("hello world")) - fmt.Println(s) //b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 -} -``` - - - -### Sha512 - -

获取字符串sha512值

- -函数签名: - -```go -func Sha512(data string) string -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - s := cryptor.Sha512("hello world")) - fmt.Println(s) //309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f -} -``` - - - -### GenerateRsaKey - -

在当前目录下创建rsa私钥文件和公钥文件

- -函数签名: - -```go -func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### RsaEncrypt - -

用公钥文件ras加密数据

- -函数签名: - -```go -func RsaEncrypt(data []byte, pubKeyFileName string) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } - data := []byte("hello world") - encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") - decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") - fmt.Println(string(decrypted)) //hello world -} -``` - - - -### RsaDecrypt - -

用私钥文件rsa解密数据

- -函数签名: - -```go -func RsaDecrypt(data []byte, privateKeyFileName string) []byte -``` - -列子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/cryptor" -) - -func main() { - err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") - if err != nil { - fmt.Println(err) - } - data := []byte("hello world") - encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") - decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") - fmt.Println(string(decrypted)) //hello world -} -``` - - - diff --git a/docs/datetime.md b/docs/datetime.md deleted file mode 100644 index 4ec076ff..00000000 --- a/docs/datetime.md +++ /dev/null @@ -1,669 +0,0 @@ -# Datetime -Package datetime supports date and time format and compare. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/datetime/datetime.go](https://github.com/duke-git/lancet/blob/main/datetime/datetime.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/datetime" -) -``` - -
- -## Index -- [AddDay](#AddDay) -- [AddHour](#AddHour) -- [AddMinute](#AddMinute) -- [BeginOfMinute](#BeginOfMinute) -- [BeginOfHour](#BeginOfHour) -- [BeginOfDay](#BeginOfDay) -- [BeginOfWeek](#BeginOfWeek) -- [BeginOfMonth](#BeginOfMonth) -- [BeginOfYear](#BeginOfYear) - -- [EndOfMinute](#EndOfMinute) -- [EndOfHour](#EndOfHour) -- [EndOfDay](#EndOfDay) -- [EndOfWeek](#EndOfWeek) -- [EndOfMonth](#EndOfMonth) -- [EndOfYear](#EndOfYear) -- [GetNowDate](#GetNowDate) -- [GetNowTime](#GetNowTime) -- [GetNowDateTime](#GetNowDateTime) -- [GetZeroHourTimestamp](#GetZeroHourTimestamp) -- [GetNightTimestamp](#GetNightTimestamp) -- [FormatTimeToStr](#FormatTimeToStr) - -- [FormatStrToTime](#FormatStrToTime) - -
- -## Documentation - -## Note: -1. 'format' string param in func FormatTimeToStr and FormatStrToTime function should be one of flows: -- yyyy-mm-dd hh:mm:ss -- yyyy-mm-dd hh:mm -- yyyy-mm-dd hh -- yyyy-mm-dd -- yyyy-mm -- mm-dd -- dd-mm-yy hh:mm:ss -- yyyy/mm/dd hh:mm:ss -- yyyy/mm/dd hh:mm -- yyyy-mm-dd hh -- yyyy/mm/dd -- yyyy/mm -- mm/dd -- dd/mm/yy hh:mm:ss -- yyyy -- mm -- hh:mm:ss -- mm:ss - - -### AddDay -

Add or sub days to time.

- -Signature: - -```go -func AddDay(t time.Time, day int64) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Days := datetime.AddDay(now, 2) - before2Days := datetime.AddDay(now, -2) - - fmt.Println(after2Days, before2Days) -} -``` - - -### AddHour -

Add or sub hours to time.

- -Signature: - -```go -func AddHour(t time.Time, hour int64) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Hours := datetime.AddHour(now, 2) - before2Hours := datetime.AddHour(now, -2) - - fmt.Println(after2Hours, after2Hours) -} -``` - -### AddMinute -

Add or sub minutes to time.

- -Signature: - -```go -func AddMinute(t time.Time, minute int64) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Minute := datetime.AddMinute(now, 2) - before2Minute := datetime.AddMinute(now, -2) - - fmt.Println(after2Minute, before2Minute) -} -``` - -### BeginOfMinute -

Return beginning minute time of day.

- -Signature: - -```go -func BeginOfMinute(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfMinute(td) - fmt.Println(bm) //2022-02-15 15:48:00 +0800 CST -} -``` - -### BeginOfHour -

Return zero time of day.

- -Signature: - -```go -func BeginOfHour(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfHour(td) - fmt.Println(bm) //2022-02-15 15:00:00 +0800 CST -} -``` - -### BeginOfDay -

Return begin time of day.

- -Signature: - -```go -func BeginOfDay(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfDay(td) - fmt.Println(bm) //2022-02-15 00:00:00 +0800 CST -} -``` - - - -### BeginOfWeek -

Return beginning time of week, week begin from Sunday.

- -Signature: - -```go -func BeginOfWeek(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfWeek(td) - fmt.Println(bm) //2022-02-13 00:00:00 +0800 CST -} -``` - - - -### BeginOfMonth -

Return beginning time of month

- -Signature: - -```go -func BeginOfMonth(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfMonth(td) - fmt.Println(bm) //2022-02-01 00:00:00 +0800 CST -} -``` - - -### BeginOfYear -

Return beginning time of year.

- -Signature: - -```go -func BeginOfYear(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfYear(td) - fmt.Println(bm) //2022-01-01 00:00:00 +0800 CST -} -``` - - - -### EndOfMinute -

Return end time minute of day.

- -Signature: - -```go -func EndOfMinute(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfMinute(td) - fmt.Println(bm) //2022-02-15 15:48:59.999999999 +0800 CST -} -``` - -### EndOfHour -

Return end time hour of day.

- -Signature: - -```go -func EndOfHour(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfHour(td) - fmt.Println(bm) //2022-02-15 15:59:59.999999999 +0800 CST -} -``` - -### EndOfDay -

Return end time hour of day.

- -Signature: - -```go -func EndOfDay(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfDay(td) - fmt.Println(bm) //2022-02-15 23:59:59.999999999 +0800 CST -} -``` - - - -### EndOfWeek -

Return end time of week, week end with Saturday.

- -Signature: - -```go -func EndOfWeek(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfWeek(td) - fmt.Println(bm) //2022-02-19 23:59:59.999999999 +0800 CST -} -``` - - - -### EndOfMonth -

Return end time of month

- -Signature: - -```go -func EndOfMonth(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfMonth(td) - fmt.Println(bm) //2022-02-28 23:59:59.999999999 +0800 CST -} -``` - - -### EndOfYear -

Return beginning time of year.

- -Signature: - -```go -func EndOfYear(t time.Time) time.Time -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfYear(td) - fmt.Println(bm) //2022-12-31 23:59:59.999999999 +0800 CST -} -``` - - -### GetNowDate -

Get current date string, format is yyyy-mm-dd.

- -Signature: - -```go -func GetNowDate() string -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - currentDate := datetime.GetNowDate() - fmt.Println(currentDate) // 2022-01-28 -} -``` - - -### GetNowTime -

Get current time string, format is hh:mm:ss.

- -Signature: - -```go -func GetNowTime() string -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - currentTime := datetime.GetNowTime() - fmt.Println(currentDate) // 15:57:33 -} -``` - - -### GetNowDateTime -

Get current date time string, format is yyyy-mm-dd hh:mm:ss.

- -Signature: - -```go -func GetNowDateTime() string -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - current := datetime.GetNowDateTime() - fmt.Println(current) // 2022-01-28 15:59:33 -} -``` - - -### GetZeroHourTimestamp -

Return timestamp of zero hour (timestamp of 00:00).

- -Signature: - -```go -func GetZeroHourTimestamp() int64 -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - zeroTime := datetime.GetZeroHourTimestamp() - fmt.Println(zeroTime) // 1643299200 -} -``` - - -### GetNightTimestamp -

Return timestamp of zero hour (timestamp of 23:59).

- -Signature: - -```go -func GetNightTimestamp() int64 -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - nightTime := datetime.GetNightTimestamp() - fmt.Println(nightTime) // 1643385599 -} -``` - -### FormatTimeToStr -

Format time to string, `format` param specification see note 1.

- -Signature: - -```go -func FormatTimeToStr(t time.Time, format string) string -``` -Example: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - timeStr := datetime.FormatTimeToStr(now, "yyyy/mm/dd hh:mm:ss") - fmt.Println(timeStr) //2022/01/28 16:07:44 -} -``` - - -### FormatStrToTime -

Format string to time, `format` param specification see note 1.

- -Signature: - -```go -func FormatStrToTime(str, format string) (time.Time, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/datetime" -) - -func main() { - time := datetime.FormatStrToTime("2006-01-02 15:04:05", "yyyy/mm/dd hh:mm:ss") - fmt.Println(time) -} -``` - - - diff --git a/docs/datetime_zh-CN.md b/docs/datetime_zh-CN.md deleted file mode 100644 index db6c8899..00000000 --- a/docs/datetime_zh-CN.md +++ /dev/null @@ -1,668 +0,0 @@ -# Datetime -datetime日期时间处理包,格式化日期,比较日期。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/datetime/datetime.go](https://github.com/duke-git/lancet/blob/main/datetime/datetime.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/datetime" -) -``` - -
- -## 目录 -- [AddDay](#AddDay) -- [AddHour](#AddHour) -- [AddMinute](#AddMinute) -- [BeginOfMinute](#BeginOfMinute) -- [BeginOfHour](#BeginOfHour) -- [BeginOfDay](#BeginOfDay) -- [BeginOfWeek](#BeginOfWeek) -- [BeginOfMonth](#BeginOfMonth) -- [BeginOfYear](#BeginOfYear) - -- [EndOfMinute](#EndOfMinute) -- [EndOfHour](#EndOfHour) -- [EndOfDay](#EndOfDay) -- [EndOfWeek](#EndOfWeek) -- [EndOfMonth](#EndOfMonth) -- [EndOfYear](#EndOfYear) -- [GetNowDate](#GetNowDate) -- [GetNowTime](#GetNowTime) -- [GetNowDateTime](#GetNowDateTime) -- [GetZeroHourTimestamp](#GetZeroHourTimestamp) -- [GetNightTimestamp](#GetNightTimestamp) -- [FormatTimeToStr](#FormatTimeToStr) -- [FormatStrToTime](#FormatStrToTime) - -
- -## 文档 - -## 注: -1. 方法FormatTimeToStr和FormatStrToTime中的format参数值需要传以下类型之一: -- yyyy-mm-dd hh:mm:ss -- yyyy-mm-dd hh:mm -- yyyy-mm-dd hh -- yyyy-mm-dd -- yyyy-mm -- mm-dd -- dd-mm-yy hh:mm:ss -- yyyy/mm/dd hh:mm:ss -- yyyy/mm/dd hh:mm -- yyyy-mm-dd hh -- yyyy/mm/dd -- yyyy/mm -- mm/dd -- dd/mm/yy hh:mm:ss -- yyyy -- mm -- hh:mm:ss -- mm:ss - - -### AddDay -

将日期加/减天数

- -函数签名: - -```go -func AddDay(t time.Time, day int64) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Days := datetime.AddDay(now, 2) - before2Days := datetime.AddDay(now, -2) - - fmt.Println(after2Days, before2Days) -} -``` - - -### AddHour -

将日期加/减小时数

- -函数签名: - -```go -func AddHour(t time.Time, hour int64) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Hours := datetime.AddHour(now, 2) - before2Hours := datetime.AddHour(now, -2) - - fmt.Println(after2Hours, after2Hours) -} -``` - -### AddMinute -

将日期加/减分钟数

- -函数签名: - -```go -func AddMinute(t time.Time, minute int64) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - after2Minute := datetime.AddMinute(now, 2) - before2Minute := datetime.AddMinute(now, -2) - - fmt.Println(after2Minute, before2Minute) -} -``` - -### BeginOfMinute -

返回指定时间的分钟开始时间

- -函数签名: - -```go -func BeginOfMinute(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfMinute(td) - fmt.Println(bm) //2022-02-15 15:48:00 +0800 CST -} -``` - -### BeginOfHour -

返回指定时间的小时开始时间

- -函数签名: - -```go -func BeginOfHour(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfHour(td) - fmt.Println(bm) //2022-02-15 15:00:00 +0800 CST -} -``` - -### BeginOfDay -

返回指定时间的当天开始时间

- -函数签名: - -```go -func BeginOfDay(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfDay(td) - fmt.Println(bm) //2022-02-15 00:00:00 +0800 CST -} -``` - - - -### BeginOfWeek -

返回指定时间的星期开始时间

- -函数签名: - -```go -func BeginOfWeek(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfWeek(td) - fmt.Println(bm) //2022-02-13 00:00:00 +0800 CST -} -``` - - - -### BeginOfMonth -

返回指定时间的当月开始时间

- -函数签名: - -```go -func BeginOfMonth(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfMonth(td) - fmt.Println(bm) //2022-02-01 00:00:00 +0800 CST -} -``` - - -### BeginOfYear -

返回指定时间的当年开始时间

- -函数签名: - -```go -func BeginOfYear(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.BeginOfYear(td) - fmt.Println(bm) //2022-01-01 00:00:00 +0800 CST -} -``` - - - -### EndOfMinute -

返回指定时间的分钟结束时间

- -函数签名: - -```go -func EndOfMinute(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfMinute(td) - fmt.Println(bm) //2022-02-15 15:48:59.999999999 +0800 CST -} -``` - -### EndOfHour -

返回指定时间的小时结束时间

- -函数签名: - -```go -func EndOfHour(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfHour(td) - fmt.Println(bm) //2022-02-15 15:59:59.999999999 +0800 CST -} -``` - -### EndOfDay -

返回指定时间的当天结束时间.

- -函数签名: - -```go -func EndOfDay(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfDay(td) - fmt.Println(bm) //2022-02-15 23:59:59.999999999 +0800 CST -} -``` - - - -### EndOfWeek -

返回指定时间的星期结束时间

- -函数签名: - -```go -func EndOfWeek(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfWeek(td) - fmt.Println(bm) //2022-02-19 23:59:59.999999999 +0800 CST -} -``` - - - -### EndOfMonth -

返回指定时间的月份结束时间

- -函数签名: - -```go -func EndOfMonth(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfMonth(td) - fmt.Println(bm) //2022-02-28 23:59:59.999999999 +0800 CST -} -``` - - -### EndOfYear -

返回指定时间的年份结束时间

- -函数签名: - -```go -func EndOfYear(t time.Time) time.Time -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) - bm := datetime.EndOfYear(td) - fmt.Println(bm) //2022-12-31 23:59:59.999999999 +0800 CST -} -``` - - -### GetNowDate -

获取当天日期,返回格式:yyyy-mm-dd

- -函数签名: - -```go -func GetNowDate() string -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - currentDate := datetime.GetNowDate() - fmt.Println(currentDate) // 2022-01-28 -} -``` - - -### GetNowTime -

获取当时时间,返回格式:hh:mm:ss

- -函数签名: - -```go -func GetNowTime() string -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - currentTime := datetime.GetNowTime() - fmt.Println(currentDate) // 15:57:33 -} -``` - - -### GetNowDateTime -

获取当时日期和时间,返回格式:yyyy-mm-dd hh:mm:ss.

- -函数签名: - -```go -func GetNowDateTime() string -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - current := datetime.GetNowDateTime() - fmt.Println(current) // 2022-01-28 15:59:33 -} -``` - - -### GetZeroHourTimestamp -

获取零时时间戳(timestamp of 00:00).

- -函数签名: - -```go -func GetZeroHourTimestamp() int64 -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - zeroTime := datetime.GetZeroHourTimestamp() - fmt.Println(zeroTime) // 1643299200 -} -``` - - -### GetNightTimestamp -

获取午夜时间戳(timestamp of 23:59).

- -函数签名: - -```go -func GetNightTimestamp() int64 -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - nightTime := datetime.GetNightTimestamp() - fmt.Println(nightTime) // 1643385599 -} -``` - -### FormatTimeToStr -

将日期格式化成字符串,`format` 参数格式参考注1

- -函数签名: - -```go -func FormatTimeToStr(t time.Time, format string) string -``` -例子: - -```go -package main - -import ( - "fmt" - "time" - "github.com/duke-git/lancet/datetime" -) - -func main() { - now := time.Now() - timeStr := datetime.FormatTimeToStr(now, "yyyy/mm/dd hh:mm:ss") - fmt.Println(timeStr) //2022/01/28 16:07:44 -} -``` - - -### FormatStrToTime -

将字符串格式化成时间,`format` 参数格式参考注1

- -函数签名: - -```go -func FormatStrToTime(str, format string) (time.Time, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/datetime" -) - -func main() { - time := datetime.FormatStrToTime("2006-01-02 15:04:05", "yyyy/mm/dd hh:mm:ss") - fmt.Println(time) -} -``` - - - diff --git a/docs/en/api/overview.md b/docs/en/api/overview.md new file mode 100644 index 00000000..2d665732 --- /dev/null +++ b/docs/en/api/overview.md @@ -0,0 +1,69 @@ +--- +outline: deep +--- + +# API Overview + +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. + + + +
+

Lancet function module

+
+
algorithm
+
compare
+
concurrency
+
condition
+
convertor
+
cryptor
+
datastructure
+
datetime
+
eventbus
+
fileutil
+
formatter
+
function
+
iterator
+
maputil
+
mathutil
+
netutil
+
pointer
+
random
+
retry
+
slice
+
stream
+
structs
+
strutil
+
system
+
tuple
+
validator
+
xerror
+
+
diff --git a/docs/en/api/packages/algorithm.md b/docs/en/api/packages/algorithm.md new file mode 100644 index 00000000..a3260817 --- /dev/null +++ b/docs/en/api/packages/algorithm.md @@ -0,0 +1,636 @@ +# Algorithm + +Package algorithm implements some basic algorithm. eg. sort, search. + +
+ +## Source + +- [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/lrucache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lrucache.go) + +
+ +## Usage + +```go +import ( + "github.com/duke-git/lancet/v2/algorithm" +) +``` + +
+ +## Index + +- [BubbleSort](#BubbleSort) +- [InsertionSort](#InsertionSort) +- [SelectionSort](#SelectionSort) +- [ShellSort](#ShellSort) +- [QuickSort](#QuickSort) +- [HeapSort](#HeapSort) +- [MergeSort](#MergeSort) +- [CountSort](#CountSort) +- [BinarySearch](#BinarySearch) +- [BinaryIterativeSearch](#BinaryIterativeSearch) +- [LinearSearch](#LinearSearch) +- [LRUCache](#LRUCache) + +
+ +## Documentation + +### BubbleSort + +

Sort slice with bubble sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func BubbleSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/GNdv7Jg2Taj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.BubbleSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### InsertionSort + +

Sort slice with insertion sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func InsertionSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/G5LJiWgJJW6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type people struct { + Name string + Age int +} + +// PeopleAageComparator sort people slice by age field +type peopleAgeComparator struct{} + +// Compare implements github.com/duke-git/lancet/constraints/constraints.go/Comparator +func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int { + p1, _ := v1.(people) + p2, _ := v2.(people) + + //ascending order + if p1.Age < p2.Age { + return -1 + } else if p1.Age > p2.Age { + return 1 + } + + return 0 +} + +func main() { + peoples := []people{ + {Name: "a", Age: 20}, + {Name: "b", Age: 10}, + {Name: "c", Age: 17}, + {Name: "d", Age: 8}, + {Name: "e", Age: 28}, + } + + comparator := &peopleAgeComparator{} + + algorithm.InsertionSort(peoples, comparator) + + fmt.Println(peoples) + + // Output: + // [{d 8} {b 10} {c 17} {a 20} {e 28}] +} +``` + +### SelectionSort + +

Sort slice with selection sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func SelectionSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/oXovbkekayS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.SelectionSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### ShellSort + +

Sort slice with shell sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func ShellSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/3ibkszpJEu3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.ShellSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### QuickSort + +

Sort slice with quick sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func QuickSort[T any](slice []T comparator constraints.Comparator) +``` + +Example:[Run](https://go.dev/play/p/7Y7c1Elk3ax) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.QuickSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### HeapSort + +

Sort slice with heap sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func HeapSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/u6Iwa1VZS_f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.HeapSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### MergeSort + +

Sort slice with merge sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func MergeSort[T any](slice []T, comparator constraints.Comparator) +``` + +Example: [Run](https://go.dev/play/p/ydinn9YzUJn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + algorithm.MergeSort(numbers, comparator) + + fmt.Println(numbers) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### CountSort + +

Sort slice with count sort algorithm. Param comparator should implements constraints.Comparator.

+ +Signature: + +```go +func CountSort[T any](slice []T, comparator constraints.Comparator) []T +``` + +Example: [Run](https://go.dev/play/p/tB-Umgm0DrP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{2, 1, 5, 3, 6, 4} + comparator := &intComparator{} + + sortedNums := algorithm.CountSort(numbers, comparator) + + fmt.Println(sortedNums) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### BinarySearch + +

BinarySearch search for target within a sorted slice, recursive call itself. If a target is found, the index of the target is returned. Else the function return -1.

+ +Signature: + +```go +func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int +``` + +Example: [Run](https://go.dev/play/p/t6MeGiUSN47) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := algorithm.BinarySearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := algorithm.BinarySearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} +``` + +### BinaryIterativeSearch + +

BinaryIterativeSearch search for target within a sorted slice, recursive call itself. If a target is found, the index of the target is returned. Else the function return -1.

+ +Signature: + +```go +func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int +``` + +Example: [Run](https://go.dev/play/p/Anozfr8ZLH3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1 any, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + //ascending order + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8} + comparator := &intComparator{} + + result1 := algorithm.BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator) + result2 := algorithm.BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 4 + // -1 +} +``` + +### LinearSearch + +

return the index of target in slice base on equal function.If a target is found, the index of the target is returned. Else the function return -1.

+ +Signature: + +```go +func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int +``` + +Example: [Run](https://go.dev/play/p/IsS7rgn5s3x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +func main() { + numbers := []int{3, 4, 5, 3, 2, 1} + + equalFunc := func(a, b int) bool { + return a == b + } + + result1 := algorithm.LinearSearch(numbers, 3, equalFunc) + result2 := algorithm.LinearSearch(numbers, 6, equalFunc) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} +``` + +### LRUCache + +

LRUCache implements mem cache with lru.

+ +Signature: + +```go +func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] +func (l *LRUCache[K, V]) Get(key K) (V, bool) +func (l *LRUCache[K, V]) Put(key K, value V) +func (l *LRUCache[K, V]) Delete(key K) bool +func (l *LRUCache[K, V]) Len() int +``` + +Example: [Run](https://go.dev/play/p/IsS7rgn5s3x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/algorithm" +) + +func main() { + cache := algorithm.NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + result1, ok1 := cache.Get(1) + result2, ok2 := cache.Get(2) + result3, ok3 := cache.Get(3) + + fmt.Println(result1, ok1) + fmt.Println(result2, ok2) + fmt.Println(result3, ok3) + + fmt.Println(cache.Len()) + + ok := cache.Delete(2) + fmt.Println(ok) + + + // Output: + // 1 true + // 2 true + // 0 false + // 2 + // true +} +``` diff --git a/docs/en/api/packages/compare.md b/docs/en/api/packages/compare.md new file mode 100644 index 00000000..84f56e64 --- /dev/null +++ b/docs/en/api/packages/compare.md @@ -0,0 +1,374 @@ +# Compare + +Package compare provides a lightweight comparison function on any type. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/compare/compare.go](https://github.com/duke-git/lancet/blob/main/compare/compare.go) + +- [https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/condition" +) +``` + +
+ +## Index + +- [Equal](#Equal) +- [EqualValue](#EqualValue) +- [LessThan](#LessThan) +- [GreaterThan](#GreaterThan) +- [LessOrEqual](#LessOrEqual) +- [GreaterOrEqual](#GreaterOrEqual) +- [InDelta](#InDelta) + +
+ +## Documentation + +### Equal + +

Checks if two values are equal or not. (check both type and value)

+ +Signature: [Run](https://go.dev/play/p/wmVxR-to4lz) + +```go +func Equal(left, right any) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.Equal(1, 1) + result2 := compare.Equal("1", "1") + result3 := compare.Equal([]int{1, 2, 3}, []int{1, 2, 3}) + result4 := compare.Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}) + + result5 := compare.Equal(1, "1") + result6 := compare.Equal(1, int64(1)) + result7 := compare.Equal([]int{1, 2}, []int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### EqualValue + +

Checks if two values are equal or not. (check value only)

+ +Signature: [Run](https://go.dev/play/p/fxnna_LLD9u) + +```go +func EqualValue(left, right any) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.EqualValue(1, 1) + result2 := compare.EqualValue(int(1), int64(1)) + result3 := compare.EqualValue(1, "1") + result4 := compare.EqualValue(1, "2") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### LessThan + +

Checks if value `left` less than value `right`.

+ +Signature: [Run](https://go.dev/play/p/cYh7FQQj0ne) + +```go +func LessThan(left, right any) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.LessThan(1, 2) + result2 := compare.LessThan(1.1, 2.2) + result3 := compare.LessThan("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.LessThan(time1, time2) + + result5 := compare.LessThan(2, 1) + result6 := compare.LessThan(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} +``` + +### GreaterThan + +

Checks if value `left` greater than value `right`.

+ +Signature: + +```go +func GreaterThan(left, right any) bool +``` + +Example: [Run](https://go.dev/play/p/9-NYDFZmIMp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.GreaterThan(2, 1) + result2 := compare.GreaterThan(2.2, 1.1) + result3 := compare.GreaterThan("b", "a") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.GreaterThan(time2, time1) + + result5 := compare.GreaterThan(1, 2) + result6 := compare.GreaterThan(int64(2), 1) + result7 := compare.GreaterThan("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### LessOrEqual + +

Checks if value `left` less than or equal than value `right`.

+ +Signature: + +```go +func LessOrEqual(left, right any) bool +``` + +Example: [Run](https://go.dev/play/p/e4T_scwoQzp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.LessOrEqual(1, 1) + result2 := compare.LessOrEqual(1.1, 2.2) + result3 := compare.LessOrEqual("a", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.LessOrEqual(time1, time2) + + result5 := compare.LessOrEqual(2, 1) + result6 := compare.LessOrEqual(1, int64(2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // true + // true + // true + // false + // false +} +``` + +### GreaterOrEqual + +

Checks if value `left` less greater or equal than value `right`.

+ +Signature: [Run](https://go.dev/play/p/vx8mP0U8DFk) + +```go +func GreaterOrEqual(left, right any) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := compare.GreaterOrEqual(1, 1) + result2 := compare.GreaterOrEqual(2.2, 1.1) + result3 := compare.GreaterOrEqual("b", "b") + + time1 := time.Now() + time2 := time1.Add(time.Second) + result4 := compare.GreaterOrEqual(time2, time1) + + result5 := compare.GreaterOrEqual(1, 2) + result6 := compare.GreaterOrEqual(int64(2), 1) + result7 := compare.GreaterOrEqual("b", "c") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // true + // true + // true + // true + // false + // false + // false +} +``` + +### InDelta + +

Checks if two values are equal or not within a delta.

+ +Signature: [Run](https://go.dev/play/p/TuDdcNtMkjo) + +```go +func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/compare" +) + +func main() { + result1 := InDelta(1, 1, 0) + result2 := InDelta(1, 2, 0) + + result3 := InDelta(2.0/3.0, 0.66667, 0.001) + result4 := InDelta(2.0/3.0, 0.0, 0.001) + + result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0) + result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // true + // false + // true + // false + // false + // true +} +``` diff --git a/docs/en/api/packages/concurrency.md b/docs/en/api/packages/concurrency.md new file mode 100644 index 00000000..fce2598f --- /dev/null +++ b/docs/en/api/packages/concurrency.md @@ -0,0 +1,854 @@ +# Concurrency + +Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel. + +
+ +## 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/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/concurrency" +) +``` + +
+ +## Index + +### Channel + +- [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) + +
+ +## Documentation + +## Channel + +### NewChannel + +

Create a Channel pointer instance.

+ +Signature: + +```go +type Channel[T any] struct +func NewChannel[T any]() *Channel[T] +``` + +Example: [Run](https://go.dev/play/p/7aB4KyMMp9A) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + c := concurrency.NewChannel[int]() +} +``` + +### Bridge + +

Link multiple channels into one channel until cancel the context.

+ +Signature: + +```go +func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T +``` + +Example: [Run](https://go.dev/play/p/qmWSy1NVF-Y) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + genVals := func() <-chan <-chan int { + out := make(chan (<-chan int)) + go func() { + defer close(out) + for i := 1; i <= 5; i++ { + stream := make(chan int, 1) + stream <- i + close(stream) + out <- stream + } + }() + return out + } + + for v := range c.Bridge(ctx, genVals()) { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 + // 4 + // 5 +} +``` + +### FanIn + +

Merge multiple channels into one channel until cancel the context.

+ +Signature: + +```go +func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T +``` + +Example: [Run](https://go.dev/play/p/2VYFMexEvTm) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + channels := make([]<-chan int, 2) + + for i := 0; i < 2; i++ { + channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2) + } + + chs := c.FanIn(ctx, channels...) + + for v := range chs { + fmt.Println(v) //1 1 0 0 or 0 0 1 1 + } +} +``` + +### Repeat + +

Create channel, put values into the channel repeatly until cancel the context.

+ +Signature: [Run](https://go.dev/play/p/k5N_ALVmYjE) + +```go +func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4) + + for v := range intStream { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 1 + // 2 +} +``` + +### Generate + +

Creates a channel, then put values into the channel.

+ +Signature: + +```go +func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T +``` + +Example: [Run](https://go.dev/play/p/7aB4KyMMp9A) + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Generate(ctx, 1, 2, 3) + + fmt.Println(<-intStream) + fmt.Println(<-intStream) + fmt.Println(<-intStream) + + // Output: + // 1 + // 2 + // 3 +} +``` + +### RepeatFn + +

Create a channel, excutes fn repeatly, and put the result into the channel, until close context.

+ +Signature: [Run](https://go.dev/play/p/4J1zAWttP85) + +```go +func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + fn := func() string { + return "hello" + } + + c := concurrency.NewChannel[string]() + intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3) + + for v := range intStream { + fmt.Println(v) + } + + // Output: + // hello + // hello + // hello +} +``` + +### Or + +

Read one or more channels into one channel, will close when any readin channel is closed.

+ +Signature: [Run](https://go.dev/play/p/Wqz9rwioPww) + +```go +func (c *Channel[T]) Or(channels ...<-chan T) <-chan T +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + sig := func(after time.Duration) <-chan any { + c := make(chan any) + go func() { + defer close(c) + time.Sleep(after) + }() + return c + } + + start := time.Now() + + c := concurrency.NewChannel[any]() + <-c.Or( + sig(1*time.Second), + sig(2*time.Second), + sig(3*time.Second), + ) + + fmt.Println("done after %v", time.Since(start)) //1.003s +} +``` + +### OrDone + +

Read a channel into another channel, will close until cancel context.

+ +Signature: [Run](https://go.dev/play/p/lm_GoS6aDjo) + +```go +func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 3) + + for v := range c.OrDone(ctx, intStream) { + fmt.Println(v) + } + + // Output: + // 1 + // 1 + // 1 +} +``` + +### Take + +

Create a channel whose values are taken from another channel with limit number.

+ +Signature: [Run](https://go.dev/play/p/9Utt-1pDr2J) + +```go +func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + numbers := make(chan int, 5) + numbers <- 1 + numbers <- 2 + numbers <- 3 + numbers <- 4 + numbers <- 5 + defer close(numbers) + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, numbers, 3) + + for v := range intStream { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 +} +``` + +### Tee + +

Split one chanel into two channels, until cancel the context.

+ +Signature: [Run](https://go.dev/play/p/3TQPKnCirrP) + +```go +func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) +``` + +Example: + +```go +package main + +import ( + "context" + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := concurrency.NewChannel[int]() + intStream := c.Take(ctx, c.Repeat(ctx, 1), 2) + + ch1, ch2 := c.Tee(ctx, intStream) + + for v := range ch1 { + fmt.Println(v) + fmt.Println(<-ch2) + } + + // Output: + // 1 + // 1 + // 1 + // 1 +} +``` + +### KeyedLocker + +### NewKeyedLocker + +

KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.

+ +Signature: + +```go +func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] +``` + +Example:[Run](https://go.dev/play/p/GzeyC33T5rw) + +```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. +} +``` + +### Do + +

Acquires a lock for the specified key and executes the provided function.

+ +Signature: + +```go +func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error +``` + +Example:[Run](https://go.dev/play/p/GzeyC33T5rw) + +```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. +} +``` + +### NewRWKeyedLocker + +

RWKeyedLocker is a read-write version of KeyedLocker.

+ +Signature: + +```go +func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] +``` + +Example:[Run](https://go.dev/play/p/ZrCr8sMo77T) + +```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! +} +``` + +### RLock + +

Acquires a read lock for the specified key and executes the provided function.

+ +Signature: + +```go +func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error +``` + +Example:[Run](https://go.dev/play/p/ZrCr8sMo77T) + +```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! +} +``` + +### Lock + +

Acquires a write lock for the specified key and executes the provided function.

+ +Signature: + +```go +func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error +``` + +Example:[Run](https://go.dev/play/p/WgAcXbOPKGk) + +```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! +} +``` + +### NewTryKeyedLocker + +

TryKeyedLocker is a non-blocking version of KeyedLocker.

+ +Signature: + +```go +func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] +``` + +Example:[Run](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` + +### TryLock + +

TryLock tries to acquire a lock for the specified key.

+ +Signature: + +```go +func (l *TryKeyedLocker[K]) TryLock(key K) bool +``` + +Example:[Run](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` + +### Unlock + +

Unlock releases the lock for the specified key.

+ +Signature: + +```go +func (l *TryKeyedLocker[K]) Unlock(key K) +``` + +Example:[Run](https://go.dev/play/p/VG9qLvyetE2) + +```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 +} +``` diff --git a/docs/en/api/packages/condition.md b/docs/en/api/packages/condition.md new file mode 100644 index 00000000..1c3af32c --- /dev/null +++ b/docs/en/api/packages/condition.md @@ -0,0 +1,346 @@ +# Condition +Package condition contains some functions for conditional judgment. eg. And, Or, TernaryOperator... The implementation of this package refers to the implementation of carlmjohnson's truthy package, you may find more useful information in [truthy](https://github.com/carlmjohnson/truthy), thanks carlmjohnson. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/condition/condition.go](https://github.com/duke-git/lancet/blob/main/condition/condition.go) + +
+ +## Usage: +```go +import ( + "github.com/duke-git/lancet/v2/condition" +) +``` + +
+ +## Index + +- [Bool](#Bool) +- [And](#And) +- [Or](#Or) +- [Xor](#Generate) +- [Nor](#Nor) +- [Xnor](#Xnor) +- [Nand](#Nand) +- [Ternary](#Ternary) +- [TernaryOperatordeprecated](#TernaryOperator) + +
+ +## Documentation + + +### Bool +

Returns the truthy value of anything.
+If the value's type has a Bool() bool method, the method is called and returned.
+If the type has an IsZero() bool method, the opposite value is returned.
+Slices and maps are truthy if they have a length greater than zero.
+All other types are truthy if they are not their zero value.

+ +Signature: + +```go +func Bool[T any](value T) bool +``` +Example:[运行](https://go.dev/play/p/ETzeDJRSvhm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + // bool + result1 := condition.Bool(false) + result2 := condition.Bool(true) + fmt.Println(result1) // false + fmt.Println(result2) // true + + // integer + result3 := condition.Bool(0) + result4 := condition.Bool(1) + fmt.Println(result3) // false + fmt.Println(result4) // true + + // string + result5 := condition.Bool("") + result6 := condition.Bool(" ") + fmt.Println(result5) // false + fmt.Println(result6) // true + + // slice + nums := []int{} + result7 := condition.Bool(nums) + + nums = append(nums, 1, 2) + result8 := condition.Bool(nums) + fmt.Println(result7) // false + fmt.Println(result8) // true + + // struct + result9 = condition.Bool(struct{}{}) + fmt.Println(result8) // false + + + // Output: + // false + // true + // false + // true + // false + // true + // false + // true + // false +} +``` + + +### And +

Returns true if both a and b are truthy.

+ +Signature: + +```go +func And[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/W1SSUmt6pvr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.And(0, 1)) // false + fmt.Println(condition.And(0, "")) // false + fmt.Println(condition.And(0, "0")) // false + fmt.Println(condition.And(1, "0")) // true +} +``` + + + +### Or +

Returns false if neither a nor b is truthy.

+ +Signature: + +```go +func Or[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/UlQTxHaeEkq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Or(0, "")) // false + fmt.Println(condition.Or(0, 1)) // true + fmt.Println(condition.Or(0, "0")) // true + fmt.Println(condition.Or(1, "0")) // true +} +``` + + + +### Xor +

Returns true if a or b but not both is truthy.

+ +Signature: + +```go +func Xor[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/gObZrW7ZbG8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Xor(0, 0)) // false + fmt.Println(condition.Xor(0, 1)) // true + fmt.Println(condition.Xor(1, 0)) // true + fmt.Println(condition.Xor(1, 1)) // false +} +``` + + + +### Nor +

Returns true if neither a nor b is truthy.

+ +Signature: + +```go +func Nor[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/g2j08F_zZky) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Nor(0, 0)) // true + fmt.Println(condition.Nor(0, 1)) // false + fmt.Println(condition.Nor(1, 0)) // false + fmt.Println(condition.Nor(1, 1)) // false +} +``` + + +### Xnor +

Returns true if both a and b or neither a nor b are truthy.

+ +Signature: + +```go +func Xnor[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/OuDB9g51643) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Xnor(0, 0)) // true + fmt.Println(condition.Xnor(0, 1)) // false + fmt.Println(condition.Xnor(1, 0)) // false + fmt.Println(condition.Xnor(1, 1)) // true +} +``` + + +### Nand +

Returns false if both a and b are truthy

+ +Signature: + +```go +func Nand[T, U any](a T, b U) bool +``` +Example:[运行](https://go.dev/play/p/vSRMLxLIbq8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + fmt.Println(condition.Nand(0, 0)) // true + fmt.Println(condition.Nand(0, 1)) // true + fmt.Println(condition.Nand(1, 0)) // true + fmt.Println(condition.Nand(1, 1)) // false +} +``` + + + +### Ternary +

Checks the value of param `isTrue`, if true return ifValue else return elseValue

+ +Signature: + +```go +func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U +``` +Example:[运行](https://go.dev/play/p/ElllPZY0guT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + conditionTrue := 2 > 1 + result1 := condition.Ternary(conditionTrue, 0, 1) + + conditionFalse := 2 > 3 + result2 := condition.Ternary(conditionFalse, 0, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} +``` + +### TernaryOperator +

Checks the value of param `isTrue`, if true return ifValue else return elseValue

+ +> ⚠️ This function is deprecated. use `Ternary` instead. + +Signature: + +```go +func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U +``` +Example:[运行](https://go.dev/play/p/ElllPZY0guT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/condition" +) + +func main() { + conditionTrue := 2 > 1 + result1 := condition.TernaryOperator(conditionTrue, 0, 1) + + conditionFalse := 2 > 3 + result2 := condition.TernaryOperator(conditionFalse, 0, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} +``` + + + + + diff --git a/docs/en/api/packages/convertor.md b/docs/en/api/packages/convertor.md new file mode 100644 index 00000000..24d1e3d0 --- /dev/null +++ b/docs/en/api/packages/convertor.md @@ -0,0 +1,1150 @@ +# Convertor + +Package convertor contains some functions for data type convertion. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/convertor" +) +``` + +
+ +## Index + +- [ColorHexToRGB](#ColorHexToRGB) +- [ColorRGBToHex](#ColorRGBToHex) +- [ToBool](#ToBool) +- [ToBytes](#ToBytes) +- [ToChar](#ToChar) +- [ToChannel](#ToChannel) +- [ToFloat](#ToFloat) +- [ToInt](#ToInt) +- [ToJson](#ToJson) +- [ToMap](#ToMap) +- [ToPointer](#ToPointer) +- [ToString](#ToString) +- [StructToMap](#StructToMap) +- [MapToSlice](#MapToSlice) +- [EncodeByte](#EncodeByte) +- [DecodeByte](#DecodeByte) +- [DeepClone](#DeepClone) +- [CopyProperties](#CopyProperties) +- [ToInterface](#ToInterface) +- [Utf8ToGbk](#Utf8ToGbk) +- [GbkToUtf8](#GbkToUtf8) +- [ToStdBase64](#ToStdBase64) +- [ToUrlBase64](#ToUrlBase64) +- [ToRawStdBase64](#ToRawStdBase64) +- [ToRawUrlBase64](#ToRawUrlBase64) +- [ToBigInt](#ToBigInt) + +
+ +## Documentation + +### ColorHexToRGB + +

Convert color hex to color rgb.

+ +Signature: + +```go +func ColorHexToRGB(colorHex string) (red, green, blue int) +``` + +Example:[Run](https://go.dev/play/p/o7_ft-JCJBV) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + colorHex := "#003366" + r, g, b := convertor.ColorHexToRGB(colorHex) + + fmt.Println(r, g, b) + + // Output: + // 0 51 102 +} +``` + +### ColorRGBToHex + +

Convert color rgb to color hex.

+ +Signature: + +```go +func ColorRGBToHex(red, green, blue int) string +``` + +Example:[Run](https://go.dev/play/p/nzKS2Ro87J1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + r := 0 + g := 51 + b := 102 + colorHex := ColorRGBToHex(r, g, b) + + fmt.Println(colorHex) + + // Output: + // #003366 +} +``` + +### ToBool + +

Convert string to bool. Use strconv.ParseBool.

+ +Signature: + +```go +func ToBool(s string) (bool, error) +``` + +Example:[Run](https://go.dev/play/p/ARht2WnGdIN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"} + + for i := 0; i < len(cases); i++ { + result, _ := convertor.ToBool(cases[i]) + fmt.Println(result) + } + + // Output: + // true + // true + // true + // false + // false + // false + // false + // false + // false +} +``` + +### ToBytes + +

Convert value to byte slice.

+ +Signature: + +```go +func ToBytes(data any) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/fAMXYFDvOvr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + bytesData, err := convertor.ToBytes("abc") + if err != nil { + fmt.Println(err) + } + + fmt.Println(bytesData) + + // Output: + // [97 98 99] +} +``` + +### ToChar + +

Convert string to char slice.

+ +Signature: + +```go +func ToChar(s string) []string +``` + +Example:[Run](https://go.dev/play/p/JJ1SvbFkVdM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1 := convertor.ToChar("") + result2 := convertor.ToChar("abc") + result3 := convertor.ToChar("1 2#3") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [] + // [a b c] + // [1 2 # 3] +} +``` + +### ToChannel + +

Convert a collection of elements to a read-only channel.

+ +Signature: + +```go +func ToChannel[T any](array []T) <-chan T +``` + +Example:[Run](https://go.dev/play/p/hOx_oYZbAnL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + ch := convertor.ToChannel([]int{1, 2, 3}) + result1 := <-ch + result2 := <-ch + result3 := <-ch + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 3 +} +``` + +### ToFloat + +

Convert value to a float64 value. If param is a invalid floatable, will return 0.0 and error.

+ +Signature: + +```go +func ToFloat(value any) (float64, error) +``` + +Example:[Run](https://go.dev/play/p/4YTmPCibqHJ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1, _ := convertor.ToFloat("") + result2, err := convertor.ToFloat("abc") + result3, _ := convertor.ToFloat("-1") + result4, _ := convertor.ToFloat("-.11") + result5, _ := convertor.ToFloat("1.23e3") + result6, _ := convertor.ToFloat(true) + + fmt.Println(result1) + fmt.Println(result2, err) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // 0 + // 0 strconv.ParseFloat: parsing "": invalid syntax + // -1 + // -0.11 + // 1230 + // 0 +} +``` + +### ToInt + +

Convert value to a int64 value. If param is a invalid intable, will return 0 and error.

+ +Signature: + +```go +func ToInt(value any) (int64, error) +``` + +Example:[Run](https://go.dev/play/p/9_h9vIt-QZ_b) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1, _ := convertor.ToInt("123") + result2, _ := convertor.ToInt("-123") + result3, _ := convertor.ToInt(float64(12.3)) + result4, err := convertor.ToInt("abc") + result5, _ := convertor.ToInt(true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4, err) + fmt.Println(result5) + + // Output: + // 123 + // -123 + // 12 + // 0 strconv.ParseInt: parsing "": invalid syntax + // 0 +} +``` + +### ToJson + +

Convert interface to json string. If param can't be converted, will return "" and error.

+ +Signature: + +```go +func ToJson(value any) (string, error) +``` + +Example:[Run](https://go.dev/play/p/2rLIkMmXWvR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result, err := convertor.ToJson(aMap) + + if err != nil { + fmt.Printf("%v", err) + } + + fmt.Println(result) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### ToMap + +

Convert a slice of structs to a map based on iteratee function.

+ +Signature: + +```go +func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V +``` + +Example:[Run](https://go.dev/play/p/tVFy7E-t24l) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Message struct { + name string + code int + } + messages := []Message{ + {name: "Hello", code: 100}, + {name: "Hi", code: 101}, + } + + result := convertor.ToMap(messages, func(msg Message) (int, string) { + return msg.code, msg.name + }) + + fmt.Println(result) + + // Output: + // map[100:Hello 101:Hi] +} +``` + +### ToPointer + +

Returns a pointer to passed value.

+ +Signature: + +```go +func ToPointer[T any](value T) *T +``` + +Example:[Run](https://go.dev/play/p/ASf_etHNlw1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result := convertor.ToPointer(123) + fmt.Println(*result) + + // Output: + // 123 +} +``` + +### ToString + +

ToString convert value to string, for number, string, []byte, will convert to string. For other type (slice, map, array, struct) will call json.Marshal

+ +Signature: + +```go +func ToString(value any) string +``` + +Example:[Run](https://go.dev/play/p/nF1zOOslpQq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + result1 := convertor.ToString("") + result2 := convertor.ToString(nil) + result3 := convertor.ToString(0) + result4 := convertor.ToString(1.23) + result5 := convertor.ToString(true) + result6 := convertor.ToString(false) + result7 := convertor.ToString([]int{1, 2, 3}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // + // + // 0 + // 1.23 + // true + // false + // [1,2,3] +} +``` + +### StructToMap + +

Convert struct to map, only convert exported field, struct field tag `json` should be set.

+ +Signature: + +```go +func StructToMap(value any) (map[string]any, error) +``` + +Example:[Run](https://go.dev/play/p/KYGYJqNUBOI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type People struct { + Name string `json:"name"` + age int + } + p := People{ + "test", + 100, + } + pm, _ := convertor.StructToMap(p) + + fmt.Println(pm) + + // Output: + // map[name:test] +} +``` + +### MapToSlice + +

Convert a map to a slice based on iteratee function.

+ +Signature: + +```go +func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T +``` + +Example:[Run](https://go.dev/play/p/dmX4Ix5V6Wl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + aMap := map[string]int{"a": 1, "b": 2, "c": 3} + result := MapToSlice(aMap, func(key string, value int) string { + return key + ":" + strconv.Itoa(value) + }) + + fmt.Println(result) //[]string{"a:1", "b:2", "c:3"} +} +``` + + +### EncodeByte + +

Encode data to byte slice.

+ +Signature: + +```go +func EncodeByte(data any) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/DVmM1G5JfuP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + byteData, _ := convertor.EncodeByte("abc") + fmt.Println(byteData) + + // Output: + // [6 12 0 3 97 98 99] +} +``` + +### DecodeByte + +

Decode byte data to target object. target should be a pointer instance.

+ +Signature: + +```go +func DecodeByte(data []byte, target any) error +``` + +Example:[Run](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + var result string + byteData := []byte{6, 12, 0, 3, 97, 98, 99} + + err := convertor.DecodeByte(byteData, &result) + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // abc +} +``` + + +### CopyProperties + +

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.

+ +Signature: + +```go +func CopyProperties[T, U any](dst T, src U) (err error) +``` + +Example:[Run](https://go.dev/play/p/oZujoB5Sgg5) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Disk struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type DiskVO struct { + Name string `json:"name"` + Total string `json:"total"` + Used string `json:"used"` + Percent float64 `json:"percent"` + } + + type Indicator struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int `json:"cpu"` + Disk []Disk `json:"disk"` + Stop chan bool `json:"-"` + } + + type IndicatorVO struct { + Id string `json:"id"` + Ip string `json:"ip"` + UpTime string `json:"upTime"` + LoadAvg string `json:"loadAvg"` + Cpu int64 `json:"cpu"` + Disk []DiskVO `json:"disk"` + } + + indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{ + {Name: "disk-001", Total: "100", Used: "1", Percent: 10}, + {Name: "disk-002", Total: "200", Used: "1", Percent: 20}, + {Name: "disk-003", Total: "300", Used: "1", Percent: 30}, + }} + + indicatorVO := IndicatorVO{} + + err := convertor.CopyProperties(&indicatorVO, indicator) + + if err != nil { + return + } + + fmt.Println(indicatorVO.Id) + fmt.Println(indicatorVO.Ip) + fmt.Println(len(indicatorVO.Disk)) + + // Output: + // 001 + // 127.0.0.1 + // 3 +} +``` + +### Utf8ToGbk + +

Converts utf8 encoding data to GBK encoding data.

+ +Signature: + +```go +func Utf8ToGbk(bs []byte) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/9FlIaFLArIL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + utf8Data := []byte("hello") + gbkData, _ := convertor.Utf8ToGbk(utf8Data) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(validator.IsGBK(gbkData)) + + // Output: + // true + // true +} +``` + +### GbkToUtf8 + +

Converts GBK encoding data to utf8 encoding data.

+ +Signature: + +```go +func GbkToUtf8(bs []byte) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/OphmHCN_9u8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + gbkData, _ := convertor.Utf8ToGbk([]byte("hello")) + utf8Data, _ := convertor.GbkToUtf8(gbkData) + + fmt.Println(utf8.Valid(utf8Data)) + fmt.Println(string(utf8Data)) + + // Output: + // true + // hello +} +``` + +### ToStdBase64 + +

Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.

+ +Signature: + +```go +func ToStdBase64(value any) string +``` + +Example:[Run](https://go.dev/play/p/_fLJqJD3NMo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + afterEncode := convertor.ToStdBase64(nil) + fmt.Println(afterEncode) + + afterEncode = convertor.ToStdBase64("") + fmt.Println(afterEncode) + + stringVal := "hello" + afterEncode = convertor.ToStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // + // + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +``` + + + +### ToUrlBase64 + +

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.

+ +Signature: + +```go +func ToUrlBase64(value any) string +``` + +Example:[Run](https://go.dev/play/p/C_d0GlvEeUR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + afterEncode := convertor.ToUrlBase64(nil) + fmt.Println(afterEncode) + + + stringVal := "hello" + afterEncode = convertor.ToUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // + // aGVsbG8= + // aGVsbG8= + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ== + // MTIzLjQ1Ng== + // dHJ1ZQ== + // ZXJy +} + +``` + +### ToRawStdBase64 + +

Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.

+ +Signature: + +```go +func ToRawStdBase64(value any) string +``` + +Example:[Run](https://go.dev/play/p/wSAr3sfkDcv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + + stringVal := "hello" + afterEncode := convertor.ToRawStdBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToRawStdBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToRawStdBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToRawStdBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToRawStdBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToRawStdBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToRawStdBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} +``` + +### ToRawUrlBase64 + +

Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.

+ +Signature: + +```go +func ToRawUrlBase64(value any) string +``` + +Example:[Run](https://go.dev/play/p/HwdDPFcza1O) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + + stringVal := "hello" + afterEncode := convertor.ToRawUrlBase64(stringVal) + fmt.Println(afterEncode) + + byteSliceVal := []byte("hello") + afterEncode = convertor.ToRawUrlBase64(byteSliceVal) + fmt.Println(afterEncode) + + intVal := 123 + afterEncode = convertor.ToRawUrlBase64(intVal) + fmt.Println(afterEncode) + + mapVal := map[string]any{"a": "hi", "b": 2, "c": struct { + A string + B int + }{"hello", 3}} + afterEncode = convertor.ToRawUrlBase64(mapVal) + fmt.Println(afterEncode) + + floatVal := 123.456 + afterEncode = convertor.ToRawUrlBase64(floatVal) + fmt.Println(afterEncode) + + boolVal := true + afterEncode = convertor.ToRawUrlBase64(boolVal) + fmt.Println(afterEncode) + + errVal := errors.New("err") + afterEncode = convertor.ToRawUrlBase64(errVal) + fmt.Println(afterEncode) + + // Output: + // aGVsbG8 + // aGVsbG8 + // MTIz + // eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ + // MTIzLjQ1Ng + // dHJ1ZQ + // ZXJy +} +``` + +### DeepClone + +

Creates a deep copy of passed item, can't clone unexported field of struct.

+ +Signature: + +```go +func DeepClone[T any](src T) T +``` + +Example:[Run](https://go.dev/play/p/j4DP5dquxnk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + type Struct struct { + Str string + Int int + Float float64 + Bool bool + Nil interface{} + unexported string + } + + cases := []interface{}{ + true, + 1, + 0.1, + map[string]int{ + "a": 1, + "b": 2, + }, + &Struct{ + Str: "test", + Int: 1, + Float: 0.1, + Bool: true, + Nil: nil, + }, + } + + for _, item := range cases { + cloned := convertor.DeepClone(item) + + isPointerEqual := &cloned == &item + fmt.Println(cloned, isPointerEqual) + } + + // Output: + // true false + // 1 false + // 0.1 false + // map[a:1 b:2] false + // &{test 1 0.1 true } false +} +``` + +### ToBigInt + +

Converts an integer of any supported type (int, int64, uint64, etc.) to *big.Int

+ +Signature:[Run](https://go.dev/play/p/X3itkCxwB_x) + +```go +func ToBigInt[T any](v T) (*big.Int, error) +``` + +Example: + +```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 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/cryptor.md b/docs/en/api/packages/cryptor.md new file mode 100644 index 00000000..cb05faf1 --- /dev/null +++ b/docs/en/api/packages/cryptor.md @@ -0,0 +1,1833 @@ +# Cryptor + +Package cryptor contains some functions for data encryption and decryption. Support base64, md5, hmac, aes, des, rsa. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/cryptor/basic.go](https://github.com/duke-git/lancet/blob/main/cryptor/basic.go) +- [https://github.com/duke-git/lancet/blob/main/cryptor/crypto.go](https://github.com/duke-git/lancet/blob/main/cryptor/crypto.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/cryptor" +) +``` + +
+ +## Index + +- [AesEcbEncrypt](#AesEcbEncrypt) +- [AesEcbDecrypt](#AesEcbDecrypt) +- [AesCbcEncrypt](#AesCbcEncrypt) +- [AesCbcDecrypt](#AesCbcDecrypt) +- [AesCtrCryptdeprecated](#AesCtrCrypt) +- [AesCtrEncrypt](#AesCtrEncrypt) +- [AesCtrDecrypt](#AesCtrDecrypt) +- [AesCfbEncrypt](#AesCfbEncrypt) +- [AesCfbDecrypt](#AesCfbDecrypt) +- [AesOfbEncrypt](#AesOfbEncrypt) +- [AesOfbDecrypt](#AesOfbDecrypt) +- [AesGcmEncrypt](#AesGcmEncrypt) +- [AesGcmDecrypt](#AesGcmDecrypt) +- [Base64StdEncode](#Base64StdEncode) +- [Base64StdDecode](#Base64StdDecode) +- [DesEcbEncrypt](#DesEcbEncrypt) +- [DesEcbDecrypt](#DesEcbDecrypt) +- [DesCbcEncrypt](#DesCbcEncrypt) +- [DesCbcDecrypt](#DesCbcDecrypt) +- [DesCtrCryptdeprecated](#DesCtrCrypt) +- [DesCfbEncrypt](#DesCfbEncrypt) +- [DesCfbDecrypt](#DesCfbDecrypt) +- [DesCfbEncrypt](#DesCfbEncrypt) +- [DesCfbDecrypt](#DesCfbDecrypt) +- [DesOfbEncrypt](#DesOfbEncrypt) +- [DesOfbDecrypt](#DesOfbDecrypt) +- [HmacMd5](#HmacMd5) +- [HmacMd5WithBase64](#HmacMd5WithBase64) +- [HmacSha1](#HmacSha1) +- [HmacSha1WithBase64](#HmacSha1WithBase64) +- [HmacSha256](#HmacSha256) +- [HmacSha256WithBase64](#HmacSha256WithBase64) +- [HmacSha512](#HmacSha512) +- [HmacSha512WithBase64](#HmacSha512WithBase64) +- [Md5String](#Md5String) +- [Md5StringWithBase64](#Md5StringWithBase64) +- [Md5Byte](#Md5Byte) +- [Md5ByteWithBase64](#Md5ByteWithBase64) +- [Md5File](#Md5File) +- [Sha1](#Sha1) +- [Sha1WithBase64](#Sha1WithBase64) +- [Sha256](#Sha256) +- [Sha256WithBase64](#Sha256WithBase64) +- [Sha512](#Sha512) +- [Sha512WithBase64](#Sha512WithBase64) +- [GenerateRsaKey](#GenerateRsaKey) +- [RsaEncrypt](#RsaEncrypt) +- [RsaDecrypt](#RsaDecrypt) +- [GenerateRsaKeyPair](#GenerateRsaKeyPair) +- [RsaEncryptOAEP](#RsaEncryptOAEP) +- [RsaDecryptOAEP](#RsaDecryptOAEP) +- [RsaSign](#RsaSign) +- [RsaVerifySign](#RsaVerifySign) + +
+ +## Documentation + +### AesEcbEncrypt + +

Encrypt data with key use AES ECB algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesEcbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesEcbDecrypt + +

Decrypt data with key use AES ECB algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesEcbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/zI6xsmuQRbn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesEcbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCbcEncrypt + +

Encrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesCbcEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/IOq_g8_lKZD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCbcDecrypt + +

Decrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesCbcDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/IOq_g8_lKZD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCtrCrypt + +

Encrypt or decrypt data with key use AES CTR algorithm. Length of `key` param should be 16, 24 or 32.

+ +> ⚠️ This function is deprecated. use `AesCtrEncrypt` and `AesCtrDecrypt` instead. + +Signature: + +```go +func AesCtrCrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/SpaZO0-5Nsp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCtrCrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCtrEncrypt + +

Encrypt data with key use AES CTR algorithm

+ +Signature: + +```go +func AesCtrEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/x6pjPAvThRz) + +```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 +} +``` + +### AesCtrDecrypt + +

Decrypt data with key use AES CTR algorithm

+ +Signature: + +```go +func AesCtrDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/x6pjPAvThRz) + +```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 +} +``` + +### AesCfbEncrypt + +

Encrypt data with key use AES CFB algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesCfbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/tfkF10B13kH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesCfbDecrypt + +

Decrypt data with key use AES CBC algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesCfbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/tfkF10B13kH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesOfbEncrypt + +

Enecrypt data with key use AES OFB algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesOfbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/VtHxtkUj-3F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesOfbDecrypt + +

Decrypt data with key use AES OFB algorithm. Length of `key` param should be 16, 24 or 32.

+ +Signature: + +```go +func AesOfbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/VtHxtkUj-3F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesGcmEncrypt + +

Encrypt data with key use AES GCM algorithm.

+ +Signature: + +```go +func AesGcmEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/rUt0-DmsPCs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### AesGcmDecrypt + +

Decrypt data with key use AES GCM algorithm.

+ +Signature: + +```go +func AesGcmDecrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/rUt0-DmsPCs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefghijklmnop" + + encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### Base64StdEncode + +

Encode string with base64 encoding.

+ +Signature: + +```go +func Base64StdEncode(s string) string +``` + +Example:[Run](https://go.dev/play/p/VOaUyQUreoK) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + base64Str := cryptor.Base64StdEncode("hello") + fmt.Println(base64Str) + + // Output: + // aGVsbG8= +} +``` + +### Base64StdDecode + +

Decode a base64 encoded string.

+ +Signature: + +```go +func Base64StdDecode(s string) string +``` + +Example:[Run](https://go.dev/play/p/RWQylnJVgIe) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := cryptor.Base64StdDecode("aGVsbG8=") + fmt.Println(str) + + // Output: + // hello +} +``` + +### DesEcbEncrypt + +

Encrypt data with key use DES ECB algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesEcbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/8qivmPeZy4P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesEcbDecrypt + +

Decrypt data with key use DES ECB algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesEcbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/8qivmPeZy4P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesEcbEncrypt([]byte(data), []byte(key)) + + decrypted := cryptor.DesEcbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCbcEncrypt + +

Encrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCbcEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/4cC4QvWfe3_1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCbcDecrypt + +

Decrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCbcDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/4cC4QvWfe3_1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCbcEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCbcDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCtrCrypt + +

Encrypt or decrypt data with key use DES CTR algorithm. Length of `key` param should be 8.

+ +> ⚠️ This function is deprecated. use `DesCtrEncrypt` and `DesCtrDecrypt` instead. + +Signature: + +```go +func DesCtrCrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/9-T6OjKpcdw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCtrCrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCtrCrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCtrCrypt + +

Encrypt data with key use DES CTR algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCtrEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/S6p_WHCgH1d) + +```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 +} +``` + +### DesCtrDecrypt + +

Decrypt data with key use DES CTR algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCtrDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/S6p_WHCgH1d) + +```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 +} +``` + +### DesCfbEncrypt + +

Encrypt data with key use DES CFB algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCfbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/y-eNxcFBlxL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesCfbDecrypt + +

Decrypt data with key use DES CBC algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesCfbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/y-eNxcFBlxL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesCfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesCfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesOfbEncrypt + +

Enecrypt data with key use DES OFB algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesOfbEncrypt(data, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/74KmNadjN1J) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### DesOfbDecrypt + +

Decrypt data with key use DES OFB algorithm. Length of `key` param should be 8.

+ +Signature: + +```go +func DesOfbDecrypt(encrypted, key []byte) []byte +``` + +Example:[Run](https://go.dev/play/p/74KmNadjN1J) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := "hello" + key := "abcdefgh" + + encrypted := cryptor.DesOfbEncrypt([]byte(data), []byte(key)) + decrypted := cryptor.DesOfbDecrypt(encrypted, []byte(key)) + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### HmacMd5 + +

Get the md5 hmac hash of string.

+ +Signature: + +```go +func HmacMd5(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/uef0q1fz53I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacMd5(str, key) + fmt.Println(hms) + + // Output: + // e834306eab892d872525d4918a7a639a +} +``` + +### HmacMd5WithBase64 + +

Get the md5 hmac hash of base64 string.

+ +Signature: + +```go +func HmacMd5WithBase64(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/UY0ng2AefFC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacMd5WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 6DQwbquJLYclJdSRinpjmg== +} +``` + +### HmacSha1 + +

Get the sha1 hmac hash of string.

+ +Signature: + +```go +func HmacSha1(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/1UI4oQ4WXKM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha1(str, key) + fmt.Println(hms) + + // Output: + // 5c6a9db0cccb92e36ed0323fd09b7f936de9ace0 +} +``` + +### HmacSha1WithBase64 + +

Return the hmac hash of string use sha1 with base64.

+ +Signature: + +```go +func HmacSha1WithBase64(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/47JmmGrnF7B) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha1WithBase64(str, key) + fmt.Println(hms) + + // Output: + // XGqdsMzLkuNu0DI/0Jt/k23prOA= +} +``` + +### HmacSha256 + +

Get the sha256 hmac hash of string

+ +Signature: + +```go +func HmacSha256(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/HhpwXxFhhC0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha256(str, key) + fmt.Println(hms) + + // Output: + // 315bb93c4e989862ba09cb62e05d73a5f376cb36f0d786edab0c320d059fde75 +} +``` + +### HmacSha256WithBase64 + +

Return the hmac hash of string use sha256 with base64.

+ +Signature: + +```go +func HmacSha256WithBase64(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/EKbkUvPTLwO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha256WithBase64(str, key) + fmt.Println(hms) + + // Output: + // MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU= +} +``` + +### HmacSha512 + +

Get the sha512 hmac hash of string.

+ +Signature: + +```go +func HmacSha512(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/59Od6m4A0Ud) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha512(str, key) + fmt.Println(hms) + + // Output: + // dd8f1290a9dd23d354e2526d9a2e9ce8cffffdd37cb320800d1c6c13d2efc363288376a196c5458daf53f8e1aa6b45a6d856303d5c0a2064bff9785861d48cfc +} +``` + +### HmacSha512WithBase64 + +

Return the hmac hash of string use sha512 with base64.

+ +Signature: + +```go +func HmacSha512WithBase64(str, key string) string +``` + +Example:[Run](https://go.dev/play/p/c6dSe3E2ydU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + key := "12345" + + hms := cryptor.HmacSha512WithBase64(str, key) + fmt.Println(hms) + + // Output: + // 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A== +} +``` + +### Md5String + +

Get the md5 value of string.

+ +Signature: + +```go +func Md5String(s string) string +``` + +Example:[Run](https://go.dev/play/p/1bLcVetbTOI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + md5Str := cryptor.Md5String(str) + fmt.Println(md5Str) + + // Output: + // 5d41402abc4b2a76b9719d911017c592 +} +``` + +### Md5StringWithBase64 + +

Return the md5 value of string with base64.

+ +Signature: + +```go +func Md5StringWithBase64(s string) string +``` + +Example:[Run](https://go.dev/play/p/Tcb-Z7LN2ax) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5StringWithBase64("hello") + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} +``` + +### Md5Byte + +

Return the md5 string of byte slice.

+ +Signature: + +```go +func Md5Byte(data []byte) string +``` + +Example:[Run](https://go.dev/play/p/suraalH8lyC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5Byte([]byte{'a'}) + fmt.Println(md5Str) + + // Output: + // 0cc175b9c0f1b6a831c399e269772661 +} +``` + +### Md5ByteWithBase64 + +

Return the md5 string of byte slice with base64.

+ +Signature: + +```go +func Md5ByteWithBase64(data []byte) string +``` + +Example:[Run](https://go.dev/play/p/Lx4gH7Vdr5_y) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + md5Str := cryptor.Md5ByteWithBase64([]byte("hello")) + fmt.Println(md5Str) + + // Output: + // XUFAKrxLKna5cZ2REBfFkg== +} +``` + +### Md5File + +

Get the md5 value of file.

+ +Signature: + +```go +func Md5File(filepath string) (string, error) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + s := cryptor.Md5File("./main.go")) + fmt.Println(s) +} +``` + +### Sha1 + +

Get the sha1 value of string.

+ +Signature: + +```go +func Sha1(str string) string +``` + +Example:[Run](https://go.dev/play/p/_m_uoD1deMT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha1(str) + fmt.Println(result) + + // Output: + // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d +} +``` + +### Sha1WithBase64 + +

Return the sha1 value (SHA-1 hash algorithm) of base64 string.

+ +Signature: + +```go +func Sha1WithBase64(str string) string +``` + +Example:[Run](https://go.dev/play/p/fSyx-Gl2l2-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha1WithBase64("hello") + fmt.Println(result) + + // Output: + // qvTGHdzF6KLavt4PO0gs2a6pQ00= +} +``` + +### Sha256 + +

Get the sha256 value of string.

+ +Signature: + +```go +func Sha256(str string) string +``` + +Example:[Run](https://go.dev/play/p/tU9tfBMIAr1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha256(str) + fmt.Println(result) + + // Output: + // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 +} +``` + +### Sha256WithBase64 + +

Return the sha256 value (SHA256 hash algorithm) of base64 string.

+ +Signature: + +```go +func Sha256WithBase64(str string) string +``` + +Example:[Run](https://go.dev/play/p/85IXJHIal1k) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha256WithBase64("hello") + fmt.Println(result) + + // Output: + // LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= +} +``` + +### Sha512 + +

Get the sha512 value of string.

+ +Signature: + +```go +func Sha512(str string) string +``` + +Example:[Run](https://go.dev/play/p/3WsvLYZxsHa) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + str := "hello" + + result := cryptor.Sha512(str) + fmt.Println(result) + + // Output: + // 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 +} +``` + +### Sha512WithBase64 + +

Get the sha512 value of string with base64.

+ +Signature: + +```go +func Sha512WithBase64(str string) string +``` + +Example:[Run](https://go.dev/play/p/q_fY2rA-k5I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + result := cryptor.Sha512WithBase64("hello") + fmt.Println(result) + + // Output: + // m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw== +} +``` + +### GenerateRsaKey + +

Create the rsa public and private key file in current directory.

+ +Signature: + +```go +func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error +``` + +Example:[Run](https://go.dev/play/p/zutRHrDqs0X) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + fmt.Println(err) + } +} +``` + +### RsaEncrypt + +

Encrypt data with public key file useing ras algorithm.

+ +Signature: + +```go +func RsaEncrypt(data []byte, pubKeyFileName string) []byte +``` + +Example:[Run](https://go.dev/play/p/7_zo6mrx-eX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") + decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### RsaDecrypt + +

Decrypt data with private key file useing ras algorithm.

+ +Signature: + +```go +func RsaDecrypt(data []byte, privateKeyFileName string) []byte +``` + +Example:[Run](https://go.dev/play/p/7_zo6mrx-eX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + err := cryptor.GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem") + if err != nil { + return + } + + data := []byte("hello") + encrypted := cryptor.RsaEncrypt(data, "rsa_public.pem") + decrypted := cryptor.RsaDecrypt(encrypted, "rsa_private.pem") + + fmt.Println(string(decrypted)) + + // Output: + // hello +} +``` + +### GenerateRsaKeyPair + +

Creates rsa private and public key.

+ +Signature: + +```go +func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) +``` + +Example:[Run](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) +} +``` + +### RsaEncryptOAEP + +

Encrypts the given data with RSA-OAEP.

+ +Signature: + +```go +func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub) + if err != nil { + return + } + + decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri) + if err != nil { + return + } + + fmt.Println(string(decrypted)) + + // Output: + // hello world +} +``` + +### RsaDecryptOAEP + +

Decrypts the data with RSA-OAEP.

+ +Signature: + +```go +func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/sSVmkfENKMz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + pri, pub := cryptor.GenerateRsaKeyPair(1024) + + data := []byte("hello world") + label := []byte("123456") + + encrypted, err := cryptor.RsaEncryptOAEP(data, label, *pub) + if err != nil { + return + } + + decrypted, err := cryptor.RsaDecryptOAEP([]byte(encrypted), label, *pri) + if err != nil { + return + } + + fmt.Println(string(decrypted)) + + // Output: + // hello world +} +``` + +### RsaSign + +

Signs the data with RSA algorithm.

+ +Signature: + +```go +func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/qhsbf8BJ6Mf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private.pem" + publicKey := "./rsa_public.pem" + + signature, err := RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } +} +``` + +### RsaVerifySign + +

Verifies the signature of the data with RSA algorithm.

+ +Signature: + +```go +func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error +``` + +Example:[Run](https://go.dev/play/p/qhsbf8BJ6Mf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/cryptor" +) + +func main() { + data := []byte("This is a test data for RSA signing") + hash := crypto.SHA256 + + privateKey := "./rsa_private.pem" + publicKey := "./rsa_public.pem" + + signature, err := RsaSign(hash, data, privateKey) + if err != nil { + return + } + + err = RsaVerifySign(hash, data, signature, publicKey) + if err != nil { + return + } +} +``` diff --git a/docs/en/api/packages/datastructure/copyonwritelist.md b/docs/en/api/packages/datastructure/copyonwritelist.md new file mode 100644 index 00000000..0419fb74 --- /dev/null +++ b/docs/en/api/packages/datastructure/copyonwritelist.md @@ -0,0 +1,525 @@ +# CopyOnWriteList + +CopyOnWriteList is a thread-safe list implementation that uses go slicing as its base. When writing, a new slice is copied and assigned to the original slice when writing is complete. When reading, the original slice is read directly. + +## 源码 + +- [https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go](https://github.com/duke-git/lancet/blob/main /datastructure/list/copyonwritelist.go) + +## 用法 + +```go +import ( + "github.com/duke-git/lancet/datastructure/list" +) + +``` + +
+ +## 目录 + +- [NewCopyOnWriteList](#NewCopyOnWriteList) +- [Size](#Size) +- [Get](#Get) +- [Set](#Set) +- [Remove](#Remove) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [IndexOfFunc](#IndexOfFunc) +- [LastIndexOfFunc](#LastIndexOfFunc) +- [IsEmpty](#IsEmpty) +- [Contain](#Contain) +- [ValueOf](#ValueOf) +- [Add](#Add) +- [AddAll](#AddAll) +- [AddByIndex](#AddByIndex) +- [DeleteAt](#DeleteAt) +- [DeleteIf](#DeleteIf) +- [DeleteBy](#DeleteBy) +- [DeleteRange](#DeleteRange) +- [Equal](#Equal) + +## Documentation + +### NewCopyOnWriteList + +Returns a CopyOnWriteList with empty slices. + +```go +type CopyOnWriteList[T any] struct { + data []T + lock sync. +} + +func NewCopyOnWriteList() *CopyOnWriteList + +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l) +} +``` + +### Size + +Returns the length of the CopyOnWriteList. + +```go +func (l *CopyOnWriteList[T]) Size() int +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Size()) +} + +``` + +### Get + +Returns the element at the specified position in the list + +```go +func (c *CopyOnWriteList[T]) Get(index int) *T +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Get(2)) +} + +``` + +### Set + +Replaces the element at the specified position in this list with the specified element. + +```go +func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool) +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.Set(2, 4)) +} + +``` + +### Remove + +### IndexOf + +Returns the index of the value in the list, or -1 if not found. + +```go +func (c *CopyOnWriteList[T]) IndexOf(e T) int +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.IndexOf(1)) +} + +``` + +### LastIndexOf + +Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain that element. + +```go +func (c *CopyOnWriteList[T]) LastIndexOf(e T) int +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,1}) + fmt.Println(l.LastIndexOf(1)) +} + +``` + + +### IndexOfFunc +

IndexOfFunc returns the first index satisfying the functional predicate f(v) bool. if not found return -1.

+ +Signature: + +```go +func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1, 2, 3}) + + fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0 + fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### LastIndexOfFunc +

LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the functional predicate f(T) bool. if not found return -1.

+ +Signature: + +```go +func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1, 2, 3, 1}) + + fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3 + fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### IsEmpty + +Returns true if this list does not contain any elements. + +```go +func (c *CopyOnWriteList[T]) IsEmpty() bool +``` + +#### Example + +```go +package main + +import ( +"fmt" +"github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{}) + fmt.Println(l.IsEmpty()) +} +``` + +### Contain + +Determines if a CopyOnWriteList contains an element. + +```go +func (c *CopyOnWriteList[T]) Contain(e T) bool +``` + +#### Example + +```go +package main + +import ( +"fmt" +"github.com/duke-git/lancet/datastructure/list" +) + +func main() { +l := list.NewCopyOnWriteList([]int{1,2,3}) +fmt.Println(l.Contain(1)) +} +``` + +### ValueOf + +Returns a pointer to the value at the index in the list + +```go +func (c *CopyOnWriteList[T]) ValueOf(index int) []T +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + fmt.Println(l.ValueOf(2)) +} + +``` + +### Add + +Appends the specified element to the end of the list. + +```go +func (c *CopyOnWriteList[T]) Add(e T) bool +``` + +#### Example + +```go + +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + l.Add(4) + fmt.Println(l.getList()) +} + +``` + +### AddAll + +Appends all the elements of the specified collection to the end of this list + +```go +func (c *CopyOnWriteList[T]) AddAll(e []T) bool +``` + +#### Example + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + l.AddAll([]int{4,5,6}) + fmt.Println(l.getList()) +} + +``` + +### AddByIndex + +Inserts the specified element into the list at the specified position. + +```go +func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool +``` + +#### Example + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.AddByIndex(2, 6) + fmt.Println(l.getList()) +} + +``` + +### DeleteAt + +Removes the element at the specified position in this list. + +```go +func (c *CopyOnWriteList[T]) DeleteAt(index int) (oldValue *T, ok bool) +``` + +#### Example + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteAt(2) + fmt.Println(l.getList()) +} +``` + +### DeleteIf + +Removes the first occurrence of the specified element from this list (if it exists). + +```go +func (c *CopyOnWriteList[T]) DeleteIf(func(T) bool) (oldValue *T, ok bool) +``` + +#### Example + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteIf(func(i int) bool { + return i == 2 + }) + fmt.Println(l.getList()) +} +``` + +### DeleteBy + +Deletes the first occurrence of the specified element from this list (if it exists). + +```go +func (c *CopyOnWriteList[T]) DeleteBy(e T) (*T bool) +``` + +#### Example + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3}) + list.DeleteBy(2) + fmt.Println(l.getList()) +} +``` + +### DeleteRange + +Deletes all elements from this list with indexes between fromIndex (included) and toIndex (not included). +(leftCloseRightOpen) + +```go +func (c *CopyOnWriteList[T]) DeleteRange(start int, end int) +``` + +#### Example + +```go +package main +import ( +"fmt" +"github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9}) + list.DeleteRange(2, 5) + fmt.Println(l.getList()) +} +``` + +### Equal + +Returns true if the specified object is equal to this list + +```go +func (c *CopyOnWriteList[T]) Equal(e []T) bool +``` + +#### Example + +```go +package main +import ( + "fmt" + "github.com/duke-git/lancet/datastructure/list" +) + +func main() { + l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9}) + fmt.Println(l.Equal([]int{1,2,3,4,5,6,7,8,9})) +} +``` diff --git a/docs/en/api/packages/datastructure/hashmap.md b/docs/en/api/packages/datastructure/hashmap.md new file mode 100644 index 00000000..59d0a4ac --- /dev/null +++ b/docs/en/api/packages/datastructure/hashmap.md @@ -0,0 +1,388 @@ +# HashMap + +HashMap is a key value map data structure. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go](https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go) + +
+ +## Usage + +```go +import ( + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) +``` + +
+ + +## Index + +- [NewHashMap](#NewHashMap) +- [NewHashMapWithCapacity](#NewHashMapWithCapacity) +- [Get](#Get) +- [Put](#Put) +- [Delete](#Delete) +- [Contains](#Contains) +- [Iterate](#Iterate) +- [Keys](#Keys) +- [Values](#Values) +- [FilterByValue](#FilterByValue) + +
+ +## Documentation + +### NewHashMap + +

Make a HashMap instance with default capacity is 1 << 10.

+ +Signature: + +```go +func NewHashMap() *HashMap +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + fmt.Println(hm) +} +``` + +### NewHashMap + +

Make a HashMap instance with given size and capacity.

+ +Signature: + +```go +func NewHashMapWithCapacity(size, capacity uint64) *HashMap +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMapWithCapacity(uint64(100), uint64(1000)) + fmt.Println(hm) +} +``` + +### Get + +

Get the value of given key in hashmap

+ +Signature: + +```go +func (hm *HashMap) Get(key any) any +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + val := hm.Get("a") + + fmt.Println(val) //nil +} +``` + +### Put + +

Put new key value in hashmap, then return value

+ +Signature: + +```go +func (hm *HashMap) Put(key any, value any) any +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + + val := hm.Get("a") + fmt.Println(val) //1 +} +``` + + + +### Delete + +

Delete key-value item by given key in hashmap.

+ +Signature: + +```go +func (hm *HashMap) Delete(key any) +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + val := hm.Get("a") + fmt.Println(val) //1 + + hm.Delete("a") + val = hm.Get("a") + fmt.Println(val) //nil +} +``` + + + +### Contains + +

Checks if given key is in hashmap or not.

+ +Signature: + +```go +func (hm *HashMap) Contains(key any) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + + fmt.Println(hm.Contains("a")) //true + fmt.Println(hm.Contains("b")) //false +} +``` + + + +### Iterate + +

Executes iteratee funcation for every key and value pair of hashmap.

+ +Signature: + +```go +func (hm *HashMap) Iterate(iteratee func(key, value any)) +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + hm.Iterate(func(key, value any) { + fmt.Println(key) + fmt.Println(value) + }) +} +``` + + + +### Keys + +

Return a slice of the hashmap's keys (random order).

+ +Signature: + +```go +func (hm *HashMap) Keys() []any +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + keys := hm.Keys() + fmt.Println(keys) //[]interface{"a", "b", "c"} +} +``` + + +### Values + +

Return a slice of the hashmap's values (random order).

+ +Signature: + +```go +func (hm *HashMap) Values() []any +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := heap.NewHashMap() + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + + values := hm.Values() + fmt.Println(values) //[]interface{2, 1, 3} +} +``` + +### FilterByValue + +

Returns a filtered HashMap.

+ +Signature: + +```go +func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap +``` + +Example: + +```go +package main + +import ( + "fmt" + hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap" +) + +func main() { + hm := hashmap.NewHashMap() + + hm.Put("a", 1) + hm.Put("b", 2) + hm.Put("c", 3) + hm.Put("d", 4) + hm.Put("e", 5) + hm.Put("f", 6) + + filteredHM := hm.FilterByValue(func(value any) bool { + return value.(int) == 1 || value.(int) == 3 + }) + + fmt.Println(filteredHM.Size()) //2 +} +``` + + + +### ToInterface + +

Converts reflect value to its interface type.

+ +Signature: + +```go +func ToInterface(v reflect.Value) (value interface{}, ok bool) +``` + +Example:[Run](https://go.dev/play/p/syqw0-WG7Xd) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/convertor" +) + +func main() { + val := reflect.ValueOf("abc") + iVal, ok := convertor.ToInterface(val) + + fmt.Printf("%T\n", iVal) + fmt.Printf("%v\n", iVal) + fmt.Println(ok) + + // Output: + // string + // abc + // true +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/datastructure/heap.md b/docs/en/api/packages/datastructure/heap.md new file mode 100644 index 00000000..863bfc33 --- /dev/null +++ b/docs/en/api/packages/datastructure/heap.md @@ -0,0 +1,364 @@ +# Heap +Heap is a binary heap tree implemented by slice. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go](https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go) + + +
+ +## Usage +```go +import ( + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) +``` + +
+ +## Index + +- [MaxHeap](#MaxHeap) +- [Push](#Push) +- [Pop](#Pop) +- [Peek](#Peek) +- [Data](#Data) +- [Size](#Size) + + +
+ +## Documentation + +### 1. MaxHeap +MaxHeap is a binary heap tree implemented by slice, The key of the root node is both greater than or equal to the key value of the left subtree and greater than or equal to the key value of the right subtree. + +### NewMaxHeap +

Return a NewMaxHeap pointer instance.

+ +Signature: + +```go +type MaxHeap[T any] struct { + data []T + comparator constraints.Comparator +} +func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T] +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + fmt.Println(maxHeap) +} +``` + + + + +### Push +

Push value into the heap

+ +Signature: + +```go +func (h *MaxHeap[T]) Push(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} +} +``` + + + + +### Pop +

Pop return the largest value, and remove it from the heap if heap is empty, return zero value and fasle

+ +Signature: + +```go +func (h *MaxHeap[T]) Pop() (T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + val, ok := maxHeap.Pop() + + fmt.Println(val) //12 + fmt.Println(ok) //true +} +``` + + + +### Peek +

Return the largest element from the heap without removing it, if heap is empty, it returns zero value and false.

+ +Signature: + +```go +func (h *MaxHeap[T]) Peek() (T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + val, ok := maxHeap.Peek() + + fmt.Println(val) //12 + fmt.Println(maxHeap.Size()) //12 +} +``` + + + +### Data +

Return all element of the heap

+ +Signature: + +```go +func (h *MaxHeap[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2} +} +``` + + +### Size +

Return the number of elements in the heap

+ +Signature: + +```go +func (h *MaxHeap[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.Size()) //3 +} +``` + + + +### PrintStructure +

Print the tree structure of the heap

+ +Signature: + +```go +func (h *MaxHeap[T]) PrintStructure() +``` +Example: + +```go +package main + +import ( + "fmt" + heap "github.com/duke-git/lancet/v2/datastructure/heap" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + maxHeap := heap.NewMaxHeap[int](&intComparator{}) + values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11} + + for _, v := range values { + maxHeap.Push(v) + } + + fmt.Println(maxHeap.PrintStructure()) +// 12 +// 9 11 +// 4 8 10 7 +// 1 3 5 6 2 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/datastructure/link.md b/docs/en/api/packages/datastructure/link.md new file mode 100644 index 00000000..174ccff9 --- /dev/null +++ b/docs/en/api/packages/datastructure/link.md @@ -0,0 +1,1018 @@ +# Linklist +Linklist a linked list, whose node has a value and a pointer points to next node of the link. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/link/singlylink.go](https://github.com/duke-git/lancet/blob/main/datastructure/link/singlylink.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/link/doublylink.go](https://github.com/duke-git/lancet/blob/main/datastructure/link/doublylink.go) + + +
+ +## Usage +```go +import ( + link "github.com/duke-git/lancet/v2/datastructure/link" +) +``` + +
+ +## Index + +### 1. SinglyLink + +- [NewSinglyLink](#NewSinglyLink) +- [Values](#SinglyLink_Values) +- [InsertAt](#SinglyLink_InsertAt) +- [InsertAtHead](#SinglyLink_InsertAtHead) +- [InsertAtTail](#SinglyLink_InsertAtTail) +- [DeleteAt](#SinglyLink_DeleteAt) +- [DeleteAtHead](#SinglyLink_DeleteAtHead) +- [DeleteAtTail](#SinglyLink_DeleteAtTail) +- [DeleteValue](#SinglyLink_DeleteValue) +- [Reverse](#SinglyLink_Reverse) +- [GetMiddleNode](#SinglyLink_GetMiddleNode) +- [Size](#SinglyLink_Size) +- [IsEmpty](#SinglyLink_IsEmpty) +- [Clear](#SinglyLink_Clear) +- [Print](#SinglyLink_Print) + +### 2. DoublyLink + +- [NewDoublyLink](#NewDoublyLink) +- [Values](#DoublyLink_Values) +- [InsertAt](#DoublyLink_InsertAt) +- [InsertAtHead](#DoublyLink_InsertAtHead) +- [InsertAtTail](#DoublyLink_InsertAtTail) +- [DeleteAt](#DoublyLink_DeleteAt) +- [DeleteAtHead](#DoublyLink_DeleteAtHead) +- [DeleteAtTail](#DoublyLink_DeleteAtTail) +- [Reverse](#DoublyLink_Reverse) +- [GetMiddleNode](#DoublyLink_GetMiddleNode) +- [Size](#DoublyLink_Size) +- [IsEmpty](#DoublyLink_IsEmpty) +- [Clear](#DoublyLink_Clear) +- [Print](#DoublyLink_Print) + + +
+ +## Documentation + +### 1. SinglyLink +SinglyLink a linked list, whose node has a value and a pointer points to next node of the link. + +### NewSinglyLink +

Return a singly link(SinglyLink) instance

+ +Signature: + +```go +type LinkNode[T any] struct { + Value T + Next *LinkNode[T] +} +type SinglyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} +func NewSinglyLink[T any]() *SinglyLink[T] +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + fmt.Println(lk) +} +``` + + + +### Values +

Return a slice of all node value in singly linklist

+ +Signature: + +```go +func (link *SinglyLink[T]) Values() []T +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + + +### InsertAt +

Insert value into singly linklist at index, param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing

+ +Signature: + +```go +func (link *SinglyLink[T]) InsertAt(index int, value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAt(1, 1) //do nothing + + lk.InsertAt(0, 1) + lk.InsertAt(1, 2) + lk.InsertAt(2, 3) + lk.InsertAt(2, 4) + + fmt.Println(lk.Values()) //[]int{1, 2, 4, 3} +} +``` + + + + +### InsertAtHead +

Insert value into singly linklist at head(first) index

+ +Signature: + +```go +func (link *SinglyLink[T]) InsertAtHead(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtHead(1) + lk.InsertAtHead(2) + lk.InsertAtHead(3) + + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + + +### InsertAtTail +

Insert value into singly linklist at tail(last) index

+ +Signature: + +```go +func (link *SinglyLink[T]) InsertAtTail(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAt +

Delete value at specific index, param `index` should be [0, len(SinglyLink)-1]

+ +Signature: + +```go +func (link *SinglyLink[T]) DeleteAt(index int) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAt(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAtHead +

Delete value in singly linklist at first index

+ +Signature: + +```go +func (link *SinglyLink[T]) DeleteAtHead() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAtHead() + + fmt.Println(lk.Values()) //[]int{2, 3, 4} +} +``` + + + + +### DeleteAtTail +

Delete value in singly linklist at last index

+ +Signature: + +```go +func (link *SinglyLink[T]) DeleteAtTail() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.DeleteAtTail() + + fmt.Println(lk.Values()) //[]int{1, 2} +} +``` + + + +### DeleteValue +

Delete all `value` in singly linklist

+ +Signature: + +```go +func (link *SinglyLink[T]) DeleteValue(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.DeleteValue(2) + fmt.Println(lk.Values()) //[]int{1, 3} +} +``` + + + + +### Reverse +

Reverse all nodes order in linkist

+ +Signature: + +```go +func (link *SinglyLink[T]) Reverse() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Reverse() + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + +### GetMiddleNode +

Get the node at middle index of linkist

+ +Signature: + +```go +func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + midNode := lk.GetMiddleNode() + fmt.Println(midNode.Value) //2 +} +``` + + + +### Size +

Get the number of nodes in linklist

+ +Signature: + +```go +func (link *SinglyLink[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Size()) //3 +} +``` + + + +### IsEmpty +

Checks if linklist is empty or not

+ +Signature: + +```go +func (link *SinglyLink[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + fmt.Println(lk.IsEmpty()) //true + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.IsEmpty()) //false +} +``` + + + +### Clear +

Clear all nodes in the linklist, make it empty

+ +Signature: + +```go +func (link *SinglyLink[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Clear() + + fmt.Println(lk.Values()) // +} +``` + + + +### Print +

Print all nodes info of linklist

+ +Signature: + +```go +func (link *SinglyLink[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewSinglyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Print() //[ &{Value:1 Pre: Next:0xc0000a4048}, &{Value:2 Pre: Next:0xc0000a4060}, &{Value:3 Pre: Next:} ] +} +``` + + + +### 2. DoublyLink +DoublyLink is a linked list, whose node has a value, a next pointer points to next node and pre pointer points to previous node of the link. + +### NewDoublyLink +

Return a doubly link instance

+ +Signature: + +```go +type LinkNode[T any] struct { + Value T + Pre *LinkNode[T] + Next *LinkNode[T] +} +type DoublyLink[T any] struct { + Head *datastructure.LinkNode[T] + length int +} +func NewDoublyLink[T any]() *DoublyLink[T] +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + fmt.Println(lk) +} +``` + + + +### Values +

Return a slice of all node value in doubly linklist

+ +Signature: + +```go +func (link *DoublyLink[T]) Values() []T +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + + +### InsertAt +

Insert value into doubly linklist at index, param `index` should between [0, len(DoublyLink)], if index do not meet the conditions, do nothing

+ +Signature: + +```go +func (link *DoublyLink[T]) InsertAt(index int, value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAt(1, 1) //do nothing + + lk.InsertAt(0, 1) + lk.InsertAt(1, 2) + lk.InsertAt(2, 3) + lk.InsertAt(2, 4) + + fmt.Println(lk.Values()) //[]int{1, 2, 4, 3} +} +``` + + + + +### InsertAtHead +

Insert value into doubly linklist at head(first) index

+ +Signature: + +```go +func (link *DoublyLink[T]) InsertAtHead(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtHead(1) + lk.InsertAtHead(2) + lk.InsertAtHead(3) + + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + + +### InsertAtTail +

Insert value into doubly linklist at tail(last) index

+ +Signature: + +```go +func (link *DoublyLink[T]) InsertAtTail(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAt +

Delete value at specific index, param `index` should be [0, len(DoublyLink)-1]

+ +Signature: + +```go +func (link *DoublyLink[T]) DeleteAt(index int) +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAt(3) + + fmt.Println(lk.Values()) //[]int{1, 2, 3} +} +``` + + + +### DeleteAtHead +

Delete value in doubly linklist at first index

+ +Signature: + +```go +func (link *DoublyLink[T]) DeleteAtHead() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + lk.InsertAtTail(4) + + lk.DeleteAtHead() + + fmt.Println(lk.Values()) //[]int{2, 3, 4} +} +``` + + + + +### DeleteAtTail +

Delete value in doubly linklist at last index

+ +Signature: + +```go +func (link *DoublyLink[T]) DeleteAtTail() error +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + err := lk.DeleteAtTail() + + fmt.Println(err) //nil + fmt.Println(lk.Values()) //[]int{1, 2} +} +``` + + + + +### Reverse +

Reverse all nodes order in linkist

+ +Signature: + +```go +func (link *DoublyLink[T]) Reverse() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Reverse() + fmt.Println(lk.Values()) //[]int{3, 2, 1} +} +``` + + + +### GetMiddleNode +

Get the node at middle index of linkist

+ +Signature: + +```go +func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + midNode := lk.GetMiddleNode() + fmt.Println(midNode.Value) //2 +} +``` + + + +### Size +

Get the number of nodes in linklist

+ +Signature: + +```go +func (link *DoublyLink[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.Size()) //3 +} +``` + + + +### IsEmpty +

Checks if linklist is empty or not

+ +Signature: + +```go +func (link *DoublyLink[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + fmt.Println(lk.IsEmpty()) //true + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + fmt.Println(lk.IsEmpty()) //false +} +``` + + + +### Clear +

Clear all nodes in the linklist, make it empty

+ +Signature: + +```go +func (link *DoublyLink[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Clear() + + fmt.Println(lk.Values()) // +} +``` + + + +### Print +

Print all nodes info of linklist

+ +Signature: + +```go +func (link *DoublyLink[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + link "github.com/duke-git/lancet/v2/datastructure/link" +) + +func main() { + lk := link.NewDoublyLink[int]() + + lk.InsertAtTail(1) + lk.InsertAtTail(2) + lk.InsertAtTail(3) + + lk.Print() // +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/datastructure/list.md b/docs/en/api/packages/datastructure/list.md new file mode 100644 index 00000000..6f6d9b8f --- /dev/null +++ b/docs/en/api/packages/datastructure/list.md @@ -0,0 +1,1118 @@ +# List +List is a linear table, implemented with slice. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/list/list.go](https://github.com/duke-git/lancet/blob/main/datastructure/list/list.go) + + +
+ +## Usage +```go +import ( + list "github.com/duke-git/lancet/v2/datastructure/list" +) +``` + +
+ +## Index + +- [NewList](#NewList) +- [Contain](#Contain) +- [Data](#Data) +- [ValueOf](#ValueOf) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [IndexOfFunc](#IndexOfFunc) +- [LastIndexOfFunc](#LastIndexOfFunc) +- [Push](#Push) +- [PopFirst](#PopFirst) +- [PopLast](#PopLast) +- [DeleteAt](#DeleteAt) +- [InsertAt](#InsertAt) +- [UpdateAt](#UpdateAt) +- [Equal](#Equal) +- [IsEmpty](#IsEmpty) +- [Clear](#Clear) +- [Clone](#Clone) +- [Merge](#Merge) +- [Size](#Size) +- [Cap](#Cap) +- [Swap](#Swap) +- [Reverse](#Reverse) +- [Unique](#Unique) +- [Union](#Union) +- [Intersection](#Intersection) +- [Difference](#Difference) +- [SymmetricDifference](#SymmetricDifference) +- [RetainAll](#RetainAll) +- [DeleteAll](#DeleteAll) +- [ForEach](#ForEach) +- [Iterator](#Iterator) +- [ListToMap](#ListToMap) +- [SubList](#SubList) +- [DeleteIf](#DeleteIf) + +
+ +## Documentation + +### NewList +

List is a linear table, implemented with slice. +NewList function return a list pointer

+ +Signature: + +```go +type List[T any] struct { + data []T +} +func NewList[T any](data []T) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + fmt.Println(li) +} +``` + + + +### Contain +

Check if the value in the list or not

+ +Signature: + +```go +func (l *List[T]) Contain(value T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.Contain(1)) //true + fmt.Println(li.Contain(0)) //false +} +``` + + + + +### Data +

Return slice of list data

+ +Signature: + +```go +func (l *List[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + data := li.Data() + + fmt.Println(data) //[]int{1, 2, 3} +} +``` + + + + +### ValueOf +

Return the value pointer at index in list

+ +Signature: + +```go +func (l *List[T]) ValueOf(index int) (*T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.ValueOf(0) + + fmt.Println(*v) //1 + fmt.Println(ok) //true +} +``` + + + + +### IndexOf +

Returns the index of value in the list. if not found return -1

+ +Signature: + +```go +func (l *List[T]) IndexOf(value T) int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.IndexOf(1)) //0 + fmt.Println(li.IndexOf(0)) //-1 +} +``` + +### LastIndexOf +

Returns the index of the last occurrence of the value in this list if not found return -1

+ +Signature: + +```go +func (l *List[T]) LastIndexOf(value T) int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 1}) + + fmt.Println(li.LastIndexOf(1)) // 3 + fmt.Println(li.LastIndexOf(0)) //-1 +} +``` + +### IndexOfFunc +

IndexOfFunc returns the first index satisfying f(v). if not found return -1

+ +Signature: + +```go +func (l *List[T]) IndexOfFunc(f func(T) bool) int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + fmt.Println(li.IndexOfFunc(func(a int) bool { return a == 1 })) //0 + fmt.Println(li.IndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + +### LastIndexOfFunc +

LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying f(data[i]). if not found return -1

+ +Signature: + +```go +func (l *List[T]) LastIndexOfFunc(f func(T) bool) int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 1}) + + fmt.Println(li.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3 + fmt.Println(li.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1 +} +``` + + + +### Push +

Append value to the list

+ +Signature: + +```go +func (l *List[T]) Push(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + li.Push(4) + + fmt.Println(li.Data()) //[]int{1, 2, 3, 4} +} +``` + + + + +### PopFirst +

Delete the first value of list and return it

+ +Signature: + +```go +func (l *List[T]) PopFirst() (*T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.PopFirst() + + fmt.Println(*v) //1 + fmt.Println(ok) //true + fmt.Println(li.Data()) //2, 3 +} +``` + + + + + +### PopFirst +

Delete the last value of list and return it

+ +Signature: + +```go +func (l *List[T]) PopLast() (*T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + v, ok := li.PopLast() + + fmt.Println(*v) //3 + fmt.Println(ok) //true + fmt.Println(li.Data()) //1, 2 +} +``` + + + + +### DeleteAt +

Delete the value of list at index, if index is not between 0 and length of list data, do nothing

+ +Signature: + +```go +func (l *List[T]) DeleteAt(index int) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + + li.DeleteAt(-1) + fmt.Println(li.Data()) //1,2,3,4 + + li.DeleteAt(4) + fmt.Println(li.Data()) //1,2,3,4 + + li.DeleteAt(0) + fmt.Println(li.Data()) //2,3,4 + + li.DeleteAt(2) + fmt.Println(li.Data()) //2,3 +} +``` + + + + +### InsertAt +

Insert value into list at index, if index is not between 0 and length of list data, do nothing

+ +Signature: + +```go +func (l *List[T]) InsertAt(index int, value T) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + li.InsertAt(-1, 0) + fmt.Println(li.Data()) //1,2,3 + + li.InsertAt(4, 0) + fmt.Println(li.Data()) //1,2,3 + + li.InsertAt(3, 4) + fmt.Println(li.Data()) //1,2,3,4 + + // li.InsertAt(2, 4) + // fmt.Println(li.Data()) //1,2,4,3 +} +``` + + + +### UpdateAt +

Update value of list at index, index shoud between 0 and list size - 1

+ +Signature: + +```go +func (l *List[T]) UpdateAt(index int, value T) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + + li.UpdateAt(-1, 0) + fmt.Println(li.Data()) //1,2,3 + + li.UpdateAt(2, 4) + fmt.Println(li.Data()) //1,2,4 + + li.UpdateAt(3, 5) + fmt.Println(li.Data()) //1,2,4 +} +``` + + +### Equal +

Compare a list to another list, use reflect.DeepEqual on every element

+ +Signature: + +```go +func (l *List[T]) Equal(other *List[T]) bool +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{1, 2, 3, 4}) + li3 := list.NewList([]int{1, 2, 3}) + + fmt.Println(li1.Equal(li2)) //true + fmt.Println(li1.Equal(li3)) //false +} +``` + + + +### IsEmpty +

Check if a list is empty or not

+ +Signature: + +```go +func (l *List[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3}) + li2 := list.NewList([]int{}) + + fmt.Println(li1.IsEmpty()) //false + fmt.Println(li2.IsEmpty()) //true +} +``` + + + + +### Clear +

Clear the data of list

+ +Signature: + +```go +func (l *List[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + li.Clear() + + fmt.Println(li.Data()) // empty +} +``` + + + +### Clone +

Return a copy of list

+ +Signature: + +```go +func (l *List[T]) Clone() *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3}) + cloneList := li.Clone() + + fmt.Println(cloneList.Data()) // 1,2,3 +} +``` + + + + +### Merge +

Merge two list, return new list, don't change original list

+ +Signature: + +```go +func (l *List[T]) Merge(other *List[T]) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Merge(li2) + + fmt.Println(li3.Data()) //1, 2, 3, 4, 4, 5, 6 +} +``` + + + +### Size +

Return number of list data items

+ +Signature: + +```go +func (l *List[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + + fmt.Println(li.Size()) //4 +} +``` + + + + +### Cap +

Cap return cap of the inner data

+ +Signature: + +```go +func (l *List[T]) Cap() int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + data := make([]int, 0, 100) + + li := list.NewList(data) + + fmt.Println(li.Cap()) // 100 +} +``` + + + + +### Swap +

Swap the value at two index in list

+ +Signature: + +```go +func (l *List[T]) Swap(i, j int) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + li.Swap(0, 3) + + fmt.Println(li.Data()) //4, 2, 3, 1 +} +``` + + + + +### Reverse +

Reverse the data item order of list

+ +Signature: + +```go +func (l *List[T]) Reverse() +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 3, 4}) + li.Reverse() + + fmt.Println(li.Data()) //4, 3, 2, 1 +} +``` + + + + +### Unique +

Remove duplicate items in list

+ +Signature: + +```go +func (l *List[T]) Unique() +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li := list.NewList([]int{1, 2, 2, 3, 4}) + li.Unique() + + fmt.Println(li.Data()) //1,2,3,4 +} +``` + + + + +### Union +

Creates a new list contain all elements in list l and other, remove duplicate element

+ +Signature: + +```go +func (l *List[T]) Union(other *List[T]) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Union(li2) + + fmt.Println(li3.Data()) //1,2,3,4,5,6 +} +``` + + + + +### Intersection +

Creates a new list whose element both be contained in list l and other

+ +Signature: + +```go +func (l *List[T]) Intersection(other *List[T]) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + li1 := list.NewList([]int{1, 2, 3, 4}) + li2 := list.NewList([]int{4, 5, 6}) + li3 := li1.Intersection(li2) + + fmt.Println(li3.Data()) //4 +} +``` + + + +### Difference +

Return a list whose element in the original list, not in the given list.

+ +Signature: + +```go +func (l *List[T]) Difference(other *List[T]) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + + list3 := list1.Intersection(list2) + + fmt.Println(list3.Data()) //3 +} +``` + + +### SymmetricDifference +

Oppoiste operation of intersection function.

+ +Signature: + +```go +func (l *List[T]) SymmetricDifference(other *List[T]) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list1 := NewList([]int{1, 2, 3}) + list2 := NewList([]int{1, 2, 4}) + + list3 := list1.Intersection(list2) + + fmt.Println(list3.Data()) //3, 4 +} +``` + + +### RetainAll +

Retains only the elements in this list that are contained in the given list.

+ +Signature: + +```go +func (l *List[T]) RetainAll(list *List[T]) bool +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + retain := NewList([]int{1, 2}) + retain1 := NewList([]int{2, 3}) + retain2 := NewList([]int{1, 2, 5}) + + list.RetainAll(retain) + list1.RetainAll(retain1) + list2.RetainAll(retain2) + + fmt.Println(list.Data()) //1, 2 + fmt.Println(list1.Data()) //2, 3 + fmt.Println(list2.Data()) //1, 2 +} +``` + + +### DeleteAll +

Removes from this list all of its elements that are contained in the given list.

+ +Signature: + +```go +func (l *List[T]) DeleteAll(list *List[T]) bool +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + list1 := NewList([]int{1, 2, 3, 4}) + list2 := NewList([]int{1, 2, 3, 4}) + + del := NewList([]int{1}) + del1 := NewList([]int{2, 3}) + del2 := NewList([]int{1, 2, 5}) + + list.DeleteAll(del) + list1.DeleteAll(del1) + list2.DeleteAll(del2) + + fmt.Println(list.Data()) //2,3,4 + fmt.Println(list1.Data()) //1,4 + fmt.Println(list2.Data()) //3,4 +} +``` + + +### ForEach +

Performs the given action for each element of the list.

+ +Signature: + +```go +func (l *List[T]) ForEach(consumer func(T)) +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + result := make([]int, 0) + list.ForEach(func(i int) { + result = append(result, i) + }) + + fmt.Println(result.Data()) //1,2,3,4 +} +``` + + +### Iterator +

Returns an iterator over the elements in this list in proper sequence.

+ +Signature: + +```go +func (l *List[T]) Iterator() iterator.Iterator[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + iterator := list.Iterator() + + result := make([]int, 0) + for iterator.HasNext() { + item, _ := iterator.Next() + result = append(result, item) + } + + fmt.Println(result.Data()) //1,2,3,4 +} +``` + + +### ListToMap +

Converts a list to a map based on iteratee function.

+ +Signature: + +```go +func ListToMap[T any, K comparable, V any](list *List[T], iteratee func(T) (K, V)) map[K]V +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + list := NewList([]int{1, 2, 3, 4}) + + result := ListToMap(list, func(n int) (int, bool) { + return n, n > 1 + }) + + fmt.Println(result) //map[int]bool{1: false, 2: true, 3: true, 4: true} +} +``` + +### SubList +

SubList returns a sub list of the original list between the specified fromIndex, inclusive, and toIndex, exclusive.

+ +Signature: + +```go +func (l *List[T]) SubList(fromIndex, toIndex int) *List[T] +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewList([]int{1, 2, 3, 4, 5, 6}) + + fmt.Println(l.SubList(2, 5)) // []int{3, 4, 5} +} +``` + + + + +### DeleteIf +

DeleteIf delete all satisfying f(data[i]), returns count of removed elements

+ +Signature: + +```go +func (l *List[T]) DeleteIf(f func(T) bool) int +``` +Example: + +```go +package main + +import ( + "fmt" + list "github.com/duke-git/lancet/v2/datastructure/list" +) + +func main() { + l := list.NewList([]int{1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1}) + + fmt.Println(l.DeleteIf(func(a int) bool { return a == 1 })) // 12 + fmt.Println(l.Data()) // []int{2, 3, 4} +} +``` diff --git a/docs/en/api/packages/datastructure/optional.md b/docs/en/api/packages/datastructure/optional.md new file mode 100644 index 00000000..0b91700f --- /dev/null +++ b/docs/en/api/packages/datastructure/optional.md @@ -0,0 +1,416 @@ +# Optional +Optional is a type that may or may not contain a non-nil value. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go) + + +
+ +## Usage +```go +import ( + "github.com/duke-git/lancet/v2/datastructure/optional" +) +``` + +
+ +## Index + +- [Of](#Of) +- [FromNillable](#FromNillable) +- [Default](#Default) +- [IsNotNil](#IsNotNil) +- [IsNil](#IsNil) +- [IsNotNil](#IsNotNil) +- [IfNotNilOrElse](#IfNotNilOrElse) +- [Umwarp](#Umwarp) +- [OrElse](#OrElse) +- [OrElseGet](#OrElseGet) +- [OrElseTrigger](#OrElseTrigger) + + + +
+ +## Documentation + +### Of +

Returns an Optional with a non-nil value.

+ +Signature: + +```go +func Of[T any](value T) Optional[T] +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + value := 42 + opt := optional.Of(value) + + fmt.Println(opt.Get()) + + // Output: + // 42 +} +``` + +### FromNillable +

Returns an Optional for a given value, which may be nil.

+ +Signature: + +```go +func FromNillable[T any](value *T) Optional[T] +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + var value *int = nil + opt := optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + value = new(int) + *value = 42 + opt = optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + + // Output: + // false + // true +} +``` + + +### Default +

Returns an default Optional instance.

+ +Signature: + +```go +func Default[T any]() Optional[T] +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + fmt.Println(optDefault.IsNil()) + + // Output: + // true +} +``` + + +### IsNil +

Checks if the Optional is nil.

+ +Signature: + +```go +func (o Optional[T]) IsNil() bool +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + fmt.Println(optDefault.IsNil()) + + // Output: + // true +} +``` + + +### IsNotNil +

Checks if there is a value not nil.

+ +Signature: + +```go +func (o Optional[T]) IsNotNil() bool +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + var value *int = nil + opt := optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + value = new(int) + *value = 42 + opt = optional.FromNillable(value) + + fmt.Println(opt.IsNotNil()) + + + // Output: + // false + // true +} +``` + + +### IfNotNil +

Performs the given action with the value if a value is present.

+ +Signature: + +```go +func (o Optional[T]) IfNotNil(action func(value T)) +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + called := false + action := func(value int) { called = true } + + optDefault := optional.Default[int]() + optDefault.IfNotNil(action) + + fmt.Println(called) + + called = false // Reset for next test + optWithValue := optional.Of(42) + optWithValue.IfNotNil(action) + + fmt.Println(optWithValue.IsNotNil()) + + // Output: + // false + // true +} +``` + + +### IfNotNilOrElse +

Performs the action with the value if not nil, otherwise performs the fallback action.

+ +Signature: + +```go +func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + calledWithValue := false + valueAction := func(value int) { calledWithValue = true } + emptyAction := func() { t.Errorf("Empty action should not be called when value is present") } + + optWithValue := optional.Of(42) + optWithValue.IfNotNilOrElse(valueAction, emptyAction) + + fmt.Println(calledWithValue) + + calledWithEmpty := false + valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") } + emptyAction = func() { calledWithEmpty = true } + + optDefault := optional.Default[int]() + optDefault.IfNotNilOrElse(valueAction, emptyAction) + + fmt.Println(calledWithEmpty) + + // Output: + // true + // true +} +``` + +### Unwrap +

Returns the value if not nil, otherwise panics.

+ +Signature: + +```go +func (o Optional[T]) Unwrap() T +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + value := 42 + opt := optional.Of(value) + + fmt.Println(opt.Unwrap()) + + // Output: + // 42 +} +``` + + +### OrElse +

Returns the value if not nill, otherwise returns other.

+ +Signature: + +```go +func (o Optional[T]) OrElse(other T) T +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + val := optDefault.OrElse(100) + fmt.Println(val) + + optWithValue := optional.Of(42) + val = optWithValue.OrElse(100) + fmt.Println(val) + + // Output: + // 100 + // 42 +} +``` + + +### OrElseGet +

Returns the value if not nil, otherwise invokes action and returns the result.

+ +Signature: + +```go +func (o Optional[T]) OrElseGet(action func() T) T +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + action := func() int { return 100 } + + val := optDefault.OrElseGet(action) + fmt.Println(val) + + // Output: + // 100 +} +``` + + +### OrElseTrigger +

Returns the value if present, otherwise returns an error.

+ +Signature: + +```go + OrElseTrigger(errorHandler func() error) (T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datastructure/optional" +) + +func main() { + optDefault := optional.Default[int]() + _, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") }) + + fmt.Println(err.Error()) + + optWithValue := optional.Of(42) + val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") }) + + fmt.Println(val) + fmt.Println(err) + + // Output: + // no value + // 42 + // nil +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/datastructure/queue.md b/docs/en/api/packages/datastructure/queue.md new file mode 100644 index 00000000..623adc48 --- /dev/null +++ b/docs/en/api/packages/datastructure/queue.md @@ -0,0 +1,1387 @@ +# Queue +A queue is a kind of linear table. It only allows delete operations at the front of the table and insert operations at the rear of the table. This package includes ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/arrayqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/arrayqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/linkedqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/linkedqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/circularqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/circularqueue.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/queue/priorityqueue.go](https://github.com/duke-git/lancet/blob/main/datastructure/queue/priorityqueue.go) + +
+ +## Usage +```go +import ( + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) +``` + +
+ +## Index + +### 1. ArrayQueue +- [NewArrayQueue](#NewArrayQueue) +- [Data](#ArrayQueue_Data) +- [Enqueue](#ArrayQueue_Enqueue) +- [Dequeue](#ArrayQueue_Dequeue) +- [Front](#ArrayQueue_Front) +- [Back](#ArrayQueue_Back) +- [Size](#ArrayQueue_Size) +- [IsEmpty](#ArrayQueue_IsEmpty) +- [IsFull](#ArrayQueue_IsFull) +- [Clear](#ArrayQueue_Clear) +- [Contain](#ArrayQueue_Contain) + + + +### 2. LinkedQueue +- [NewLinkedQueue](#NewLinkedQueue) +- [Data](#LinkedQueue_Data) +- [Enqueue](#LinkedQueue_Enqueue) +- [Dequeue](#LinkedQueue_Dequeue) +- [Front](#LinkedQueue_Front) +- [Back](#LinkedQueue_Back) +- [Size](#LinkedQueue_Size) +- [IsEmpty](#LinkedQueue_IsEmpty) +- [Clear](#LinkedQueue_Clear) +- [Contain](#LinkedQueue_Contain) + + +### 3. CircularQueue +- [NewCircularQueue](#NewCircularQueue) +- [Data](#CircularQueue_Data) +- [Enqueue](#CircularQueue_Enqueue) +- [Dequeue](#CircularQueue_Dequeue) +- [Front](#CircularQueue_Front) +- [Back](#CircularQueue_Back) +- [Size](#CircularQueue_Size) +- [IsEmpty](#CircularQueue_IsEmpty) +- [IsFull](#CircularQueue_IsFull) +- [Clear](#CircularQueue_Clear) +- [Contain](#CircularQueue_Contain) + + + +### 4. PriorityQueue +- [NewPriorityQueue](#NewPriorityQueue) +- [Data](#PriorityQueue_Data) +- [Enqueue](#PriorityQueue_Enqueue) +- [Dequeue](#PriorityQueue_Dequeue) +- [IsEmpty](#PriorityQueue_IsEmpty) +- [IsFull](#PriorityQueue_IsFull) +- [Size](#PriorityQueue_Size) + + +
+ +## Documentation + +### 1. ArrayQueue +Common queue implemented by slice. + +### NewArrayQueue +

Return a ArrayQueue pointer with specific capacity

+ +Signature: + +```go +func NewArrayQueue[T any](capacity int) *ArrayQueue[T] + +type ArrayQueue[T any] struct { + items []T + head int + tail int + capacity int + size int +} +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

Get all queue data

+ +Signature: + +```go +func (q *ArrayQueue[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

Put element into queue, if queue is full, return false

+ +Signature: + +```go +func (q *ArrayQueue[T]) Enqueue(item T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

Remove head element of queue and return it

+ +Signature: + +```go +func (q *ArrayQueue[T]) Dequeue() (T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Dequeue()) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

Just get the head element of queue

+ +Signature: + +```go +func (q *ArrayQueue[T]) Front() T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

Just get the tail element of queue

+ +Signature: + +```go +func (q *ArrayQueue[T]) Back() T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

Get the number of elements in queue

+ +Signature: + +```go +func (q *ArrayQueue[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

Check if queue is empty or not

+ +Signature: + +```go +func (q *ArrayQueue[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

Check if queue is full or not

+ +Signature: + +```go +func (q *ArrayQueue[T]) IsFull() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](3) + fmt.Println(q.IsFull()) // false + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsFull()) // true +} +``` + + + +### Clear +

Clean all data in queue

+ +Signature: + +```go +func (q *ArrayQueue[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

Check if the value is in queue or not

+ +Signature: + +```go +func (q *ArrayQueue[T]) Contain(value T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewArrayQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + + +### 2. LinkedQueue +Common queue implemented by link. + +### NewLinkedQueue +

Return a LinkedQueue pointer

+ +Signature: + +```go +func NewLinkedQueue[T any]() *LinkedQueue[T] + +type LinkedQueue[T any] struct { + head *datastructure.QueueNode[T] + tail *datastructure.QueueNode[T] + length int +} +type QueueNode[T any] struct { + Value T + Next *QueueNode[T] +} +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int]() + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

Get all queue data

+ +Signature: + +```go +func (q *LinkedQueue[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int]() + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

Put element into queue

+ +Signature: + +```go +func (q *LinkedQueue[T]) Enqueue(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

Remove head element of queue and return it

+ +Signature: + +```go +func (q *LinkedQueue[T]) Dequeue() (T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Dequeue()) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

Just get the head element of queue

+ +Signature: + +```go +func (q *LinkedQueue[T]) Front() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

Just get the tail element of queue

+ +Signature: + +```go +func (q *LinkedQueue[T]) Back() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

Get the number of elements in queue

+ +Signature: + +```go +func (q *LinkedQueue[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

Check if queue is empty or not

+ +Signature: + +```go +func (q *LinkedQueue[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### Clear +

Clean all data in queue

+ +Signature: + +```go +func (q *LinkedQueue[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

Check if the value is in queue or not

+ +Signature: + +```go +func (q *LinkedQueue[T]) Contain(value T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewLinkedQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + + + +### 3. CircularQueue +Circular queue implemented by slice. + +### NewCircularQueue +

Return a CircularQueue pointer with specific capacity

+ +Signature: + +```go +func NewCircularQueue[T any](capacity int) *CircularQueue[T] + +type CircularQueue[T any] struct { + data []T + front int + rear int + capacity int +} +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

Get all queue data

+ +Signature: + +```go +func (q *CircularQueue[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

Put element into queue, if queue is full, return error

+ +Signature: + +```go +func (q *CircularQueue[T]) Enqueue(value T) error +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Dequeue +

Remove head element of queue and return it

+ +Signature: + +```go +func (q *CircularQueue[T]) Dequeue() (*T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + val := q.Dequeue() + fmt.Println(*val) // 1 + fmt.Println(q.Data()) // 2,3 +} +``` + + + + +### Front +

Just get head element of queue

+ +Signature: + +```go +func (q *CircularQueue[T]) Front() T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Front()) // 1 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + + +### Back +

Just get tail element of queue

+ +Signature: + +```go +func (q *CircularQueue[T]) Back() T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Back()) // 3 + fmt.Println(q.Data()) // 1,2,3 +} +``` + + + +### Size +

Get the number of elements in queue

+ +Signature: + +```go +func (q *CircularQueue[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Size()) // 3 +} +``` + + + +### IsEmpty +

Check if queue is empty or not

+ +Signature: + +```go +func (q *CircularQueue[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + fmt.Println(q.IsEmpty()) // true + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

Check if queue is full or not

+ +Signature: + +```go +func (q *CircularQueue[T]) IsFull() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](3) + fmt.Println(q.IsFull()) // false + + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.IsFull()) // true +} +``` + + + +### Clear +

Clean all data in queue

+ +Signature: + +```go +func (q *CircularQueue[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + q.Clear() + + fmt.Println(q.IsEmpty()) // true +} +``` + + + +### Contain +

Check if the value is in queue or not

+ +Signature: + +```go +func (q *CircularQueue[T]) Contain(value T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewCircularQueue[int](5) + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + fmt.Println(q.Contain(1)) // true + fmt.Println(q.Contain(4)) // false +} +``` + + +### 4. PriorityQueue +Common queue implemented by slice. + +### NewPriorityQueue +

Return a PriorityQueue pointer with specific capacity, param `comparator` is used to compare values of type T in the queue.

+ +Signature: + +```go +func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T] + +type PriorityQueue[T any] struct { + items []T + size int + comparator constraints.Comparator +} +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewPriorityQueue[int](3) + fmt.Println(q.Data()) // [] +} +``` + + + +### Data +

Get all queue data

+ +Signature: + +```go +func (q *PriorityQueue[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +func main() { + q := queue.NewPriorityQueue[int](3) + fmt.Println(q.Data()) // [] +} +``` + + + + +### Enqueue +

Put element into queue, if queue is full, return false

+ +Signature: + +```go +func (q *PriorityQueue[T]) Enqueue(item T) bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + + fmt.Println(q.Data()) // 10, 9, 6, 7, 8, 2, 5, 1, 4, 3 +} +``` + + + + +### Dequeue +

Remove head element of queue and return it

+ +Signature: + +```go +func (q *PriorityQueue[T]) Dequeue() (T, bool) +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + val, ok := pq.Dequeue() + fmt.Println(val) // 10 + fmt.Println(q.Data()) // 9, 8, 6, 7, 3, 2, 5, 1, 4 +} +``` + + + +### IsEmpty +

Check if queue is empty or not

+ +Signature: + +```go +func (q *PriorityQueue[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsEmpty()) // true + + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + fmt.Println(q.IsEmpty()) // false +} +``` + + + + +### IsFull +

Check if queue is full or not

+ +Signature: + +```go +func (q *PriorityQueue[T]) IsFull() bool +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsFull()) // false + + for i := 1; i < 11; i++ { + q.Enqueue(i) + } + fmt.Println(q.IsFull()) // true +} +``` + + + + +### Size +

Get nubmers of elements in queue

+ +Signature: + +```go +func (q *PriorityQueue[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + queue "github.com/duke-git/lancet/v2/datastructure/queue" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + comparator := &intComparator{} + q := queue.NewPriorityQueue[int](10, comparator) + fmt.Println(q.IsFull()) // false + + for i := 1; i < 5; i++ { + q.Enqueue(i) + } + fmt.Println(q.Size()) // 4 +} +``` + + diff --git a/docs/en/api/packages/datastructure/set.md b/docs/en/api/packages/datastructure/set.md new file mode 100644 index 00000000..e6115faf --- /dev/null +++ b/docs/en/api/packages/datastructure/set.md @@ -0,0 +1,710 @@ +# Set + +Set is a data container, like list, but elements of set is not duplicate. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go) + +
+ +## Usage + +```go +import ( + set "github.com/duke-git/lancet/v2/datastructure/set" +) +``` + +
+ +## Index + +- [New](#New) +- [FromSlice](#FromSlice) +- [Valuesdeprecated](#Values) +- [Add](#Add) +- [AddIfNotExist](#AddIfNotExist) +- [AddIfNotExistBy](#AddIfNotExistBy) +- [Delete](#Delete) +- [Contain](#Contain) +- [ContainAll](#ContainAll) +- [Clone](#Clone) +- [Size](#Size) +- [Equal](#Equal) +- [Iterate](#Iterate) +- [EachWithBreak](#EachWithBreak) +- [IsEmpty](#IsEmpty) +- [Union](#Union) +- [Intersection](#Intersection) +- [SymmetricDifference](#SymmetricDifference) +- [Minus](#Minus) +- [Pop](#Pop) +- [ToSlice](#ToSlice) +- [ToSortedSlice](#ToSortedSlice) + +
+ +## Documentation + +### New + +

Create a set instance

+ +Signature: + +```go +type Set[T comparable] map[T]struct{} +func New[T comparable](items ...T) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int](1,2,2,3) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### FromSlice + +

Create a set from slice

+ +Signature: + +```go +func FromSlice[T comparable](items []T) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.FromSlice([]int{1, 2, 2, 3}) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### Values + +

Return slice of all set data.

+ +> ⚠️ This function is deprecated. use `ToSlice` instead. + +Signature: + +```go +func (s Set[T]) Values() []T +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int](1,2,2,3) + fmt.Println(st.Values()) //1,2,3 +} +``` + +### Add + +

Add items to set

+ +Signature: + +```go +func (s Set[T]) Add(items ...T) +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + fmt.Println(st.Values()) //1,2,3 +} +``` + +### AddIfNotExist + +

AddIfNotExist checks if item exists in the set, it adds the item to set and returns true if it does not exist in the set, or else it does nothing and returns false.

+ +Signature: + +```go +func (s Set[T]) AddIfNotExist(item T) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + r1 := st.AddIfNotExist(1) + r2 := st.AddIfNotExist(4) + + fmt.Println(r1) // false + fmt.Println(r2) // true + fmt.Println(st.Values()) // 1,2,3,4 +} +``` + +### AddIfNotExistBy + +

AddIfNotExistBy checks if item exists in the set and pass the `checker` function it adds the item to set and returns true if it does not exists in the set and function `checker` returns true, or else it does nothing and returns false.

+ +Signature: + +```go +func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2) + + ok := st.AddIfNotExistBy(3, func(val int) bool { + return val%2 != 0 + }) + fmt.Println(ok) // true + + + notOk := st.AddIfNotExistBy(4, func(val int) bool { + return val%2 != 0 + }) + fmt.Println(notOk) // false + + fmt.Println(st.Values()) // 1, 2, 3 +} +``` + +### Delete + +

Delete item in set

+ +Signature: + +```go +func (s Set[T]) Delete(items ...T) +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + set.Delete(3) + fmt.Println(st.Values()) //1,2 +} +``` + +### Contain + +

Check if item is in set or not

+ +Signature: + +```go +func (s Set[T]) Contain(item T) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + st := set.New[int]() + st.Add(1, 2, 3) + + fmt.Println(st.Contain(1)) //true + fmt.Println(st.Contain(4)) //false +} +``` + +### ContainAll + +

Checks if set contains another set

+ +Signature: + +```go +func (s Set[T]) ContainAll(other Set[T]) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(1, 2) + set3 := set.New(1, 2, 3, 4) + + fmt.Println(set1.ContainAll(set2)) //true + fmt.Println(set1.ContainAll(set3)) //false +} +``` + +### Size + +

Get the number of elements in set

+ +Signature: + +```go +func (s Set[T]) Size() int +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + + fmt.Println(set1.Size()) //3 +} +``` + +### Clone + +

Make a copy of set

+ +Signature: + +```go +func (s Set[T]) Clone() Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set1.Clone() + + fmt.Println(set1.Size() == set2.Size()) //true + fmt.Println(set1.ContainAll(set2)) //true +} +``` + +### Equal + +

Check if two sets has same elements or not

+ +Signature: + +```go +func (s Set[T]) Equal(other Set[T]) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(1, 2, 3) + set3 := set.New(1, 2, 3, 4) + + fmt.Println(set1.Equal(set2)) //true + fmt.Println(set1.Equal(set3)) //false +} +``` + +### Iterate + +

Call function by every element of set

+ +Signature: + +```go +func (s Set[T]) Iterate(fn func(item T)) +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + arr := []int{} + set.Iterate(func(item int) { + arr = append(arr, item) + }) + + fmt.Println(arr) //1,2,3 +} +``` + +### EachWithBreak + +

Iterates over elements of a set and invokes function for each element, when iteratee return false, will break the for each loop.

+ +Signature: + +```go +func (s Set[T]) EachWithBreak(iteratee func(item T) bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + s := set.New(1, 2, 3, 4, 5) + + var sum int + + s.EachWithBreak(func(n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + fmt.Println(sum) //6 +} +``` + +### IsEmpty + +

Check if the set is empty or not

+ +Signature: + +```go +func (s Set[T]) IsEmpty() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New() + + fmt.Println(set1.IsEmpty()) //false + fmt.Println(set2.IsEmpty()) //true +} +``` + +### Union + +

Create a new set contain all element of set s and other

+ +Signature: + +```go +func (s Set[T]) Union(other Set[T]) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.Union(set2) + + fmt.Println(set3.Values()) //1,2,3,4,5 +} +``` + +### Intersection + +

Create a new set whose element both be contained in set s and other

+ +Signature: + +```go +func (s Set[T]) Intersection(other Set[T]) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.Intersection(set2) + + fmt.Println(set3.Values()) //2,3 +} +``` + +### SymmetricDifference + +

Create a new set whose element is in set1 or set2, but not in both set1 and set2

+ +Signature: + +```go +func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set1.SymmetricDifference(set2) + + fmt.Println(set3.Values()) //1,4,5 +} +``` + +### Minus + +

Create an set of whose element in origin set but not in compared set

+ +Signature: + +```go +func (s Set[T]) Minus(comparedSet Set[T]) Set[T] +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + set1 := set.New(1, 2, 3) + set2 := set.New(2, 3, 4, 5) + set3 := set.New(2, 3) + + res1 := set1.Minus(set2) + fmt.Println(res1.Values()) //1 + + res2 := set2.Minus(set3) + fmt.Println(res2.Values()) //4,5 +} +``` + +### Pop + +

Delete the top element of set then return it, if set is empty, return nil-value of T and false.

+ +Signature: + +```go +func (s Set[T]) Pop() (v T, ok bool) +``` + +Example: + +```go +package main + +import ( + "fmt" + set "github.com/duke-git/lancet/v2/datastructure/set" +) + +func main() { + s := set.New[int]() + s.Add(1) + s.Add(2) + s.Add(3) + + val, ok = s.Pop() + + fmt.Println(val) // 3 + fmt.Println(ok) // true +} +``` + +### ToSlice + +

returns a slice containing all values of the set.

+ +Signature: + +```go +func (s Set[T]) ToSlice() (v T, ok bool) +``` + +Example: + +```go +func main() { + s := set.New(1, 2, 3, 4, 5) + + val := s.ToSlice() + fmt.Println(val) // [2 3 4 5 1] +} +``` + +### ToSortedSlice + +

returns a sorted slice containing all values of the set

+ +Signature: + +```go +func (s Set[T]) ToSortedSlice() (v T, ok bool) +``` + +Example: + +```go +func main() { + s1 := set.New(1, 2, 3, 4, 5) + type Person struct { + Name string + Age int + } + s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}}) + + res1 := s1.ToSortedSlice(func(v1, v2 int) bool { + return v1 < v2 + }) + + res2 := s2.ToSortedSlice(func(v1, v2 Person) bool { + return v1.Age < v2.Age + }) + + fmt.Println(res1) // [1 2 3 4 5] + fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}] +} +``` diff --git a/docs/en/api/packages/datastructure/stack.md b/docs/en/api/packages/datastructure/stack.md new file mode 100644 index 00000000..d0455784 --- /dev/null +++ b/docs/en/api/packages/datastructure/stack.md @@ -0,0 +1,611 @@ +# Stack +Stack is an abstract data type that serves as a collection of elements. Elements follow the LIFO principle. FIFO is last-in, first-out, meaning that the most recently produced items are recorded as sold first. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go) +- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go) + + +
+ +## Usage +```go +import ( + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) +``` + +
+ +## Index + +### 1. ArrayStack + +- [NewArrayStack](#NewArrayStack) +- [Push](#ArrayStack_Push) +- [Pop](#ArrayStack_Pop) +- [Peak](#ArrayStack_Peak) +- [Data](#ArrayStack_Data) +- [Size](#ArrayStack_Size) +- [IsEmpty](#ArrayStack_IsEmpty) +- [Clear](#ArrayStack_Clear) + +### 2. LinkedStack + +- [NewLinkedStack](#NewLinkedStack) +- [Push](#LinkedStack_Push) +- [Pop](#LinkedStack_Pop) +- [Peak](#LinkedStack_Peak) +- [Data](#LinkedStack_Data) +- [Size](#LinkedStack_Size) +- [IsEmpty](#LinkedStack_IsEmpty) +- [Clear](#LinkedStack_Clear) +- [Print](#LinkedStack_Print) + +
+ +## Documentation + +### 1. ArrayStack +ArrayStack is a stack implemented by slice. + +### NewArrayStack +

Return a empty ArrayStack pointer

+ +Signature: + +```go +type ArrayStack[T any] struct { + data []T + length int +} +func NewArrayStack[T any]() *ArrayStack[T] +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + fmt.Println(sk) +} +``` + + + + +### Push +

Push element into array stack

+ +Signature: + +```go +func (s *ArrayStack[T]) Push(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Pop +

Delete the top element of stack then return it, if stack is empty, return nil and error

+ +Signature: + +```go +func (s *ArrayStack[T]) Pop() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Pop() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{2, 1} +} +``` + + + + +### Peak +

Return the top element of array stack

+ +Signature: + +```go +func (s *ArrayStack[T]) Peak() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Peak() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Data +

Return a slice of all data in array stack

+ +Signature: + +```go +func (s *ArrayStack[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Size +

Return number of elements in array stack

+ +Signature: + +```go +func (s *ArrayStack[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Size()) //3 +} +``` + + + + +### IsEmpty +

Check if array stack is empty or not

+ +Signature: + +```go +func (s *ArrayStack[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + fmt.Println(sk.IsEmpty()) //true + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.IsEmpty()) //false +} +``` + + + + +### Clear +

Clear all elments in array stack

+ +Signature: + +```go +func (s *ArrayStack[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewArrayStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + sk.Clear() + + fmt.Println(sk.Data()) //[]int{} +} +``` + + + +### 2. LinkedStack +LinkedStack is a stack implemented by linked list. + +### NewLinkedStack +

Return a empty LinkedStack pointer

+ +Signature: + +```go +type StackNode[T any] struct { + Value T + Next *StackNode[T] +} +type LinkedStack[T any] struct { + top *datastructure.StackNode[T] + length int +} +func NewLinkedStack[T any]() *LinkedStack[T] +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + fmt.Println(sk) +} +``` + + + + +### Push +

Push element into linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Push(value T) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Pop +

Delete the top element of stack then return it, if stack is empty, return nil and error

+ +Signature: + +```go +func (s *LinkedStack[T]) Pop() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Pop() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{2, 1} +} +``` + + + + +### Peak +

Return the top element of linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Peak() (*T, error) +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + val, err := sk.Peak() + fmt.Println(err) //nil + fmt.Println(*val) //3 + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Data +

Return a slice of all data in linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Data() []T +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Data()) //[]int{3, 2, 1} +} +``` + + + + +### Size +

Return number of elements in linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Size() int +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.Size()) //3 +} +``` + + + + +### IsEmpty +

Check if linked stack is empty or not

+ +Signature: + +```go +func (s *LinkedStack[T]) IsEmpty() bool +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + fmt.Println(sk.IsEmpty()) //true + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + fmt.Println(sk.IsEmpty()) //false +} +``` + + + + +### Clear +

Clear all elments in linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Clear() +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + sk.Clear() + + fmt.Println(sk.Data()) //[]int{} +} +``` + + + + +### Print +

Print the structure of a linked stack

+ +Signature: + +```go +func (s *LinkedStack[T]) Print() +``` +Example: + +```go +package main + +import ( + "fmt" + stack "github.com/duke-git/lancet/v2/datastructure/stack" +) + +func main() { + sk := stack.NewLinkedStack[int]() + + sk.Push(1) + sk.Push(2) + sk.Push(3) + + + sk.Print() //[ &{Value:3 Next:0xc000010260}, &{Value:2 Next:0xc000010250}, &{Value:1 Next:}, ] +} +``` diff --git a/docs/en/api/packages/datastructure/tree.md b/docs/en/api/packages/datastructure/tree.md new file mode 100644 index 00000000..38d57ae5 --- /dev/null +++ b/docs/en/api/packages/datastructure/tree.md @@ -0,0 +1,526 @@ +# Tree +Tree is a collection of tree nodes. Each tree node has a value, a left pointer point to left node and a right pointer point to right node. + +
+ +## Source + +- [https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go](https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go) + + +
+ +## Usage +```go +import ( + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) +``` + +
+ +## Index + +### 1. BSTree + +- [NewBSTree](#NewBSTree) +- [Insert](#BSTree_Insert) +- [Delete](#BSTree_Delete) +- [PreOrderTraverse](#BSTree_PreOrderTraverse) +- [InOrderTraverse](#BSTree_InOrderTraverse) +- [PostOrderTraverse](#BSTree_PostOrderTraverse) +- [LevelOrderTraverse](#BSTree_LevelOrderTraverse) +- [Depth](#BSTree_Depth) +- [HasSubTree](#BSTree_HasSubTree) +- [Print](#BSTree_Print) + + + +
+ +## Documentation + +## 1. BSTree +BSTree is a binary search tree data structure in which each node has at two children, which are referred to as the left child and the right child. In BSTree: leftNode < rootNode < rightNode. Type T should implements Compare function in constraints.Comparator interface. + +### NewBSTree +

Make a BSTree pointer instance

+ +Signature: + +```go +func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T] + +type BSTree[T any] struct { + root *datastructure.TreeNode[T] + comparator constraints.Comparator +} + +type TreeNode[T any] struct { + Value T + Left *TreeNode[T] + Right *TreeNode[T] +} +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + fmt.Println(bstree) // +} +``` + + + + +### Insert +

Insert value into binary search tree

+ +Signature: + +```go +func (t *BSTree[T]) Insert(data T) +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7 +} +``` + + + + +### Delete +

Delete value of binary search tree

+ +Signature: + +```go +func (t *BSTree[T]) Delete(data T) +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + bstree.Delete(4) + + fmt.Println(bstree.PreOrderTraverse()) //2, 5, 6, 7 +} +``` + + + + +### PreOrderTraverse +

Traverse tree nodes in pre order

+ +Signature: + +```go +func (t *BSTree[T]) PreOrderTraverse() []T +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7 +} +``` + + + + +### InOrderTraverse +

Traverse tree nodes in middle order

+ +Signature: + +```go +func (t *BSTree[T]) InOrderTraverse() []T +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.InOrderTraverse()) //2, 4, 5, 6, 7 +} +``` + + + + +### PostOrderTraverse +

Traverse tree nodes in post order

+ +Signature: + +```go +func (t *BSTree[T]) PostOrderTraverse() []T +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.PostOrderTraverse()) //5, 2, 4, 7, 6 +} +``` + + + + +### LevelOrderTraverse +

Traverse tree nodes in node level order

+ +Signature: + +```go +func (t *BSTree[T]) LevelOrderTraverse() []T +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.LevelOrderTraverse()) //6, 5, 7, 2, 4 +} +``` + + + + +### Depth +

Get the depth of a binary saerch tree

+ +Signature: + +```go +func (t *BSTree[T]) Depth() int +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.Depth()) //4 +} +``` + + + + +### HasSubTree +

Check if the given tree is sub tree of origin tree or not

+ +Signature: + +```go +func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + superTree := tree.NewBSTree(8, &intComparator{}) + superTree.Insert(4) + superTree.Insert(5) + superTree.Insert(6) + superTree.Insert(9) + superTree.Insert(4) + + subTree := tree.NewBSTree(5, &intComparator{}) + subTree.Insert(4) + subTree.Insert(6) + + fmt.Println(superTree.HasSubTree(subTree)) //true + fmt.Println(subTree.HasSubTree(superTree)) //false +} +``` + + + + +### Print +

Print the structure of binary saerch tree

+ +Signature: + +```go +func (t *BSTree[T]) Print() +``` +Example: + +```go +package main + +import ( + "fmt" + tree "github.com/duke-git/lancet/v2/datastructure/tree" +) + +type intComparator struct{} + +func (c *intComparator) Compare(v1, v2 any) int { + val1, _ := v1.(int) + val2, _ := v2.(int) + + if val1 < val2 { + return -1 + } else if val1 > val2 { + return 1 + } + return 0 +} + +func main() { + bstree := tree.NewBSTree(6, &intComparator{}) + bstree.Insert(7) + bstree.Insert(5) + bstree.Insert(2) + bstree.Insert(4) + + fmt.Println(bstree.Print()) +// 6 +// / \ +// / \ +// / \ +// / \ +// 5 7 +// / +// / +// 2 +// \ +// 4 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/datetime.md b/docs/en/api/packages/datetime.md new file mode 100644 index 00000000..e5fc707b --- /dev/null +++ b/docs/en/api/packages/datetime.md @@ -0,0 +1,1855 @@ +# Datetime + +Package datetime supports date and time format and compare. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/datetime/datetime.go](https://github.com/duke-git/lancet/blob/main/datetime/datetime.go) +- [https://github.com/duke-git/lancet/blob/main/datetime/conversion.go](https://github.com/duke-git/lancet/blob/main/datetime/conversion.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/datetime" +) +``` + +
+ +## 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) +- [BeginOfWeek](#BeginOfWeek) +- [BeginOfMonth](#BeginOfMonth) +- [BeginOfYear](#BeginOfYear) +- [EndOfMinute](#EndOfMinute) +- [EndOfHour](#EndOfHour) +- [EndOfDay](#EndOfDay) +- [EndOfWeek](#EndOfWeek) +- [EndOfMonth](#EndOfMonth) +- [EndOfYear](#EndOfYear) +- [GetNowDate](#GetNowDate) +- [GetNowTime](#GetNowTime) +- [GetNowDateTime](#GetNowDateTime) +- [GetTodayStartTime](#GetTodayStartTime) +- [GetTodayEndTime](#GetTodayEndTime) +- [GetZeroHourTimestamp](#GetZeroHourTimestamp) +- [GetNightTimestamp](#GetNightTimestamp) +- [FormatTimeToStr](#FormatTimeToStr) +- [FormatStrToTime](#FormatStrToTime) +- [NewUnixNow](#NewUnixNow) +- [NewUnix](#NewUnix) +- [NewFormat](#NewFormat) +- [NewISO8601](#NewISO8601) +- [ToUnix](#ToUnix) +- [ToFormat](#ToFormat) +- [ToFormatForTpl](#ToFormatForTpl) +- [ToIso8601](#ToIso8601) +- [IsLeapYear](#IsLeapYear) +- [BetweenSeconds](#BetweenSeconds) +- [DayOfYear](#DayOfYear) +- [IsWeekenddeprecated](#IsWeekend) +- [NowDateOrTime](#NowDateOrTime) +- [Timestamp](#Timestamp) +- [TimestampMilli](#TimestampMilli) +- [TimestampMicro](#TimestampMicro) +- [TimestampNano](#TimestampNano) +- [TrackFuncTime](#TrackFuncTime) +- [DaysBetween](#DaysBetween) +- [GenerateDatetimesBetween](#GenerateDatetimesBetween) +- [Min](#Min) +- [Max](#Max) +- [MaxMin](#MaxMin) + + +
+ +## Documentation + +## Note: + +1. In below functions, the `format` string param should be one of flows value (case no sensitive): + +- yyyy-mm-dd hh:mm:ss +- yyyy-mm-dd hh:mm +- yyyy-mm-dd hh +- yyyy-mm-dd +- yyyy-mm +- mm-dd +- dd-mm-yy hh:mm:ss +- yyyy/mm/dd hh:mm:ss +- yyyy/mm/dd hh:mm +- yyyy/mm/dd hh +- yyyy/mm/dd +- yyyy/mm +- mm/dd +- dd/mm/yy hh:mm:ss +- yyyymmdd +- mmddyy +- yyyy +- yy +- mm +- hh:mm:ss +- hh:mm +- mm:ss + +### AddDay + +

Add or sub days to time.

+ +Signature: + +```go +func AddDay(t time.Time, days int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/dIGbs_uTdFa) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after1Day := datetime.AddDay(date, 1) + before1Day := datetime.AddDay(date, -1) + + fmt.Println(after1Day.Format("2006-01-02 15:04:05")) + fmt.Println(before1Day.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-02 00:00:00 + // 2020-12-31 00:00:00 +} +``` + +### AddWeek + +

Add or sub weeks to time.

+ +Signature: + +```go +func AddWeek(t time.Time, weeks int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/M9TqdMiaA2p) + +```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 +} +``` + +### AddMonth + +

Add or sub months to time.

+ +Signature: + +```go +func AddMonth(t time.Time, months int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/DLoiOnpLvsN) + +```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 +} +``` + +### AddHour + +

Add or sub hours to time.

+ +Signature: + +```go +func AddHour(t time.Time, hour int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/rcMjd7OCsi5) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Hours := datetime.AddHour(date, 2) + before2Hours := datetime.AddHour(date, -2) + + fmt.Println(after2Hours.Format("2006-01-02 15:04:05")) + fmt.Println(before2Hours.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 02:00:00 + // 2020-12-31 22:00:00 +} +``` + +### AddMinute + +

Add or sub minutes to time.

+ +Signature: + +```go +func AddMinute(t time.Time, minute int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/nT1heB1KUUK) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02 15:04:05", "2021-01-01 00:00:00") + + after2Minutes := datetime.AddMinute(date, 2) + before2Minutes := datetime.AddMinute(date, -2) + + fmt.Println(after2Minutes.Format("2006-01-02 15:04:05")) + fmt.Println(before2Minutes.Format("2006-01-02 15:04:05")) + + // Output: + // 2021-01-01 00:02:00 + // 2020-12-31 23:58:00 +} +``` + +### AddYear + +

Add or sub year to the time.

+ +Signature: + +```go +func AddYear(t time.Time, year int64) time.Time +``` + +Example:[Run](https://go.dev/play/p/MqW2ujnBx10) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date, _ := time.Parse("2006-01-02", "2021-01-01") + + after2Years := AddYear(date, 2) + before2Years := AddYear(date, -2) + + fmt.Println(after2Years.Format("2006-01-02")) + fmt.Println(before2Years.Format("2006-01-02")) + + // Output: + // 2023-01-01 + // 2019-01-01 +} +``` + +### 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.

+ +Signature: + +```go +func AddDaySafe(t time.Time, days int) time.Time +``` + +Example:[Run](https://go.dev/play/p/JTohZFpoDJ3) + +```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 +} +``` + +### 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.

+ +Signature: + +```go +func AddMonthSafe(t time.Time, months int) time.Time +``` + +Example:[Run](https://go.dev/play/p/KLw0lo6mbVW) + +```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 +} +``` + +### 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.

+ +Signature: + +```go +func AddYearSafe(t time.Time, years int) time.Time +``` + +Example:[Run](https://go.dev/play/p/KVGXWZZ54ZH) + +```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 +} +``` + +### BeginOfMinute + +

Return beginning minute time of day.

+ +Signature: + +```go +func BeginOfMinute(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/ieOLVJ9CiFT) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:00 +0000 UTC +} +``` + +### BeginOfHour + +

Return zero time of day.

+ +Signature: + +```go +func BeginOfHour(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/GhdGFnDWpYs) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:00:00 +0000 UTC +} +``` + +### BeginOfDay + +

Return begin time of day.

+ +Signature: + +```go +func BeginOfDay(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/94m_UT6cWs9) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 00:00:00 +0000 UTC +} +``` + +### BeginOfWeek + +

Return beginning time of week, week begin from Sunday.

+ +Signature: + +```go +func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time +``` + +Example:[Run](https://go.dev/play/p/DCHdcL6gnfV) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + + result := datetime.BeginOfWeek(input, time.Monday) + + fmt.Println(result) + + // Output: + // 2023-01-02 00:00:00 +0000 UTC +} +``` + +### BeginOfMonth + +

Return beginning time of month

+ +Signature: + +```go +func BeginOfMonth(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/bWXVFsmmzwL) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} +``` + +### BeginOfYear + +

Return beginning time of year.

+ +Signature: + +```go +func BeginOfYear(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/i326DSwLnV8) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.BeginOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-01-01 00:00:00 +0000 UTC +} +``` + +### EndOfMinute + +

Return end time minute of day.

+ +Signature: + +```go +func EndOfMinute(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/yrL5wGzPj4z) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfMinute(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:50:59.999999999 +0000 UTC +} +``` + +### EndOfHour + +

Return end time hour of day.

+ +Signature: + +```go +func EndOfHour(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/6ce3j_6cVqN) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfHour(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 18:59:59.999999999 +0000 UTC +} +``` + +### EndOfDay + +

Return end time hour of day.

+ +Signature: + +```go +func EndOfDay(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/eMBOvmq5Ih1) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfDay(input) + + fmt.Println(result) + + // Output: + // 2023-01-08 23:59:59.999999999 +0000 UTC +} +``` + +### EndOfWeek + +

Return end time of week, week end with Saturday.

+ +Signature: + +```go +func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time +``` + +Example:[Run](https://go.dev/play/p/mGSA162YgX9) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfWeek(input, time.Sunday) + + fmt.Println(result) + + // Output: + // 2023-01-08 23:59:59.999999999 +0000 UTC +} +``` + +### EndOfMonth + +

Return end time of month

+ +Signature: + +```go +func EndOfMonth(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/_GWh10B3Nqi) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfMonth(input) + + fmt.Println(result) + + // Output: + // 2023-01-31 23:59:59.999999999 +0000 UTC +} +``` + +### EndOfYear + +

Return beginning time of year.

+ +Signature: + +```go +func EndOfYear(t time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/G01cKlMCvNm) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC) + result := datetime.EndOfYear(input) + + fmt.Println(result) + + // Output: + // 2023-12-31 23:59:59.999999999 +0000 UTC +} +``` + +### GetNowDate + +

Get current date string, format is yyyy-mm-dd.

+ +Signature: + +```go +func GetNowDate() string +``` + +Example:[Run](https://go.dev/play/p/PvfkPpcpBBf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + currentDate := datetime.GetNowDate() + fmt.Println(currentDate) + + // Output: + // 2022-01-28 +} +``` + +### GetNowTime + +

Get current time string, format is hh:mm:ss.

+ +Signature: + +```go +func GetNowTime() string +``` + +Example:[Run](https://go.dev/play/p/l7BNxCkTmJS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + currentTime := datetime.GetNowTime() + fmt.Println(currentTime) // 15:57:33 + + // Output: + // 15:57:33 +} +``` + +### GetNowDateTime + +

Get current date time string, format is yyyy-mm-dd hh:mm:ss.

+ +Signature: + +```go +func GetNowDateTime() string +``` + +Example:[Run](https://go.dev/play/p/pI4AqngD0al) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + current := datetime.GetNowDateTime() + fmt.Println(current) + + // Output: + // 2022-01-28 15:59:33 +} +``` + +### GetTodayStartTime + +

Return the start time of today, format: yyyy-mm-dd 00:00:00.

+ +Signature: + +```go +func GetTodayStartTime() string +``` + +Example:[Run](https://go.dev/play/p/84siyYF7t99) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + startTime := datetime.GetTodayStartTime() + fmt.Println(startTime) + + // Output: + // 2023-06-29 00:00:00 +} +``` + +### GetTodayEndTime + +

Return the end time of today, format: yyyy-mm-dd 23:59:59.

+ +Signature: + +```go +func GetTodayEndTime() string +``` + +Example:[Run](https://go.dev/play/p/jjrLnfoqgn3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + endTime := datetime.GetTodayEndTime() + fmt.Println(endTime) + + // Output: + // 2023-06-29 23:59:59 +} +``` + +### GetZeroHourTimestamp + +

Return timestamp of zero hour (timestamp of 00:00).

+ +Signature: + +```go +func GetZeroHourTimestamp() int64 +``` + +Example:[Run](https://go.dev/play/p/QmL2oIaGE3q) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + zeroTime := datetime.GetZeroHourTimestamp() + fmt.Println(zeroTime) + + // Output: + // 1643299200 +} +``` + +### GetNightTimestamp + +

Return timestamp of zero hour (timestamp of 23:59).

+ +Signature: + +```go +func GetNightTimestamp() int64 +``` + +Example:[Run](https://go.dev/play/p/UolysR3MYP1) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + nightTime := datetime.GetNightTimestamp() + fmt.Println(nightTime) + + // Output: + // 1643385599 +} +``` + +### FormatTimeToStr + +

Format time to string, `format` param specification see note 1.

+ +Signature: + +```go +func FormatTimeToStr(t time.Time, format string, timezone ...string) string +``` + +Example:[Run](https://go.dev/play/p/_Ia7M8H_OvE) + +```go +package main + +import ( + "fmt" + "time" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + t, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") + + result1 := datetime.FormatTimeToStr(t, "yyyy-mm-dd hh:mm:ss") + result2 := datetime.FormatTimeToStr(t, "yyyy-mm-dd") + result3 := datetime.FormatTimeToStr(t, "dd-mm-yy hh:mm:ss") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2021-01-02 16:04:08 + // 2021-01-02 + // 02-01-21 16:04:08 +} +``` + +### FormatStrToTime + +

Format string to time, `format` param specification see note 1.

+ +Signature: + +```go +func FormatStrToTime(str, format string, timezone ...string) (time.Time, error) +``` + +Example:[Run](https://go.dev/play/p/1h9FwdU8ql4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1, _ := datetime.FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss") + result2, _ := datetime.FormatStrToTime("2021-01-02", "yyyy-mm-dd") + result3, _ := datetime.FormatStrToTime("02-01-21 16:04:08", "dd-mm-yy hh:mm:ss") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 2021-01-02 16:04:08 +0000 UTC + // 2021-01-02 00:00:00 +0000 UTC + // 2021-01-02 16:04:08 +0000 UTC +} +``` + +### NewUnixNow + +

Return unix timestamp of current time

+ +Signature: + +```go +type theTime struct { + unix int64 +} +func NewUnixNow() *theTime +``` + +Example:[Run](https://go.dev/play/p/U4PPx-9D0oz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnixNow() + fmt.Println(tm) + + // Output: + // &{1647597438} +} +``` + +### NewUnix + +

Return unix timestamp of specified int64 value.

+ +Signature: + +```go +type theTime struct { + unix int64 +} +func NewUnix(unix int64) *theTime +``` + +Example:[Run](https://go.dev/play/p/psoSuh_kLRt) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnix(1647597438) + fmt.Println(tm) + + // Output: + // &{1647597438} +} +``` + +### NewFormat + +

Return unix timestamp of specified time string, t should be "yyyy-mm-dd hh:mm:ss".

+ +Signature: + +```go +type theTime struct { + unix int64 +} +func NewFormat(t string) (*theTime, error) +``` + +Example:[Run](https://go.dev/play/p/VkW08ZOaXPZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, err := datetime.NewFormat("2022-03-18 17:04:05") + fmt.Println(tm) + + // Output: + // &{1647594245} +} +``` + +### NewISO8601 + +

Return unix timestamp of specified iso8601 time string.

+ +Signature: + +```go +type theTime struct { + unix int64 +} +func NewISO8601(iso8601 string) (*theTime, error) +``` + +Example:[Run](https://go.dev/play/p/mkhOHQkdeA2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, err := datetime.NewISO8601("2006-01-02T15:04:05.999Z") + fmt.Println(tm) + + // Output: + // &{1136214245} +} +``` + +### ToUnix + +

Return unix timestamp.

+ +Signature: + +```go +func (t *theTime) ToUnix() int64 +``` + +Example:[Run](https://go.dev/play/p/_LUiwAdocjy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm := datetime.NewUnixNow() + fmt.Println(tm.ToUnix()) + + // Output: + // 1647597438 +} +``` + +### ToFormat + +

Return time string 'yyyy-mm-dd hh:mm:ss'.

+ +Signature: + +```go +func (t *theTime) ToFormat() string +``` + +Example:[Run](https://go.dev/play/p/VkW08ZOaXPZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewFormat("2022-03-18 17:04:05") + fmt.Println(tm.ToFormat()) + + // Output: + // 2022-03-18 17:04:05 +} +``` + +### ToFormatForTpl + +

Return the time string which format is specified tpl.

+ +Signature: + +```go +func (t *theTime) ToFormatForTpl(tpl string) string +``` + +Example:[Run](https://go.dev/play/p/nyXxXcQJ8L5) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewFormat("2022-03-18 17:04:05") + ts := tm.ToFormatForTpl("2006/01/02 15:04:05") + fmt.Println(ts) + + // Output: + // 2022/03/18 17:04:05 +} +``` + +### ToIso8601 + +

Return iso8601 time string.

+ +Signature: + +```go +func (t *theTime) ToIso8601() string +``` + +Example:[Run](https://go.dev/play/p/mkhOHQkdeA2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + tm, _ := datetime.NewISO8601("2006-01-02T15:04:05.999Z") + ts := tm.ToIso8601() + fmt.Println(ts) + + // Output: + // 2006-01-02T23:04:05+08:00 +} +``` + +### IsLeapYear + +

check if param `year` is leap year or not.

+ +Signature: + +```go +func IsLeapYear(year int) bool +``` + +Example:[Run](https://go.dev/play/p/xS1eS2ejGew) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1 := datetime.IsLeapYear(2000) + result2 := datetime.IsLeapYear(2001) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### BetweenSeconds + +

Return the number of seconds between two times.

+ +Signature: + +```go +func BetweenSeconds(t1 time.Time, t2 time.Time) int64 +``` + +Example:[Run](https://go.dev/play/p/n3YDRyfyXJu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + today := time.Now() + tomorrow := datetime.AddDay(today, 1) + yesterday := datetime.AddDay(today, -1) + + result1 := datetime.BetweenSeconds(today, tomorrow) + result2 := datetime.BetweenSeconds(today, yesterday) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 86400 + // -86400 +} +``` + +### DayOfYear + +

Returns which day of the year the parameter date `t` is.

+ +Signature: + +```go +func DayOfYear(t time.Time) int +``` + +Example:[Run](https://go.dev/play/p/0hjqhTwFNlH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local) + result1 := datetime.DayOfYear(date1) + + date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local) + result2 := datetime.DayOfYear(date2) + + date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local) + result3 := datetime.DayOfYear(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 31 + // 1 + // 0 +} +``` + +### IsWeekend(Deprecated, Use '== Weekday' instead) + +

Checks if passed time is weekend or not.

+ +Signature: + +```go +func IsWeekend(t time.Time) bool +``` + +Example:[Run](https://go.dev/play/p/cupRM5aZOIY) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + date1 := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local) + date2 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local) + date3 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local) + + result1 := datetime.IsWeekend(date1) + result2 := datetime.IsWeekend(date2) + result3 := datetime.IsWeekend(date3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### NowDateOrTime + +

Return current datetime with specific format and timezone.

+ +Signature: + +```go +func NowDateOrTime(format string, timezone ...string) string +``` + +Example:[Run](https://go.dev/play/p/EZ-begEjtT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss") + + result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 2023-07-26 15:01:30 + // 2023-07-26 02:01:30 +} +``` + +### Timestamp + +

Return current second timestamp.

+ +Signature: + +```go +func Timestamp(timezone ...string) int64 +``` + +Example:[Run](https://go.dev/play/p/iU5b7Vvjx6x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.Timestamp() + + fmt.Println(ts) + + // Output: + // 1690363051 +} +``` + +### TimestampMilli + +

Return current mill second timestamp.

+ +Signature: + +```go +func TimestampMilli(timezone ...string) int64 +``` + +Example:[Run](https://go.dev/play/p/4gvEusOTu1T) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampMilli() + + fmt.Println(ts) + + // Output: + // 1690363051331 +} +``` + +### TimestampMicro + +

Return current micro second timestamp.

+ +Signature: + +```go +func TimestampMicro(timezone ...string) int64 +``` + +Example:[Run](https://go.dev/play/p/2maANglKHQE) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampMicro() + + fmt.Println(ts) + + // Output: + // 1690363051331784 +} +``` + +### TimestampNano + +

Return current nano second timestamp.

+ +Signature: + +```go +func TimestampNano(timezone ...string) int64 +``` + +Example:[Run](https://go.dev/play/p/A9Oq_COrcCF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + ts := datetime.TimestampNano() + + fmt.Println(ts) + + // Output: + // 1690363051331788000 +} +``` + +### TrackFuncTime + +

Tracks function execution time.

+ +Signature: + +```go +func TrackFuncTime(pre time.Time) func() +``` + +Example:[Run](https://go.dev/play/p/QBSEdfXHPTp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + defer datetime.TrackFuncTime(time.Now())() + + var n int + for i := 0; i < 5000000; i++ { + n++ + } + + fmt.Println(1) // Function main execution time: 1.460287ms +} +``` + +### DaysBetween + +

Returns the number of days between two times.

+ +Signature: + +```go +func DaysBetween(start, end time.Time) int +``` + +Example:[Run](https://go.dev/play/p/qD6qGb3TbOy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC) + + result := datetime.DaysBetween(start, end) + + fmt.Println(result) + + // Output: + // 9 +} +``` + +### GenerateDatetimesBetween + +

Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.

+ +Signature: + +```go +func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) +``` + +Example:[Run](https://go.dev/play/p/6kHBpAxD9ZC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC) + + layout := "2006-01-02 15:04:05" + interval := "1h" + + result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval) + + fmt.Println(result) + fmt.Println(err) + + // Output: + // [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00] + // +} +``` + +### Min + +

Returns the earliest time among the given times.

+ +Signature: + +```go +func Min(t1 time.Time, times ...time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/MCIDvHNOGGb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + minTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(minTime) + + // Output: + // 2024-09-01 00:00:00 +0000 UTC +} +``` + +### Max + +

Returns the latest time among the given times.

+ +Signature: + +```go +func Max(t1 time.Time, times ...time.Time) time.Time +``` + +Example:[Run](https://go.dev/play/p/9m6JMk1LB7-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + maxTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC)) + + fmt.Println(maxTime) + + // Output: + // 2024-09-02 00:00:00 +0000 UTC +} +``` + +### MaxMin + +

Returns the latest and earliest time among the given times.

+ +Signature: + +```go +func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) +``` + +Example:[Run](https://go.dev/play/p/rbW51cDtM_2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/datetime" +) + +func main() { + max, min := datetime.MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC)) + + fmt.Println(max) + fmt.Println(min) + + // Output: + // 2024-09-03 00:00:00 +0000 UTC + // 2024-09-01 00:00:00 +0000 UTC +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/eventbus.md b/docs/en/api/packages/eventbus.md new file mode 100644 index 00000000..17427662 --- /dev/null +++ b/docs/en/api/packages/eventbus.md @@ -0,0 +1,407 @@ +# EventBus + +EventBus is an event bus used for handling events within an application. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go](https://github.com/duke-git/lancet/blob/main/eventbus/eventbus.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/eventbus" +) +``` + +
+ +## Index + +- [NewEventBus](#NewEventBus) +- [Subscribe](#Subscribe) +- [Unsubscribe](#Unsubscribe) +- [Publish](#Publish) +- [ClearListeners](#ClearListeners) +- [ClearListenersByTopic](#ClearListenersByTopic) +- [GetListenersCount](#GetListenersCount) +- [GetAllListenersCount](#GetAllListenersCount) +- [GetEvents](#GetEvents) +- [SetErrorHandler](#SetErrorHandler) + + +
+ +## Documentation + +### NewEventBus + +

Create an EventBus instance.

+ +Signature: + +```go +func NewEventBus[T any]() *EventBus[T] +``` + +Example:[Run](https://go.dev/play/p/gHbOPV_NUOJ) + +```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 +} +``` + +### Subscribe + +

Subscribes to an event with a specific event topic and listener function.

+ +Signature: + +```go +func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool) +``` + +Example:[Run](https://go.dev/play/p/EYGf_8cHei-) + +```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 +} +``` + +### Unsubscribe + +

Unsubscribes from an event with a specific event topic and listener function.

+ +Signature: + +```go +func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T)) +``` + +Example:[Run](https://go.dev/play/p/Tmh7Ttfvprf) + +```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 +} +``` + +### Publish + +

Publishes an event with a specific event topic and data payload.

+ +Signature: + +```go +func (eb *EventBus[T]) Publish(event eventbus.Event[T]) +``` + +Example:[Run](https://go.dev/play/p/gHTtVexFSH9) + +```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 +} +``` + +### ClearListeners + +

Clears all the listeners.

+ +Signature: + +```go +func (eb *EventBus[T]) ClearListeners() +``` + +Example:[Run](https://go.dev/play/p/KBfBYlKPgqD) + +```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 +} +``` + +### ClearListenersByTopic + +

Clears all the listeners by topic.

+ +Signature: + +```go +func (eb *EventBus[T]) ClearListenersByTopic(topic string) +``` + +Example:[Run](https://go.dev/play/p/gvMljmJOZmU) + +```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 +} +``` + +### GetListenersCount + +

Returns the number of listeners for a specific event topic.

+ +Signature: + +```go +func (eb *EventBus[T]) GetListenersCount(topic string) int +``` + +Example:[Run](https://go.dev/play/p/j6yaJ2xAmFz) + +```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 +} +``` + +### GetAllListenersCount + +

Returns the total number of all listeners.

+ +Signature: + +```go +func (eb *EventBus[T]) GetAllListenersCount() int +``` + +Example:[Run](https://go.dev/play/p/PUlr0xcpEOz) + +```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 +} +``` + +### GetEvents + +

Returns all the events topics.

+ +Signature: + +```go +func (eb *EventBus[T]) GetEvents() []string +``` + +Example:[Run](https://go.dev/play/p/etgjjcOtAjX) + +```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 +} +``` + +### SetErrorHandler + +

Sets the error handler function.

+ +Signature: + +```go +func (eb *EventBus[T]) SetErrorHandler(handler func(err error)) +``` + +Example:[Run](https://go.dev/play/p/gmB0gnFe5mc) + +```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 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/fileutil.md b/docs/en/api/packages/fileutil.md new file mode 100644 index 00000000..a236846a --- /dev/null +++ b/docs/en/api/packages/fileutil.md @@ -0,0 +1,1147 @@ +# Fileutil + +Package fileutil implements some basic functions for file operations. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/fileutil/file.go](https://github.com/duke-git/lancet/blob/main/fileutil/file.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/fileutil" +) +``` + +
+ +## Index + +- [ClearFile](#ClearFile) +- [CreateFile](#CreateFile) +- [CreateDir](#CreateDir) +- [CopyFile](#CopyFile) +- [CopyDir](#CopyDir) +- [CurrentPath](#CurrentPath) +- [FileMode](#FileMode) +- [MiMeType](#MiMeType) +- [IsExist](#IsExist) +- [IsLink](#IsLink) +- [IsDir](#IsDir) +- [ListFileNames](#ListFileNames) +- [RemoveFile](#RemoveFile) +- [RemoveDir](#RemoveDir) +- [ReadFileToString](#ReadFileToString) +- [ReadFileByLine](#ReadFileByLine) +- [Zip](#Zip) +- [ZipAppendEntry](#ZipAppendEntry) +- [UnZip](#UnZip) +- [IsZipFile](#IsZipFile) +- [FileSize](#FileSize) +- [MTime](#MTime) +- [Sha](#Sha) +- [ReadCsvFile](#ReadCsvFile) +- [WriteCsvFile](#WriteCsvFile) +- [WriteMapsToCsv](#WriteMapsToCsv) +- [WriteStringToFile](#WriteStringToFile) +- [WriteBytesToFile](#WriteBytesToFile) +- [ReadFile](#ReadFile) +- [ChunkRead](#ChunkRead) +- [ParallelChunkRead](#ParallelChunkRead) +- [GetExeOrDllVersion](#GetExeOrDllVersion) + +
+ +## Documentation + +### ClearFile + +

Clear the file content, write empty string to the file.

+ +Signature: + +```go +func ClearFile(path string) error +``` + +Example:[Run](https://go.dev/play/p/NRZ0ZT-G94H) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.ClearFile("./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### CreateFile + +

Create file in path. return true if create succeed.

+ +Signature: + +```go +func CreateFile(path string) bool +``` + +Example:[Run](https://go.dev/play/p/lDt8PEsTNKI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isCreatedSucceed := fileutil.CreateFile("./test.txt") + fmt.Println(isCreatedSucceed) +} +``` + +### CreateDir + +

Create directory in absolute path. param `absPath` like /a, /a/b.

+ +Signature: + +```go +func CreateDir(absPath string) error +``` + +Example:[Run](https://go.dev/play/p/qUuCe1OGQnM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CreateDir("/a/b") // will create folder /a/b + fmt.Println(err) +} +``` + +### CopyFile + +

Copy src file to dest file. If dest file exist will overwrite it.

+ +Signature: + +```go +func CopyFile(srcPath string, dstPath string) error +``` + +Example:[Run](https://go.dev/play/p/Jg9AMJMLrJi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CopyFile("./test.txt", "./test_copy.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### CopyDir + +

Copy src directory to dst directory, it will copy all files and directories recursively. the access permission will be the same as the source directory. if dstPath exists, it will return an error.

+ +Signature: + +```go +func CopyDir(srcPath string, dstPath string) error +``` + +Example:[Run](https://go.dev/play/p/YAyFTA_UuPb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.CopyFile("./test_src", "./test_dest") + if err != nil { + fmt.Println(err) + } +} +``` + +### CurrentPath + +

return current absolute path.

+ +Signature: + +```go +func CurrentPath() string +``` + +Example:[Run](https://go.dev/play/p/s74a9iBGcSw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + absPath := CurrentPath() + fmt.Println(absPath) +} +``` + +### FileMode + +

Return file mode infomation.

+ +Signature: + +```go +func FileMode(path string) (fs.FileMode, error) +``` + +Example:[Run](https://go.dev/play/p/2l2hI42fA3p) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + mode, err := fileutil.FileMode("./test.txt") + if err != nil { + fmt.Println(err) + } + fmt.Println(mode) +} +``` + +### MiMeType + +

Get file mime type, 'file' param's type should be string or *os.File.

+ +Signature: + +```go +func MiMeType(file any) string +``` + +Example:[Run](https://go.dev/play/p/bd5sevSUZNu) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + type1 := fileutil.MiMeType("./test.txt") + fmt.Println(type1) //text/plain; charset=utf-8 + + f, _ := os.Open("./file.go") + type2 := fileutil.MiMeType(f) + fmt.Println(type2) //text/plain; charset=utf-8 +} +``` + +### IsExist + +

Checks if a file or directory exists.

+ +Signature: + +```go +func IsExist(path string) bool +``` + +Example:[Run](https://go.dev/play/p/nKKXt8ZQbmh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fileutil.CreateFile("./test.txt") + isFileExist := fileutil.IsExist("./test.txt") + fmt.Println(isFileExist) //true +} +``` + +### IsLink + +

Checks if a file is symbol link or not.

+ +Signature: + +```go +func IsLink(path string) bool +``` + +Example:[Run](https://go.dev/play/p/TL-b-Kzvf44) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isLinkFile := fileutil.IsLink("./test.txt") + fmt.Println(isLinkFile) //false +} +``` + +### IsDir + +

Checks if the path is directy or not.

+ +Signature: + +```go +func IsDir(path string) bool +``` + +Example:[Run](https://go.dev/play/p/WkVwEKqtOWk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isDir := fileutil.IsDir("./") + fmt.Println(isDir) //true + + isDir = fileutil.IsDir("./test.txt") + fmt.Println(isDir) //false +} +``` + +### ListFileNames + +

List all file names in given path.

+ +Signature: + +```go +func ListFileNames(path string) ([]string, error) +``` + +Example:[Run](https://go.dev/play/p/Tjd7Y07rejl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fileNames, _ := fileutil.ListFileNames("./") + fmt.Println(fileNames) +} +``` + +### RemoveFile + +

Remove the file of path.

+ +Signature: + +```go +func RemoveFile(path string, onDelete ...func(path string)) error +``` + +Example:[Run](https://go.dev/play/p/P2y0XW8a1SH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.RemoveFile("./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### RemoveDir + +

Delete directory.

+ +Signature: + +```go +func RemoveDir(path string, onDelete ...func(path string)) error +``` + +Example:[Run](https://go.dev/play/p/Oa6KnPek2uy) + +```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) +} +``` + +### ReadFileToString + +

Return string of file content.

+ +Signature: + +```go +func ReadFileToString(path string) (string, error) +``` + +Example:[Run](https://go.dev/play/p/cmfwp_5SQTp) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + path := "./test.txt" + fileutil.CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + f.WriteString("hello world") + + content, _ := fileutil.ReadFileToString(path) + fmt.Println(content) //hello world +} +``` + +### ReadFileByLine + +

Read file line by line, and return slice of lines

+ +Signature: + +```go +func ReadFileByLine(path string)([]string, error) +``` + +Example:[Run](https://go.dev/play/p/svJP_7ZrBrD) + +```go +package main + +import ( + "fmt" + "os" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + path := "./text.txt" + fileutil.CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + f.WriteString("hello\nworld") + + contents, _ := fileutil.ReadFileByLine(path) + fmt.Println(contents) //[]string{"hello", "world"} +} +``` + +### Zip + +

Create a zip file of fpath, fpath could be a file or a directory.

+ +Signature: + +```go +func Zip(fpath string, destPath string) error +``` + +Example:[Run](https://go.dev/play/p/j-3sWBp8ik_P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.Zip("./test.txt", "./test.zip") + if err != nil { + fmt.Println(err) + } +} +``` + +### ZipAppendEntry + +

Append a single file or directory by fpath to an existing zip file.

+ +Signature: + +```go +func ZipAppendEntry(fpath string, destPath string) error +``` + +Example:[Run](https://go.dev/play/p/cxvaT8TRNQp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.ZipAppendEntry("./test.txt", "./test.zip") + if err != nil { + fmt.Println(err) + } +} +``` + +### UnZip + +

Unzip the file and save it to dest path.

+ +Signature: + +```go +func UnZip(zipFile string, destPath string) error +``` + +Example:[Run](https://go.dev/play/p/g0w34kS7B8m) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + err := fileutil.UnZip("./test.zip", "./test.txt") + if err != nil { + fmt.Println(err) + } +} +``` + +### IsZipFile + +

Checks if file is zip file or not.

+ +Signature: + +```go +func IsZipFile(filepath string) bool +``` + +Example:[Run](https://go.dev/play/p/9M0g2j_uF_e) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + isZip := fileutil.IsZipFile("./zipfile.zip") + fmt.Println(isZip) +} +``` + +### FileSize + +

Returns file size in bytes.

+ +Signature: + +```go +func FileSize(path string) (int64, error) +``` + +Example:[Run](https://go.dev/play/p/H9Z05uD-Jjc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + size, err := fileutil.FileSize("./testdata/test.txt") + + fmt.Println(size) + fmt.Println(err) + + // Output: + // 20 + // +} +``` + +### MTime + +

Returns file modified time(unix timestamp).

+ +Signature: + +```go +func MTime(filepath string) (int64, error) +``` + +Example:[Run](https://go.dev/play/p/s_Tl7lZoAaY) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + mtime, err := fileutil.MTime("./testdata/test.txt") + + fmt.Println(mtime) + fmt.Println(err) + + // Output: + // 1682391110 + // +} +``` + +### Sha + +

returns file sha value, param `shaType` should be 1, 256 or 512.

+ +Signature: + +```go +func Sha(filepath string, shaType ...int) (string, error) +``` + +Example:[Run](https://go.dev/play/p/VfEEcO2MJYf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + sha1, err := fileutil.Sha("./testdata/test.txt", 1) + sha256, _ := fileutil.Sha("./testdata/test.txt", 256) + sha512, _ := fileutil.Sha("./testdata/test.txt", 512) + + fmt.Println(sha1) + fmt.Println(sha256) + fmt.Println(sha512) + fmt.Println(err) + + // Output: + // dda3cf10c5a6ff6c6659a497bf7261b287af2bc7 + // aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35 + // d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870 + // +} +``` + +### ReadCsvFile + +

Reads file content into slice.

+ +Signature: + +```go +func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) +``` + +Example:[Run](https://go.dev/play/p/OExTkhGEd3_u) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + content, err := fileutil.ReadCsvFile("./testdata/test.csv") + + fmt.Println(content) + fmt.Println(err) + + // Output: + // [[Bob 12 male] [Duke 14 male] [Lucy 16 female]] + // +} +``` + +### WriteCsvFile + +

Write content to target csv file.

+ +Signature: + +```go +func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error +``` + +Example:[Run](https://go.dev/play/p/dAXm58Q5U1o) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fpath := "./test.csv" + fileutil.CreateFile(fpath) + + f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + data := [][]string{ + {"Lili", "22", "female"}, + {"Jim", "21", "male"}, + } + err := fileutil.WriteCsvFile(fpath, data, false) + + if err != nil { + return + } + + content, err := fileutil.ReadCsvFile(fpath) + + if err != nil { + return + } + fmt.Println(content) + + // Output: + // [[Lili 22 female] [Jim 21 male]] +} +``` + +### WriteMapsToCsv + +

Write slice of map to csv file.

+ +Signature: + +```go +// filepath: path of the CSV file. +// records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file. +// appendToExistingFile: If true, data will be appended to the file if it exists. +// delimiter: Delimiter to use in the CSV file. +// headers: order of the csv column headers, needs to be consistent with the key of the map. +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error +``` + +Example:[运行](https://go.dev/play/p/umAIomZFV1c) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + fpath := "./test.csv" + fileutil.CreateFile(fpath) + + f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + records := []map[string]any{ + {"Name": "Lili", "Age": "22", "Gender": "female"}, + {"Name": "Jim", "Age": "21", "Gender": "male"}, + } + + headers := []string{"Name", "Age", "Gender"} + err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers) + + if err != nil { + log.Fatal(err) + } + + content, err := fileutil.ReadCsvFile(csvFilePath, ';') + + fmt.Println(content) + + // Output: + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] +} +``` + +### WriteBytesToFile + +

Writes bytes to target file.

+ +Signature: + +```go +func WriteBytesToFile(filepath string, content []byte) error +``` + +Example:[Run](https://go.dev/play/p/s7QlDxMj3P8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + filepath := "./bytes.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = fileutil.WriteBytesToFile(filepath, []byte("hello")) + if err != nil { + return + } + + content, err := fileutil.ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} +``` + +### WriteStringToFile + +

Writes string to target file.

+ +Signature: + +```go +func WriteStringToFile(filepath string, content string, append bool) error +``` + +Example:[Run](https://go.dev/play/p/GhLS6d8lH_g) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + filepath := "./test.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = fileutil.WriteStringToFile(filepath, "hello", true) + if err != nil { + return + } + + content, err := fileutil.ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} +``` + +### ReadFile + +

Read File or URL.

+ +Signature: + +```go +func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) +``` + +Example:[Run](https://go.dev/play/p/uNep3Tr8fqF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + reader, fn, err := fileutil.ReadFile("https://httpbin.org/robots.txt") + if err != nil { + return + } + defer fn() + + dat, err := io.ReadAll(reader) + if err != nil { + return + } + fmt.Println(string(dat)) + // Output: + // User-agent: * + // Disallow: /deny +} +``` + +### ChunkRead + +

reads a block from the file at the specified offset and returns all lines within the block.

+ +Signature : + +```go +func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) +``` + +Example:[Run](https://go.dev/play/p/r0hPmKWhsgf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 + + // test1.csv file content: + // Lili,22,female + // Jim,21,male + filePath := "./testdata/test1.csv" + f, err := os.Open(filePath) + if err != nil { + return + } + + defer f.Close() + + var bufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, defaultChunkSizeMB*mb) + }, + } + + lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool) + if err != nil { + return + } + + fmt.Println(lines[0]) + fmt.Println(lines[1]) + + // Output: + // Lili,22,female + // Jim,21,male +} +``` + +### ParallelChunkRead + +

Reads the file in parallel and send each chunk of lines to the specified channel.

+ +Signature : + +```go +// filePath: file path. +// chunkSizeMB: The size of the block (in MB, the default is 100MB when set to 0). Setting it too large will be detrimental. Adjust it as appropriate. +// maxGoroutine: The number of concurrent read chunks, the number of CPU cores used when set to 0. +// linesCh: The channel used to receive the returned results. +func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error +``` + +Example:[Run](https://go.dev/play/p/teMXnCsdSEw) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 // 默认值 + + numParsers := runtime.NumCPU() + + linesCh := make(chan []string, numParsers) + + // test1.csv file content: + // Lili,22,female + // Jim,21,male + filePath := "./testdata/test1.csv" + + go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers) + + var totalLines int + for lines := range linesCh { + totalLines += len(lines) + + for _, line := range lines { + fmt.Println(line) + } + } + + fmt.Println(totalLines) + + // Output: + // Lili,22,female + // Jim,21,male + // 2 +} +``` + +### GetExeOrDllVersion + +

Get the version of exe or dll file on windows.

+ +Signature: + +```go +func GetExeOrDllVersion(filePath string) (string, error) +``` + +Example:[Run](https://go.dev/play/p/iLRrDBhE38E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/fileutil" +) + +func main() { + v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`) + if err != nil { + panic(err) + } + + fmt.Println(v) + + // Output: + // 3.9.10.19 +} +``` diff --git a/docs/en/api/packages/formatter.md b/docs/en/api/packages/formatter.md new file mode 100644 index 00000000..57014cb3 --- /dev/null +++ b/docs/en/api/packages/formatter.md @@ -0,0 +1,310 @@ +# Formatter + +formatter contains some functions for data formatting. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/formatter/formatter.go](https://github.com/duke-git/lancet/blob/main/formatter/formatter.go) +- [https://github.com/duke-git/lancet/blob/main/formatter/byte.go](https://github.com/duke-git/lancet/blob/main/formatter/byte.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/formatter" +) +``` + +
+ +## Index + +- [Comma](#Comma) +- [Pretty](#Pretty) +- [PrettyToWriter](#PrettyToWriter) +- [DecimalBytes](#DecimalBytes) +- [BinaryBytes](#BinaryBytes) +- [ParseDecimalBytes](#ParseDecimalBytes) +- [ParseBinaryBytes](#ParseBinaryBytes) + +
+ +## Documentation + +### Comma + +

Add comma to a number value by every 3 numbers from right to left. ahead by a prefix symbol char. if value is a invalid number string like "aa", return empty string.

+ +Signature: + +```go +func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string +``` + +Example:[Run](https://go.dev/play/p/eRD5k2vzUVX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.Comma("123", "") + result2 := formatter.Comma("12345", "$") + result3 := formatter.Comma(1234567, "¥") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 123 + // $12,345 + // ¥1,234,567 +} +``` + +### Pretty + +

Pretty data to JSON string.

+ +Signature: + +```go +func Pretty(v any) (string, error) +``` + +Example:[Run](https://go.dev/play/p/YsciGj3FH2x) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.Pretty([]string{"a", "b", "c"}) + result2, _ := formatter.Pretty(map[string]int{"a": 1}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [ + // "a", + // "b", + // "c" + // ] + // { + // "a": 1 + // } +} +``` + +### PrettyToWriter + +

Pretty encode data to writer.

+ +Signature: + +```go +func PrettyToWriter(v any, out io.Writer) error +``` + +Example:[Run](https://go.dev/play/p/LPLZ3lDi5ma) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + type User struct { + Name string `json:"name"` + Aage uint `json:"age"` + } + user := User{Name: "King", Aage: 10000} + + buf := &bytes.Buffer{} + err := formatter.PrettyToWriter(user, buf) + + fmt.Println(buf) + fmt.Println(err) + + // Output: + // { + // "name": "King", + // "age": 10000 + // } + // + // +} +``` + +### DecimalBytes + +

Returns a human readable byte size under decimal standard (base 1000). The precision parameter specifies the number of digits after the decimal point, which is 4 for default.

+ +Signature: + +```go +func DecimalBytes(size float64, precision ...int) string +``` + +Example:[Run](https://go.dev/play/p/FPXs1suwRcs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.DecimalBytes(1000) + result2 := formatter.DecimalBytes(1024) + result3 := formatter.DecimalBytes(1234567) + result4 := formatter.DecimalBytes(1234567, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KB + // 1.024KB + // 1.2346MB + // 1.235MB +} +``` + +### BinaryBytes + +

Returns a human readable byte size under binary standard (base 1024). The precision parameter specifies the number of digits after the decimal point, which is 4 for default.

+ +Signature: + +```go +func BinaryBytes(size float64, precision ...int) string +``` + +Example:[Run](https://go.dev/play/p/G9oHHMCAZxP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1 := formatter.BinaryBytes(1024) + result2 := formatter.BinaryBytes(1024 * 1024) + result3 := formatter.BinaryBytes(1234567) + result4 := formatter.BinaryBytes(1234567, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KiB + // 1MiB + // 1.1774MiB + // 1.18MiB +} +``` + +### ParseDecimalBytes + +

Returns the human readable bytes size string into the amount it represents(base 1000).

+ +Signature: + +```go +func ParseDecimalBytes(size string) (uint64, error) +``` + +Example:[Run](https://go.dev/play/p/Am98ybWjvjj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.ParseDecimalBytes("12") + result2, _ := formatter.ParseDecimalBytes("12k") + result3, _ := formatter.ParseDecimalBytes("12 Kb") + result4, _ := formatter.ParseDecimalBytes("12.2 kb") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12000 + // 12000 + // 12200 +} +``` + +### ParseBinaryBytes + +

Returns the human readable bytes size string into the amount it represents(base 1024).

+ +Signature: + +```go +func ParseBinaryBytes(size string) (uint64, error) +``` + +Example:[Run](https://go.dev/play/p/69v1tTT62x8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/formatter" +) + +func main() { + result1, _ := formatter.ParseBinaryBytes("12") + result2, _ := formatter.ParseBinaryBytes("12ki") + result3, _ := formatter.ParseBinaryBytes("12 KiB") + result4, _ := formatter.ParseBinaryBytes("12.2 kib") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12288 + // 12288 + // 12492 +} +``` diff --git a/docs/en/api/packages/function.md b/docs/en/api/packages/function.md new file mode 100644 index 00000000..cf6d165c --- /dev/null +++ b/docs/en/api/packages/function.md @@ -0,0 +1,785 @@ +# Function + +Package function can control the flow of function execution and support part of functional programming. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go) +- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go) +- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/function" +) +``` + +
+ +## Index + +- [After](#After) +- [Before](#Before) +- [CurryFn](#CurryFn) +- [Compose](#Compose) +- [Debounce](#Debounce) +- [Debounceddeprecated](#Debounced) +- [Delay](#Delay) +- [Schedule](#Schedule) +- [Pipeline](#Pipeline) +- [Watcher](#Watcher) +- [And](#And) +- [Or](#Or) +- [Negate](#Negate) +- [Nor](#Nor) +- [Xnor](#Xnor) +- [Nand](#Nand) +- [AcceptIf](#AcceptIf) +- [Throttle](#Throttle) + + +
+ +## Documentation + +### After + +

Creates a function that invokes given func once it's called n or more times.

+ +Signature: + +```go +func After(n int, fn any) func(args ...any) []reflect.Value +``` + +Example:[Run](https://go.dev/play/p/eRD5k2vzUVX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + fn := function.After(2, func() { + fmt.Println("hello") + }) + + fn() + fn() + + // Output: + // hello +} +``` + +### Before + +

creates a function that invokes func once it's called less than n times.

+ +Signature: + +```go +func Before(n int, fn any) func(args ...any) []reflect.Value +``` + +Example:[Run](https://go.dev/play/p/0HqUDIFZ3IL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + fn := function.Before(2, func() { + fmt.Println("hello") + }) + + fn() + fn() + fn() + fn() + + // Output: + // hello + // hello +} +``` + +### CurryFn + +

Make curry function.

+ +Signature: + +```go +type CurryFn[T any] func(...T) T +func (cf CurryFn[T]) New(val T) func(...T) T +``` + +Example:[Run](https://go.dev/play/p/5HopfDwANKX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + add := func(a, b int) int { + return a + b + } + + var addCurry function.CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) + } + add1 := addCurry.New(1) + + result := add1(2) + + fmt.Println(result) + + // Output: + // 3 +} +``` + +### Compose + +

Compose the function list from right to left, then return the composed function.

+ +Signature: + +```go +func Compose[T any](fnList ...func(...T) T) func(...T) T +``` + +Example:[Run](https://go.dev/play/p/KKfugD4PKYF) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) + } + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) + } + transform := function.Compose(toUpper, toLower) + + result := transform("aBCde") + + fmt.Println(result) + + // Output: + // ABCDE +} +``` +### Debounce + +

Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.

+ +Signature: + +```go +func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) +``` + +Example:[Run](https://go.dev/play/p/-dGFrYn_1Zi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + callCount := 0 + fn := func() { + callCount++ + } + + debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond) + + for i := 0; i < 10; i++ { + debouncedFn() + time.Sleep(50 * time.Millisecond) + } + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + debouncedFn() + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + // Output: + // 1 + // 2 +} +``` + +### Debounced + +

Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.

+ +> ⚠️ This function is deprecated. use `Debounce` instead. + +Signature: + +```go +func Debounced(fn func(), duration time.Duration) func() +``` + +Example:[Run](https://go.dev/play/p/absuEGB_GN7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + count := 0 + + add := func() { + count++ + } + + debouncedAdd := function.Debounced(add, 50*time.Microsecond) + + debouncedAdd() + debouncedAdd() + debouncedAdd() + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + // Output: + // 1 + // 2 +} +``` + +### Delay + +

Invoke function after delayed time.

+ +Signature: + +```go +func Delay(delay time.Duration, fn any, args ...any) +``` + +Example:[Run](https://go.dev/play/p/Ivtc2ZE-Tye) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + var print = func(s string) { + fmt.Println(s) + } + + function.Delay(2*time.Second, print, "hello") + + // Output: + // hello +} +``` + +### Schedule + +

Invoke function every duration time, until close the returned bool chan.

+ +Signature: + +```go +func Schedule(d time.Duration, fn any, args ...any) chan bool +``` + +Example:[Run](https://go.dev/play/p/hbON-Xeyn5N) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + count := 0 + + increase := func() { + count++ + } + + stop := function.Schedule(2*time.Second, increase) + + time.Sleep(2 * time.Second) + close(stop) + + fmt.Println(count) + + // Output: + // 2 +} +``` + +### Pipeline + +

Pipeline takes a list of functions and returns a function whose param will be passed into +the functions one by one.

+ +Signature: + +```go +func Pipeline[T any](funcs ...func(T) T) func(T) T +``` + +Example:[Run](https://go.dev/play/p/mPdUVvj6HD6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + addOne := func(x int) int { + return x + 1 + } + double := func(x int) int { + return 2 * x + } + square := func(x int) int { + return x * x + } + + fn := function.Pipeline(addOne, double, square) + + result := fn(2) + + fmt.Println(result) + + // Output: + // 36 +} +``` + +### Watcher + +

Watcher is used for record code excution time. can start/stop/reset the watch timer. get the elapsed time of function execution.

+ +Signature: + +```go +type Watcher struct { + startTime int64 + stopTime int64 + excuting bool +} +func NewWatcher() *Watcher +func (w *Watcher) Start() //start the watcher +func (w *Watcher) Stop() //stop the watcher +func (w *Watcher) Reset() //reset the watcher +func (w *Watcher) GetElapsedTime() time.Duration //get the elapsed time of function execution +``` + +Example:[Run](https://go.dev/play/p/l2yrOpCLd1I) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + w := function.NewWatcher() + + w.Start() + + longRunningTask() + + fmt.Println(w.excuting) //true + + w.Stop() + + eapsedTime := w.GetElapsedTime().Milliseconds() + + fmt.Println(eapsedTime) + + w.Reset() +} + +func longRunningTask() { + var slice []int64 + for i := 0; i < 10000000; i++ { + slice = append(slice, int64(i)) + } +} + +``` + + +### And + +

Returns a composed predicate that represents the logical AND of a list of predicates. It evaluates to true only if all predicates evaluate to true for the given value.

+ +Signature: + +```go +func And[T any](predicates ...func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/dTBHJMQ0zD2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isNumericAndLength5 := function.And( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcde")) + + // Output: + // true + // false + // false +} +``` + +### Or + +

Returns a composed predicate that represents the logical OR of a list of predicates. It evaluates to true if at least one of the predicates evaluates to true for the given value.

+ +Signature: + +```go +func Or[T any](predicates ...func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/LitCIsDFNDA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + containsDigitOrSpecialChar := function.Or( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return strings.ContainsAny(s, "!@#$%") }, + ) + + fmt.Println(containsDigitOrSpecialChar("hello!")) + fmt.Println(containsDigitOrSpecialChar("hello")) + + // Output: + // true + // false +} +``` + +### Negate + +

Returns a predicate that represents the logical negation of this predicate.

+ +Signature: + +```go +func Negate[T any](predicate func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/jbI8BtgFnVE) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + // Define some simple predicates for demonstration + isUpperCase := func(s string) bool { + return strings.ToUpper(s) == s + } + isLowerCase := func(s string) bool { + return strings.ToLower(s) == s + } + isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase)) + + fmt.Println(isMixedCase("ABC")) + fmt.Println(isMixedCase("AbC")) + + // Output: + // false + // true +} +``` + + +### Nor + +

Returns a composed predicate that represents the logical NOR of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.

+ +Signature: + +```go +func Nor[T any](predicates ...func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/2KdCoBEOq84) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + match := function.Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("dbcdckkeee")) + + + match = function.Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("0123456789")) + + // Output: + // true + // false +} +``` + +### 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.

+ +Signature: + +```go +func Nand[T any](predicates ...func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/Rb-FdNGpgSO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isNumericAndLength5 := function.Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcdef")) + + // Output: + // false + // false + // true +} +``` + +### Xnor + +

Returns a composed predicate that represents the logical XNOR of a list of predicates. It evaluates to true only if all predicates evaluate to true or false for the given value.

+ +Signature: + +```go +func Xnor[T any](predicates ...func(T) bool) func(T) bool +``` + +Example:[Run](https://go.dev/play/p/FJxko8SFbqc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + isEven := func(i int) bool { return i%2 == 0 } + isPositive := func(i int) bool { return i > 0 } + + match := function.Xnor(isEven, isPositive) + + fmt.Println(match(2)) + fmt.Println(match(-3)) + fmt.Println(match(3)) + + // Output: + // true + // true + // false +} +``` + +### AcceptIf + +

AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. A predicate function that takes an argument of type T and returns a bool. An apply function that also takes an argument of type T and returns a modified value of the same type.

+ +Signature: + +```go +func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) +``` + +Example:[Run](https://go.dev/play/p/XlXHHtzCf7d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + + adder := function.AcceptIf( + function.And( + func(x int) bool { + return x > 10 + }, func(x int) bool { + return x%2 == 0 + }), + func(x int) int { + return x + 1 + }, + ) + + result, ok := adder(20) + fmt.Println(result) + fmt.Println(ok) + + result, ok = adder(21) + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 21 + // true + // 0 + // false +} +``` + +### Throttle + +

Throttle creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.

+ +Signature: + +```go +func Throttle(fn func(), interval time.Duration) func() +``` + +Example:[Run](https://go.dev/play/p/HpoMov-tJSN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/function" +) + +func main() { + callCount := 0 + + fn := func() { + callCount++ + } + + throttledFn := function.Throttle(fn, 1*time.Second) + + for i := 0; i < 5; i++ { + throttledFn() + } + + time.Sleep(1 * time.Second) + + fmt.Println(callCount) + + // Output: + // 1 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/maputil.md b/docs/en/api/packages/maputil.md new file mode 100644 index 00000000..9288c49d --- /dev/null +++ b/docs/en/api/packages/maputil.md @@ -0,0 +1,2365 @@ +# Maputil + +Package maputil includes some functions to manipulate map. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go) +- [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) + +
+ +## Example: + +```go +import ( + "github.com/duke-git/lancet/v2/maputil" +) +``` + +
+ +## Index + +- [MapTo](#MapTo) +- [ForEach](#ForEach) +- [Filter](#Filter) +- [FilterByKeys](#FilterByKeys) +- [FilterByValues](#FilterByValues) +- [OmitBy](#OmitBy) +- [OmitByKeys](#OmitByKeys) +- [OmitByValues](#OmitByValues) +- [Intersect](#Intersect) +- [Keys](#Keys) +- [Values](#Values) +- [KeysBy](#KeysBy) +- [ValuesBy](#ValuesBy) +- [MapKeys](#MapKeys) +- [MapValues](#MapValues) +- [Entries](#Entries) +- [FromEntries](#FromEntries) +- [Transform](#Transform) +- [Merge](#Merge) +- [Minus](#Minus) +- [IsDisjoint](#IsDisjoint) +- [HasKey](#HasKey) +- [MapToStruct](#MapToStruct) +- [ToSortedSlicesDefault](#ToSortedSlicesDefault) +- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator) +- [NewOrderedMap](#NewOrderedMap) +- [OrderedMap_Set](#OrderedMap_Set) +- [OrderedMap_Get](#OrderedMap_Get) +- [OrderedMap_Front](#OrderedMap_Front) +- [OrderedMap_Back](#OrderedMap_Back) +- [OrderedMap_Delete](#OrderedMap_Delete) +- [OrderedMap_Clear](#OrderedMap_Clear) +- [OrderedMap_Len](#OrderedMap_Len) +- [OrderedMap_Keys](#OrderedMap_Keys) +- [OrderedMap_Values](#OrderedMap_Values) +- [OrderedMap_Contains](#OrderedMap_Contains) +- [OrderedMap_Range](#OrderedMap_Range) +- [OrderedMap_Elements](#OrderedMap_Elements) +- [OrderedMap_Iter](#OrderedMap_Iter) +- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter) +- [OrderedMap_SortByKey](#OrderedMap_SortByKey) +- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON) +- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON) +- [NewConcurrentMap](#NewConcurrentMap) +- [ConcurrentMap_Get](#ConcurrentMap_Get) +- [ConcurrentMap_Set](#ConcurrentMap_Set) +- [ConcurrentMap_GetOrSet](#ConcurrentMap_GetOrSet) +- [ConcurrentMap_Delete](#ConcurrentMap_Delete) +- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete) +- [ConcurrentMap_Has](#ConcurrentMap_Has) +- [ConcurrentMap_Range](#ConcurrentMap_Range) +- [GetOrSet](#GetOrSet) +- [SortByKey](#SortByKey) +- [GetOrDefault](#GetOrDefault) +- [FindValuesBy](#FindValuesBy) + +
+ +## Documentation + +### MapTo + +

Rry to map any interface to struct or base type.

+ +Signature: + +```go +func MapTo(src any, dst any) error +``` + +Example:[Run](https://go.dev/play/p/4K7KBEPgS5M) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} +``` + +### ForEach + +

Executes iteratee funcation for every key and value pair in map.

+ +Signature: + +```go +func ForEach[K comparable, V any](m map[K]V, iteratee func(key K, value V)) +``` + +Example:[Run](https://go.dev/play/p/OaThj6iNVXK) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + } + + var sum int + + maputil.ForEach(m, func(_ string, value int) { + sum += value + }) + + fmt.Println(sum) + + // Output: + // 10 +} +``` + +### Filter + +

Iterates over map, return a new map contains all key and value pairs pass the predicate function.

+ +Signature: + +```go +func Filter[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V +``` + +Example:[Run](https://go.dev/play/p/fSvF3wxuNG7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + maputil.Filter(m, func(_ string, value int) { + sum += value + }) + + result := maputil.Filter(m, isEven) + + fmt.Println(result) + + // Output: + // map[b:2 d:4] +} +``` + +### FilterByKeys + +

Iterates over map, return a new map whose keys are all given keys.

+ +Signature: + +```go +func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V +``` + +Example:[Run](https://go.dev/play/p/7ov6BJHbVqh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.FilterByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2] +} +``` + +### FilterByValues + +

Iterates over map, return a new map whose values are all given values.

+ +Signature: + +```go +func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V +``` + +Example:[Run](https://go.dev/play/p/P3-9MdcXegR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.FilterByValues(m, []int{3, 4}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4] +} +``` + +### OmitBy + +

OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.

+ +Signature: + +```go +func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V +``` + +Example:[Run](https://go.dev/play/p/YJM4Hj5hNwm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + result := maputil.OmitBy(m, isEven) + + fmt.Println(result) + + // Output: + // map[a:1 c:3 e:5] +} +``` + +### OmitByKeys + +

The opposite of FilterByKeys, extracts all the map elements which keys are not omitted.

+ +Signature: + +```go +func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V +``` + +Example:[Run](https://go.dev/play/p/jXGrWDBfSRp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.OmitByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4 e:5] +} +``` + +### OmitByValues + +

The opposite of FilterByValues. remov all elements whose value are in the give slice.

+ +Signature: + +```go +func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V +``` + +Example:[Run](https://go.dev/play/p/XB7Y10uw20_U) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := maputil.OmitByValues(m, []int{4, 5}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### Intersect + +

Iterates over maps, return a new map of key and value pairs in all given maps.

+ +Signature: + +```go +func Intersect[K comparable, V any](maps ...map[K]V) map[K]V +``` + +Example:[Run](https://go.dev/play/p/Zld0oj3sjcC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 1, + "b": 2, + "c": 6, + "d": 7, + } + + m3 := map[string]int{ + "a": 1, + "b": 9, + "e": 9, + } + + result1 := maputil.Intersect(m1) + result2 := maputil.Intersect(m1, m2) + result3 := maputil.Intersect(m1, m2, m3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // map[a:1 b:2 c:3] + // map[a:1 b:2] + // map[a:1] +} +``` + +### Keys + +

Returns a slice of the map's keys.

+ +Signature: + +```go +func Keys[K comparable, V any](m map[K]V) []K +``` + +Example:[Run](https://go.dev/play/p/xNB5bTb97Wd) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + keys := maputil.Keys(m) + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [1 2 3 4 5] +} +``` + +### Merge + +

Merge maps, next key will overwrite previous key.

+ +Signature: + +```go +func Merge[K comparable, V any](maps ...map[K]V) map[K]V +``` + +Example:[Run](https://go.dev/play/p/H95LENF1uB-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[int]string{ + 1: "a", + 2: "b", + } + m2 := map[int]string{ + 1: "1", + 3: "2", + } + + result := maputil.Merge(m1, m2) + + fmt.Println(result) + + // Output: + // map[1:c 2:b 3:d] +} +``` + +### Minus + +

Creates an map of whose key in mapA but not in mapB.

+ +Signature: + +```go +func Minus[K comparable, V any](mapA, mapB map[K]V) map[K]V +``` + +Example:[Run](https://go.dev/play/p/3u5U9K7YZ9m) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 11, + "b": 22, + "d": 33, + } + + result := maputil.Minus(m1, m2) + + fmt.Println(result) + + // Output: + // map[c:3] +} +``` + +### Values + +

Returns a slice of the map's values.

+ +Signature: + +```go +func Values[K comparable, V any](m map[K]V) []V +``` + +Example:[Run](https://go.dev/play/p/CBKdUc5FTW6) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + values := maputil.Values(m) + sort.Strings(values) + + fmt.Println(values) + + // Output: + // [a a b c d] +} +``` + +### KeysBy + +

Creates a slice whose element is the result of function mapper invoked by every map's key.

+ +Signature: + +```go +func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T +``` + +Example:[Run](https://go.dev/play/p/hI371iB8Up8) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + } + + keys := maputil.KeysBy(m, func(n int) int { + return n + 1 + }) + + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [2 3 4] +} +``` + +### ValuesBy + +

Creates a slice whose element is the result of function mapper invoked by every map's value.

+ +Signature: + +```go +func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T +``` + +Example:[Run](https://go.dev/play/p/sg9-oRidh8f) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + values := maputil.ValuesBy(m, func(v string) string { + switch v { + case "a": + return "a-1" + case "b": + return "b-2" + case "c": + return "c-3" + default: + return "" + } + }) + + sort.Strings(values) + + fmt.Println(values) + + // Output: + // [a-1 b-2 c-3] +} +``` + +### MapKeys + +

Transforms a map to other type map by manipulating it's keys.

+ +Signature: + +```go +func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V +``` + +Example:[Run](https://go.dev/play/p/8scDxWeBDKd) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := maputil.MapKeys(m, func(k int, _ string) string { + return strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c] +} +``` + +### MapValues + +

Transforms a map to other type map by manipulating it's values.

+ +Signature: + +```go +func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T +``` + +Example:[Run](https://go.dev/play/p/g92aY3fc7Iw) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := maputil.MapValues(m, func(k int, v string) string { + return v + strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a1 2:b2 3:c3] +} +``` + +### Entry + +

Transforms a map into array of key/value pairs.

+ +Signature: + +```go +type Entry[K comparable, V any] struct { + Key K + Value V +} +func Entries[K comparable, V any](m map[K]V) []Entry[K, V] +``` + +Example:[Run](https://go.dev/play/p/Ltb11LNcElY) + +```go +package main + +import ( + "fmt" + "sort" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := maputil.Entries(m) + + sort.Slice(result, func(i, j int) bool { + return result[i].Value < result[j].Value + }) + + fmt.Println(result) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### FromEntries + +

Creates a map based on a slice of key/value pairs.

+ +Signature: + +```go +type Entry[K comparable, V any] struct { + Key K + Value V +} +func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V +``` + +Example:[Run](https://go.dev/play/p/fTdu4sCNjQO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + result := maputil.FromEntries([]Entry[string, int]{ + {Key: "a", Value: 1}, + {Key: "b", Value: 2}, + {Key: "c", Value: 3}, + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### Transform + +

Transform a map to another type map.

+ +Signature: + +```go +func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 +``` + +Example:[Run](https://go.dev/play/p/P6ovfToM3zj) + +```go +package main + +import ( + "fmt" + "strconv" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Transform(m, func(k string, v int) (string, string) { + return k, strconv.Itoa(v) + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### IsDisjoint + +

Checks two maps are disjoint if they have no keys in common.

+ +Signature: + +```go +func IsDisjoint[K comparable, V any](mapA, mapB map[K]V) bool +``` + +Example:[Run](https://go.dev/play/p/N9qgYg_Ho6f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "d": 22, + } + + m3 := map[string]int{ + "a": 22, + } + + result1 := maputil.IsDisjoint(m1, m2) + result2 := maputil.IsDisjoint(m1, m3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### HasKey + +

Checks if map has key or not. This function is used to replace the following boilerplate code:

+ +```go +_, haskey := amap["baz"]; + +if haskey { + fmt.Println("map has key baz") +} +``` + +Signature: + +```go +func HasKey[K comparable, V any](m map[K]V, key K) bool +``` + +Example:[Run](https://go.dev/play/p/isZZHOsDhFc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[string]int{ + "a": 1, + "b": 2, + } + + result1 := maputil.HasKey(m, "a") + result2 := maputil.HasKey(m, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### MapToStruct + +

Converts map to struct

+ +Signature: + +```go +func MapToStruct(m map[string]any, structObj any) error +``` + +Example:[Run](https://go.dev/play/p/7wYyVfX38Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + personReqMap := map[string]any{ + "name": "Nothin", + "max_age": 35, + "page": 1, + "pageSize": 10, + } + + type PersonReq struct { + Name string `json:"name"` + MaxAge int `json:"max_age"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + } + var personReq PersonReq + _ = maputil.MapToStruct(personReqMap, &personReq) + fmt.Println(personReq) + + // Output: + // {Nothin 35 1 10} +} +``` + +### ToSortedSlicesDefault + +

+Translate the key and value of the map into two slices that are sorted in ascending order according to the key’s value, with the position of the elements in the value slice corresponding to the key.

+ +Signature: + +```go +func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) +``` + +Example:[Run](https://go.dev/play/p/43gEM2po-qy) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + + keys, values := maputil.ToSortedSlicesDefault(m) + + fmt.Println(keys) + fmt.Println(values) + + // Output: + // [1 2 3] + // [a b c] +} +``` + +### ToSortedSlicesWithComparator + +

+Translate the key and value of the map into two slices that are sorted according to a custom sorting rule defined by a comparator function based on the key's value, with the position of the elements in the value slice corresponding to the key. +

+ +Signature: + +```go +func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) +``` + +Example:[Run](https://go.dev/play/p/0nlPo6YLdt3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m1 := map[time.Time]string{ + time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today", + time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday", + time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow", + } + + keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool { + return a.Before(b) + }) + + m2 := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool { + return a > b + }) + + fmt.Println(keys2) + fmt.Println(values2) + + fmt.Println(keys1) + fmt.Println(values1) + + // Output: + // [3 2 1] + // [c b a] + // [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC] + // [yesterday today tomorrow] +} +``` + +### NewOrderedMap + +

Creates a new OrderedMap.

+ +Signature: + +```go +func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] +``` + +Example:[Run](https://go.dev/play/p/99QjSYSBdiM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Set + +

Sets the given key-value pair.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Set(key K, value V) +``` + +Example:[Run](https://go.dev/play/p/Y4ZJ_oOc1FU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Get + +

Returns the value for the given key.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Get(key K) (V, bool) +``` + +Example:[Run](https://go.dev/play/p/Y4ZJ_oOc1FU) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Delete + +

Deletes the key-value pair for the given key.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Delete(key K) +``` + +Example:[Run](https://go.dev/play/p/5bIi4yaZ3K-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} +``` + +### OrderedMap_Clear + +

Clears the map.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Clear() +``` + +Example:[Run](https://go.dev/play/p/8LwoJyEfuFr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} +``` + +### OrderedMap_Front + +

Returns the first key-value pair.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Front() (struct { + Key K + Value V +}, bool) +``` + +Example:[Run](https://go.dev/play/p/ty57XSimpoe) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Back + +

Returns the last key-value pair.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Back() (struct { + Key K + Value V +}, bool) +``` + +Example:[Run](https://go.dev/play/p/rQMjp1yQmpa) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + backElement, ok := om.Back() + fmt.Println(backElement) + fmt.Println(ok) + + // Output: + // {c 3} + // true +} +``` + +### OrderedMap_Range + +

Calls the given function for each key-value pair.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) +``` + +Example:[Run](https://go.dev/play/p/U-KpORhc7LZ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} +``` + +### OrderedMap_Keys + +

Returns the keys in order.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Keys() []K +``` + +Example:[Run](https://go.dev/play/p/Vv_y9ExKclA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} +``` + +### OrderedMap_Values + +

Returns the values in order.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Values() []V +``` + +Example:[Run](https://go.dev/play/p/TWj5n1-PUfx) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} +``` + +### OrderedMap_Elements + +

Returns the key-value pairs in order.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Elements() []struct +``` + +Example:[Run](https://go.dev/play/p/4BHG4kKz6bB) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### OrderedMap_Len + +

Returns the number of key-value pairs.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Len() int +``` + +Example:[Run](https://go.dev/play/p/cLe6z2VX5N-) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} +``` + +### OrderedMap_Contains + +

Returns true if the given key exists.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Contains(key K) bool +``` + +Example:[Run](https://go.dev/play/p/QuwqqnzwDNX) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### OrderedMap_Iter + +

Returns a channel that yields key-value pairs in order.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) Iter() <-chan struct { + Key K + Value V +} +``` + +Example:[Run](https://go.dev/play/p/tlq2tdvicPt) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} +``` + +### OrderedMap_ReverseIter + +

Returns a channel that yields key-value pairs in reverse order.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) ReverseIter() <-chan struct { + Key K + Value V +} +``` + +Example:[Run](https://go.dev/play/p/8Q0ssg6hZzO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} +``` + +### OrderedMap_SortByKey + +

Sorts the map by key given less function.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) +``` + +Example:[Run](https://go.dev/play/p/N7hjD_alZPq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} +``` + +### OrderedMap_MarshalJSON + +

Implements the json.Marshaler interface.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) +``` + +Example:[Run](https://go.dev/play/p/C-wAwydIAC7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### OrderedMap_UnmarshalJSON + +

Implements the json.Unmarshaler interface.

+ +Signature: + +```go +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error +``` + +Example:[Run](https://go.dev/play/p/8C3MvJ3-mut) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + data := []byte(`{"a":1,"b":2,"c":3}`) + + om.UnmarshalJSON(data) + + fmt.Println(om.Elements()) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### NewConcurrentMap + +

ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.

+ +Signature: + +```go +// NewConcurrentMap create a ConcurrentMap with specific shard count. +func NewConcurrentMap[K comparable, V any](shardCount int) *ConcurrentMap[K, V] +``` + +Example:[Run](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + // create a ConcurrentMap whose key type is string, value type is int + cm := maputil.NewConcurrentMap[string, int](100) +} +``` + +### ConcurrentMap_Set + +

Set the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Set(key K, value V) +``` + +Example:[Run](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + wg2.Done() + }(j) + } + wg2.Wait() + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_Get + +

Get the value stored in the map for a key, or nil if no.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Get(key K) (V, bool) +``` + +Example:[Run](https://go.dev/play/p/3PenTPETJT0) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) + wg2.Done() + }(j) + } + wg2.Wait() + + // output: (order may change) + // 1 true + // 3 true + // 2 true + // 0 true + // 4 true +} +``` + +### ConcurrentMap_GetOrSet + +

Returns the existing value for the key if present. Otherwise, it sets and returns the given value.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) GetOrSet(key K, value V) (actual V, ok bool) +``` + +Example:[Run](https://go.dev/play/p/aDcDApOK01a) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg sync.WaitGroup + wg.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + val, ok := cm.GetOrSet(fmt.Sprintf("%d", n), n) + fmt.Println(val, ok) + wg.Done() + }(i) + } + wg.Wait() + + // output: (order may change) + // 1 false + // 3 false + // 2 false + // 0 false + // 4 false +} +``` + +### ConcurrentMap_Delete + +

Delete the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Delete(key K) +``` + +Example:[Run](https://go.dev/play/p/uTIJZYhpVMS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + cm.Delete(fmt.Sprintf("%d", n)) + wg2.Done() + }(j) + } + + wg2.Wait() +} +``` + +### ConcurrentMap_GetAndDelete + +

Returns the existing value for the key if present and then delete the value for the key. Otherwise, do nothing, just return false.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) GetAndDelete(key K) (actual V, ok bool) +``` + +Example:[Run](https://go.dev/play/p/ZyxeIXSZUiM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //n, true + + _, ok = cm.Get(fmt.Sprintf("%d", n)) + fmt.Println(val, ok) //false + + wg2.Done() + }(j) + } + + wg2.Wait() +} +``` + +### ConcurrentMap_Has + +

Checks if map has the value for a key.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Has(key K) bool +``` + +Example:[Run](https://go.dev/play/p/C8L4ul9TVwf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(5) + for j := 0; j < 5; j++ { + go func(n int) { + ok := cm.Has(fmt.Sprintf("%d", n)) + fmt.Println(ok) // true + wg2.Done() + }(j) + } + wg2.Wait() +} +``` + +### ConcurrentMap_Range + +

Calls iterator sequentially for each key and value present in each of the shards in the map. If iterator returns false, range stops the iteration.

+ +Signature: + +```go +func (cm *ConcurrentMap[K, V]) Range(iterator func(key K, value V) bool) +``` + +Example:[Run](https://go.dev/play/p/iqcy7P8P0Pr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + cm := maputil.NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + + cm.Range(func(key string, value int) bool { + fmt.Println(value) + return true + }) +} +``` + +### GetOrSet + +

Returns value of the given key or set the given value value if not present.

+ +Signature: + +```go +func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V +``` + +Example:[Run](https://go.dev/play/p/IVQwO1OkEJC) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 1: "a", + } + + result1 := maputil.GetOrSet(m, 1, "1") + result2 := maputil.GetOrSet(m, 2, "b") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // a + // b +} +``` + +### SortByKey + +

Sorts the map by its keys and returns a new map with sorted keys.

+ +Signature: + +```go +func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) +``` + +Example:[Run](https://go.dev/play/p/PVdmBSnm6P_W) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + + result := maputil.SortByKey(m, func(a, b int) bool { + return a < b + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c 4:d] +} +``` + +### GetOrDefault + +

Returns the value of the given key or a default value if the key is not present.

+ +Signature: + +```go +func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V +``` + +Example:[Run](https://go.dev/play/p/99QjSYSBdiM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + m := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + + result1 := maputil.GetOrDefault(m, 1, "default") + result2 := maputil.GetOrDefault(m, 6, "default") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // a + // default +} +``` + +### FindValuesBy + +

Returns a slice of values from the map that satisfy the given predicate function.

+ +Signature: + +```go +func FindValuesBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) []V +``` + +Example:[Run](https://go.dev/play/p/bvNwNBZDm6v) + +```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] +} +``` diff --git a/docs/en/api/packages/mathutil.md b/docs/en/api/packages/mathutil.md new file mode 100644 index 00000000..61ec2be3 --- /dev/null +++ b/docs/en/api/packages/mathutil.md @@ -0,0 +1,1300 @@ +# Mathutil + +Package mathutil implements some functions for math calculation. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go) + +
+ +## Example: + +```go +import ( + "github.com/duke-git/lancet/v2/mathutil" +) +``` + +
+ +## Index + +- [Average](#Average) +- [Exponent](#Exponent) +- [Fibonacci](#Fibonacci) +- [Factorial](#Factorial) +- [Max](#Max) +- [MaxBy](#MaxBy) +- [Min](#Min) +- [MinBy](#MaxBy) +- [Percent](#Percent) +- [RoundToFloat](#RoundToFloat) +- [RoundToString](#RoundToString) +- [TruncRound](#TruncRound) +- [CeilToFloat](#CeilToFloat) +- [CeilToString](#CeilToString) +- [FloorToFloat](#FloorToFloat) +- [FloorToString](#FloorToString) +- [Range](#Range) +- [RangeWithStep](#RangeWithStep) +- [AngleToRadian](#AngleToRadian) +- [RadianToAngle](#RadianToAngle) +- [PointDistance](#PointDistance) +- [IsPrime](#IsPrime) +- [GCD](#GCD) +- [LCM](#LCM) +- [Cos](#Cos) +- [Sin](#Sin) +- [Log](#Log) +- [Sum](#Sum) +- [Abs](#Abs) +- [Div](#Div) +- [Variance](#Variance) +- [StdDev](#StdDev) +- [Permutation](#Permutation) +- [Combination](#Combination) + +
+ +## Documentation + +### Average + +

Return average value of numbers. Maybe call RoundToFloat to round result.

+ +Signature: + +```go +func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 +``` + +Example:[Run](https://go.dev/play/p/Vv7LBwER-pz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Average(1, 2) + + avg := mathutil.Average(1.2, 1.4) + result2 := mathutil.RoundToFloat(avg, 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1.5 + // 1.3 +} +``` + +### Exponent + +

Calculate x to the nth power.

+ +Signature: + +```go +func Exponent(x, n int64) int64 +``` + +Example:[Run](https://go.dev/play/p/uF3HGNPk8wr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Exponent(10, 0) + result2 := mathutil.Exponent(10, 1) + result3 := mathutil.Exponent(10, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 10 + // 100 +} +``` + +### Fibonacci + +

Calculate the nth number of fibonacci sequence.

+ +Signature: + +```go +func Fibonacci(first, second, n int) int +``` + +Example:[Run](https://go.dev/play/p/IscseUNMuUc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Fibonacci(1, 1, 1) + result2 := mathutil.Fibonacci(1, 1, 2) + result3 := mathutil.Fibonacci(1, 1, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 1 + // 5 +} +``` + +### Factorial + +

Calculate the factorial of x.

+ +Signature: + +```go +func Factorial(x uint) uint +``` + +Example:[Run](https://go.dev/play/p/tt6LdOK67Nx) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Factorial(1) + result2 := mathutil.Factorial(2) + result3 := mathutil.Factorial(3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 6 +} +``` + +### Max + +

Return max value of numbers.

+ +Signature: + +```go +func Max[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +Example:[Run](https://go.dev/play/p/cN8DHI0rTkH) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Max(1, 2, 3) + result2 := mathutil.Max(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.4 +} +``` + +### MaxBy + +

Return the maximum value of a slice using the given comparator function.

+ +Signature: + +```go +func MaxBy[T any](slice []T, comparator func(T, T) bool) T +``` + +Example:[Run](https://go.dev/play/p/pbe2MT-7DV2) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.MaxBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result2 := mathutil.MaxBy([]string{"abd", "abc", "ab"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result3 := mathutil.MaxBy([]string{}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abc + // abd + // +} +``` + +### Min + +

Return the minimum value of numbers.

+ +Signature: + +```go +func Min[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +Example:[Run](https://go.dev/play/p/21BER_mlGUj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Min(1, 2, 3) + result2 := mathutil.Min(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 1.1 +} +``` + +### MinBy + +

Return the minimum value of a slice using the given comparator function.

+ +Signature: + +```go +func MinBy[T any](slice []T, comparator func(T, T) bool) T +``` + +Example:[Run](https://go.dev/play/p/N9qgYg_Ho6f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.MinBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result2 := mathutil.MinBy([]string{"ab", "ac", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result3 := mathutil.MinBy([]string{}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // a + // ab + // +} +``` + +### Percent + +

calculate the percentage of val to total, retain n decimal places.

+ +Signature: + +```go +func Percent(val, total float64, n int) float64 +``` + +Example:[Run](https://go.dev/play/p/s0NdFCtwuyd) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Percent(1, 2, 2) + result2 := mathutil.Percent(0.1, 0.3, 2) + result3 := mathutil.Percent(-30305, 408420, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 50 + // 33.33 + // -7.42 +} +``` + +### RoundToFloat + +

Round float up to n decimal places.

+ +Signature: + +```go +func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +Example:[Run](https://go.dev/play/p/ghyb528JRJL) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RoundToFloat(0.124, 2) + result2 := mathutil.RoundToFloat(0.125, 2) + result3 := mathutil.RoundToFloat(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} +``` + +### RoundToString + +

Round float up to n decimal places. will return string.

+ +Signature: + +```go +func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +Example:[Run](https://go.dev/play/p/kZwpBRAcllO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RoundToString(0.124, 2) + result2 := mathutil.RoundToString(0.125, 2) + result3 := mathutil.RoundToString(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} +``` + +### TruncRound + +

Round float off n decimal places.

+ +Signature: + +```go +func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T +``` + +Example:[Run](https://go.dev/play/p/aumarSHIGzP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.TruncRound(0.124, 2) + result2 := mathutil.TruncRound(0.125, 2) + result3 := mathutil.TruncRound(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.12 + // 0.125 +} +``` + +### CeilToFloat + +

Round float up n decimal places.

+ +Signature: + +```go +func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +Example:[Run](https://go.dev/play/p/8hOeSADZPCo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.CeilToFloat(3.14159, 1) + result2 := mathutil.CeilToFloat(3.14159, 2) + result3 := mathutil.CeilToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5 +} +``` + +### CeilToString + +

Round float up n decimal places.

+ +Signature: + +```go +func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +Example:[Run](https://go.dev/play/p/wy5bYEyUKKG) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.CeilToString(3.14159, 1) + result2 := mathutil.CeilToString(3.14159, 2) + result3 := mathutil.CeilToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5.0000 +} +``` + +### FloorToFloat + +

Round float down n decimal places.

+ +Signature: + +```go +func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 +``` + +Example:[Run](https://go.dev/play/p/vbCBrQHZEED) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.FloorToFloat(3.14159, 1) + result2 := mathutil.FloorToFloat(3.14159, 2) + result3 := mathutil.FloorToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5 +} +``` + +### FloorToString + +

Round float down n decimal places.

+ +Signature: + +```go +func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string +``` + +Example:[Run](https://go.dev/play/p/Qk9KPd2IdDb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.FloorToString(3.14159, 1) + result2 := mathutil.FloorToString(3.14159, 2) + result3 := mathutil.FloorToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5.0000 +} +``` + +### Range + +

Creates a slice of numbers from start with specified count, element step is 1.

+ +Signature: + +```go +func Range[T constraints.Integer | constraints.Float](start T, count int) []T +``` + +Example:[Run](https://go.dev/play/p/9ke2opxa8ZP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Range(1, 4) + result2 := mathutil.Range(1, -4) + result3 := mathutil.Range(-4, 4) + result4 := mathutil.Range(1.0, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [-4 -3 -2 -1] + // [1 2 3 4] +} +``` + +### RangeWithStep + +

Creates a slice of numbers from start to end with specified step.

+ +Signature: + +```go +func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T +``` + +Example:[Run](https://go.dev/play/p/akLWz0EqOSM) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RangeWithStep(1, 4, 1) + result2 := mathutil.RangeWithStep(1, -1, 0) + result3 := mathutil.RangeWithStep(-4, 1, 2) + result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3] + // [] + // [-4 -2 0] + // [1 2.1 3.2] +} +``` + +### AngleToRadian + +

Converts angle value to radian value.

+ +Signature: + +```go +func AngleToRadian(angle float64) float64 +``` + +Example:[Run](https://go.dev/play/p/CIvlICqrHql) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.AngleToRadian(45) + result2 := mathutil.AngleToRadian(90) + result3 := mathutil.AngleToRadian(180) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.7853981633974483 + // 1.5707963267948966 + // 3.141592653589793 +} +``` + +### RadianToAngle + +

Converts radian value to angle value.

+ +Signature: + +```go +func RadianToAngle(radian float64) float64 +``` + +Example:[Run](https://go.dev/play/p/dQtmOTUOMgi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.RadianToAngle(math.Pi) + result2 := mathutil.RadianToAngle(math.Pi / 2) + result3 := mathutil.RadianToAngle(math.Pi / 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 180 + // 90 + // 45 +} +``` + +### PointDistance + +

Caculates two points distance.

+ +Signature: + +```go +func PointDistance(x1, y1, x2, y2 float64) float64 +``` + +Example:[Run](https://go.dev/play/p/RrG4JIaziM8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.PointDistance(1, 1, 4, 5) + + fmt.Println(result1) + + // Output: + // 5 +} +``` + +### IsPrime + +

Checks if number is prime number.

+ +Signature: + +```go +func IsPrime(n int) bool +``` + +Example:[Run](https://go.dev/play/p/Rdd8UTHZJ7u) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.IsPrime(-1) + result2 := mathutil.IsPrime(0) + result3 := mathutil.IsPrime(1) + result4 := mathutil.IsPrime(2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### GCD + +

Return greatest common divisor (GCD) of integers.

+ +Signature: + +```go +func GCD[T constraints.Integer](integers ...T) T +``` + +Example:[Run](https://go.dev/play/p/CiEceLSoAKB) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.GCD(1, 1) + result2 := mathutil.GCD(1, -1) + result3 := mathutil.GCD(-1, 1) + result4 := mathutil.GCD(-1, -1) + result5 := mathutil.GCD(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // 1 + // -1 + // -1 + // 3 +} +``` + +### LCM + +

Return Least Common Multiple (LCM) of integers.

+ +Signature: + +```go +func LCM[T constraints.Integer](integers ...T) T +``` + +Example:[Run](https://go.dev/play/p/EjcZxfY7G_g) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.LCM(1, 1) + result2 := mathutil.LCM(1, 2) + result3 := mathutil.LCM(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 18 +} +``` + +### Cos + +

Returns the cosine of the radian argument.

+ +Signature: + +```go +func Cos(radian float64, precision ...int) float64 +``` + +Example:[Run](https://go.dev/play/p/Sm89LoIfvFq) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Cos(0) + result2 := mathutil.Cos(90) + result3 := mathutil.Cos(180) + result4 := mathutil.Cos(math.Pi) + result5 := mathutil.Cos(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // -0.447 + // -0.598 + // -1 + // 0 +} +``` + +### Sin + +

Returns the sine of the radian argument.

+ +Signature: + +```go +func Sin(radian float64, precision ...int) float64 +``` + +Example:[Run](https://go.dev/play/p/TWMQlMywDsP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Sin(0) + result2 := mathutil.Sin(90) + result3 := mathutil.Sin(180) + result4 := mathutil.Sin(math.Pi) + result5 := mathutil.Sin(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 0 + // 0.894 + // -0.801 + // 0 + // 1 +} +``` + +### Log + +

Returns the logarithm of base n.

+ +Signature: + +```go +func Log(n, base float64) float64 +``` + +Example:[Run](https://go.dev/play/p/_d4bi8oyhat) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Log(8, 2) + result2 := mathutil.TruncRound(mathutil.Log(5, 2), 2) + result3 := mathutil.TruncRound(mathutil.Log(27, 3), 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3 + // 2.32 + // 3 +} +``` + +### Sum + +

Returns sum of passed numbers.

+ +Signature: + +```go +func Sum[T constraints.Integer | constraints.Float](numbers ...T) T +``` + +Example:[Run](https://go.dev/play/p/1To2ImAMJA7) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Sum(1, 2) + result2 := mathutil.Sum(0.1, float64(1)) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.1 +} +``` + +### Abs + +

Returns the absolute value of x.

+ +Signature: + +```go +func Abs[T constraints.Integer | constraints.Float](x T) T +``` + +Example:[Run](https://go.dev/play/p/fsyBh1Os-1d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Abs(-1) + result2 := mathutil.Abs(-0.1) + result3 := mathutil.Abs(float32(0.2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 0.1 + // 0.2 +} +``` + +### Div + +

Returns the result of x divided by y.

+ +Signature: + +```go +func Div[T constraints.Float | constraints.Integer](x T, y T) float64 +``` + +Example:[Run](https://go.dev/play/p/WLxDdGXXYat) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Div(9, 4) + result2 := mathutil.Div(1, 2) + result3 := mathutil.Div(0, 666) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + // Output: + // 2.25 + // 0.5 + // 0 +} +``` + +### Variance + +

Returns the variance of numbers.

+ +Signature: + +```go +func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 +``` + +Example:[Run](https://go.dev/play/p/uHuV4YgXf8F) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Variance([]int{1, 2, 3, 4, 5}) + result2 := mathutil.Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 2 + // 2.42 +} +``` + +### StdDev + +

Returns the standard deviation of numbers.

+ +Signature: + +```go +func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 +``` + +Example:[Run](https://go.dev/play/p/FkNZDXvHD2l) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + 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) + + // Output: + // 1.41 + // 1.55 +} +``` + +### Permutation + +

Calculates P(n, k).

+ +Signature: + +```go +func Permutation(n, k uint) uint +``` + +Example:[Run](https://go.dev/play/p/MgobwH_FOxj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Permutation(5, 3) + result2 := mathutil.Permutation(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 60 + // 120 +} +``` + +### Combination + +

Calculates C(n, k).

+ +Signature: + +```go +func Combination(n, k uint) uint +``` + +Example:[Run](https://go.dev/play/p/ENFQRDQUFi9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/mathutil" +) + +func main() { + result1 := mathutil.Combination(5, 3) + result2 := mathutil.Combination(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/netutil.md b/docs/en/api/packages/netutil.md new file mode 100644 index 00000000..6b4cf236 --- /dev/null +++ b/docs/en/api/packages/netutil.md @@ -0,0 +1,1115 @@ +# Netutil + +Package netutil contains functions to get net information and send http request. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/netutil/net.go](https://github.com/duke-git/lancet/blob/main/netutil/net.go) + +- [https://github.com/duke-git/lancet/blob/main/netutil/http.go](https://github.com/duke-git/lancet/blob/main/netutil/http.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/netutil" +) +``` + +
+ +## Index + +- [ConvertMapToQueryString](#ConvertMapToQueryString) +- [EncodeUrl](#EncodeUrl) +- [GetInternalIp](#GetInternalIp) +- [GetIps](#GetIps) +- [GetMacAddrs](#GetMacAddrs) +- [GetPublicIpInfo](#GetPublicIpInfo) +- [GetRequestPublicIp](#GetRequestPublicIp) +- [IsPublicIP](#IsPublicIP) +- [IsInternalIP](#IsInternalIP) +- [HttpRequest](#HttpRequest) +- [HttpClient](#HttpClient) +- [SendRequest](#SendRequest) +- [DecodeResponse](#DecodeResponse) +- [StructToUrlValues](#StructToUrlValues) +- [HttpGetDeprecated](#HttpGet) +- [HttpDeleteDeprecated](#HttpDelete) +- [HttpPostDeprecated](#HttpPost) +- [HttpPutDeprecated](#HttpPut) +- [HttpPatchDeprecated](#HttpPatch) +- [ParseHttpResponse](#ParseHttpResponse) +- [DownloadFile](#DownloadFile) +- [UploadFile](#UploadFile) +- [IsPingConnected](#IsPingConnected) +- [IsTelnetConnected](#IsTelnetConnected) +- [BuildUrl](#BuildUrl) +- [AddQueryParams](#AddQueryParams) + +
+ + + +## Documentation + +### ConvertMapToQueryString + +

Convert map to url query string.

+ +Signature: + +```go +func ConvertMapToQueryString(param map[string]any) string +``` + +Example:[Run](https://go.dev/play/p/jnNt_qoSnRi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + var m = map[string]any{ + "c": 3, + "a": 1, + "b": 2, + } + qs := netutil.ConvertMapToQueryString(m) + + // Output: + // a=1&b=2&c=3 +} +``` + +### EncodeUrl + +

Encode url query string values.

+ +Signature: + +```go +func EncodeUrl(urlStr string) (string, error) +``` + +Example:[Run](https://go.dev/play/p/bsZ6BRC4uKI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + urlAddr := "http://www.lancet.com?a=1&b=[2]" + encodedUrl, err := netutil.EncodeUrl(urlAddr) + + if err != nil { + fmt.Println(err) + } + + fmt.Println(encodedUrl) + + // Output: + // http://www.lancet.com?a=1&b=%5B2%5D +} +``` + +### GetInternalIp + +

Get internal ip information.

+ +Signature: + +```go +func GetInternalIp() string +``` + +Example:[Run](https://go.dev/play/p/5mbu-gFp7ei) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + internalIp := netutil.GetInternalIp() + ip := net.ParseIP(internalIp) + + fmt.Println(ip) + + // Output: + // 192.168.1.9 +} +``` + +### GetIps + +

Get all ipv4 list.

+ +Signature: + +```go +func GetIps() []string +``` + +Example:[Run](https://go.dev/play/p/NUFfcEmukx1) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ips := netutil.GetIps() + fmt.Println(ips) + + // Output: + // [192.168.1.9] +} +``` + +### GetMacAddrs + +

Get all mac addresses list.

+ +Signature: + +```go +func GetMacAddrs() []string { +``` + +Example:[Run](https://go.dev/play/p/Rq9UUBS_Xp1) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + macAddrs := netutil.GetMacAddrs() + fmt.Println(macAddrs) + + // Output: + // [18:31:bf:09:d1:56 76:ee:2a:e6:2e:0f 74:ee:2a:e6:2e:0f 74:ee:2a:e6:2e:0f] +} +``` + +### GetPublicIpInfo + +

Get public ip information.

+ +Signature: + +```go +func GetPublicIpInfo() (*PublicIpInfo, error) +type PublicIpInfo struct { + Status string `json:"status"` + Country string `json:"country"` + CountryCode string `json:"countryCode"` + Region string `json:"region"` + RegionName string `json:"regionName"` + City string `json:"city"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + Isp string `json:"isp"` + Org string `json:"org"` + As string `json:"as"` + Ip string `json:"query"` +} +``` + +Example:[Run](https://go.dev/play/p/YDxIfozsRHR) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + publicIpInfo, err := netutil.GetPublicIpInfo() + if err != nil { + fmt.Println(err) + } + + fmt.Println(publicIpInfo) +} +``` + +### GetRequestPublicIp + +

Get http request public ip.

+ +Signature: + +```go +func GetRequestPublicIp(req *http.Request) string +``` + +Example:[Run](https://go.dev/play/p/kxU-YDc_eBo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip := "36.112.24.10" + + request := http.Request{ + Method: "GET", + Header: http.Header{ + "X-Forwarded-For": {ip}, + }, + } + publicIp := netutil.GetRequestPublicIp(&request) + + fmt.Println(publicIp) + + // Output: + // 36.112.24.10 +} +``` + +### IsPublicIP + +

Checks if an ip is public or not.

+ +Signature: + +```go +func IsPublicIP(IP net.IP) bool +``` + +Example:[Run](https://go.dev/play/p/nmktSQpJZnn) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip1 := netutil.IsPublicIP(net.ParseIP("127.0.0.1")) + ip2 := netutil.IsPublicIP(net.ParseIP("192.168.0.1")) + ip3 := netutil.IsPublicIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // false + // false + // true +} +``` + +### IsInternalIP + +

Checks if an ip is intranet or not.

+ +Signature: + +```go +func IsInternalIP(IP net.IP) bool +``` + +Example:[Run](https://go.dev/play/p/sYGhXbgO4Cb) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ip1 := netutil.IsInternalIP(net.ParseIP("127.0.0.1")) + ip2 := netutil.IsInternalIP(net.ParseIP("192.168.0.1")) + ip3 := netutil.IsInternalIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // true + // true + // false +} +``` + +### HttpRequest + +

HttpRequest is a struct used to abstract HTTP request entity.

+ +Signature: + +```go +type HttpRequest struct { + RawURL string + Method string + Headers http.Header + QueryParams url.Values + FormData url.Values + Body []byte +} +``` + +Example:[Run](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + header := http.Header{} + header.Add("Content-Type", "multipart/form-data") + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "testItem") + + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos", + Method: "POST", + Headers: header, + FormData: postData, + } +} +``` + +### HttpClient + +

HttpClient is a struct used to send HTTP request. It can be instanced with some configurations or none config.

+ +Signature: + +```go +type HttpClient struct { + *http.Client + TLS *tls.Config + Request *http.Request + Config HttpClientConfig +} + +type HttpClientConfig struct { + SSLEnabled bool + TLSConfig *tls.Config + Compressed bool + HandshakeTimeout time.Duration + ResponseTimeout time.Duration + Verbose bool +} + +func NewHttpClient() *HttpClient + +func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient + +``` + +Example:[Run](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + httpClientCfg := netutil.HttpClientConfig{ + SSLEnabled: true, + HandshakeTimeout:10 * time.Second + } + httpClient := netutil.NewHttpClientWithConfig(&httpClientCfg) +} +``` + +### SendRequest + +

Use HttpClient to send HTTP request.

+ +Signature: + +```go +func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, error) +``` + +Example:[Run](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := netutil.NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + return + } + + fmt.Println(todo.Id) + + // Output: + // 1 +} +``` + +### DecodeResponse + +

Decode http response into target object.

+ +Signature: + +```go +func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error +``` + +Example:[Run](https://go.dev/play/p/jUSgynekH7G) + +```go +package main + +import ( + "fmt" + "net" + "time" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + request := &netutil.HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := netutil.NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + return + } + + fmt.Println(todo.Id) + + // Output: + // 1 +} +``` + +### StructToUrlValues + +

Convert struct to url values, only convert the field which is exported and has `json` tag.

+ +Signature: + +```go +func StructToUrlValues(targetStruct any) url.Values +``` + +Example:[Run](https://go.dev/play/p/pFqMkM40w9z) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + type TodoQuery struct { + Id int `json:"id"` + UserId int `json:"userId"` + Name string `json:"name,omitempty"` + Status string + } + item := TodoQuery{ + Id: 1, + UserId: 123, + Name: "test", + Status: "completed", + } + queryValues := netutil.StructToUrlValues(item) + + fmt.Println(todoValues.Get("id")) + fmt.Println(todoValues.Get("userId")) + fmt.Println(todoValues.Get("name")) + fmt.Println(todoValues.Get("status")) + + // Output: + // 1 + // 123 + // test + // +} +``` + +### HttpGet + +

Send http get request.

+ +> ⚠️ This function is deprecated. use `SendRequest` instead. + +Signature: + +```go +// params[0] is header which type should be http.Header or map[string]string, +// params[1] is query param which type should be url.Values or map[string]string, +// params[2] is post body which type should be []byte. +// params[3] is http client which type should be http.Client. +func HttpGet(url string, params ...any) (*http.Response, error) +``` + +Example: + +```go +package main + +import ( + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + + resp, err := netutil.HttpGet(url, header) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPost + +

Send http post request.

+ +> ⚠️ This function is deprecated. use `SendRequest` instead. + +Signature: + +```go +// params[0] is header which type should be http.Header or map[string]string, +// params[1] is query param which type should be url.Values or map[string]string, +// params[2] is post body which type should be []byte. +// params[3] is http client which type should be http.Client. +func HttpPost(url string, params ...any) (*http.Response, error) +``` + +Example: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos" + header := map[string]string{ + "Content-Type": "application/x-www-form-urlencoded", + } + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "TestToDo") + + resp, err := netutil.HttpPost(apiUrl, header, nil, postData) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPut + +

Send http put request.

+ +> ⚠️ This function is deprecated. use `SendRequest` instead. + +Signature: + +```go +// params[0] is header which type should be http.Header or map[string]string, +// params[1] is query param which type should be url.Values or map[string]string, +// params[2] is post body which type should be []byte. +// params[3] is http client which type should be http.Client. +func HttpPut(url string, params ...any) (*http.Response, error) +``` + +Example: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + } + todo := Todo{1, 1, "TestPutToDo"} + bodyParams, _ := json.Marshal(todo) + + resp, err := netutil.HttpPut(url, header, nil, bodyParams) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpDelete + +

Send http delete request.

+ +> ⚠️ This function is deprecated. use `SendRequest` instead. + +Signature: + +```go +// params[0] is header which type should be http.Header or map[string]string, +// params[1] is query param which type should be url.Values or map[string]string, +// params[2] is post body which type should be []byte. +// params[3] is http client which type should be http.Client. +func HttpDelete(url string, params ...any) (*http.Response, error) +``` + +Example: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + resp, err := netutil.HttpDelete(url) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### HttpPatch + +

Send http patch request.

+ +> ⚠️ This function is deprecated. use `SendRequest` instead. + +Signature: + +```go +// params[0] is header which type should be http.Header or map[string]string, +// params[1] is query param which type should be url.Values or map[string]string, +// params[2] is post body which type should be []byte. +// params[3] is http client which type should be http.Client. +func HttpPatch(url string, params ...any) (*http.Response, error) +``` + +Example: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + } + todo := Todo{1, 1, "TestPatchToDo"} + bodyParams, _ := json.Marshal(todo) + + resp, err := netutil.HttpPatch(url, header, nil, bodyParams) + if err != nil { + log.Fatal(err) + } + + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(body) +} +``` + +### ParseHttpResponse + +

Decode http response to specified interface.

+ +Signature: + +```go +func ParseHttpResponse(resp *http.Response, obj any) error +``` + +Example: + +```go +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + url := "https://jsonplaceholder.typicode.com/todos/1" + header := map[string]string{ + "Content-Type": "application/json", + } + + resp, err := netutil.HttpGet(url, header) + if err != nil { + log.Fatal(err) + } + + type Todo struct { + Id int `json:"id"` + UserId int `json:"userId"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + toDoResp := &Todo{} + err = netutil.ParseHttpResponse(resp, toDoResp) + if err != nil { + log.Fatal(err) + } + + fmt.Println(toDoResp) +} +``` + +### DownloadFile + +

Download the file exist in url to a local file.

+ +Signature: + +```go +func DownloadFile(filepath string, url string) error +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + err := netutil.DownloadFile("./lancet_logo.jpg", "https://picx.zhimg.com/v2-fc82a4199749de9cfb71e32e54f489d3_720w.jpg?source=172ae18b") + + fmt.Println(err) +} +``` + +### UploadFile + +

Upload the file to a server.

+ +Signature: + +```go +func UploadFile(filepath string, server string) (bool, error) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + ok, err := netutil.UploadFile("./a.jpg", "http://www.xxx.com/bucket/test") + + fmt.Println(ok) + fmt.Println(err) +} +``` + +### IsPingConnected + +

checks if can ping the specified host or not.

+ +Signature: + +```go +func IsPingConnected(host string) bool +``` + +Example:[Run](https://go.dev/play/p/q8OzTijsA87) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + result1 := netutil.IsPingConnected("www.baidu.com") + result2 := netutil.IsPingConnected("www.!@#&&&.com") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsTelnetConnected + +

Checks if can telnet the specified host or not.

+ +Signature: + +```go +func IsTelnetConnected(host string, port string) bool +``` + +Example:[Run](https://go.dev/play/p/yiLCGtQv_ZG) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/netutil" +) + +func main() { + result1 := netutil.IsTelnetConnected("www.baidu.com", "80") + result2 := netutil.IsTelnetConnected("www.baidu.com", "123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### BuildUrl + +

Builds a URL from the given params.

+ +Signature: + +```go +func BuildUrl(scheme, host, path string, query map[string][]string) (string, error) +``` + +Example:[Run](https://go.dev/play/p/JLXl1hZK7l4) + +```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 + // +} +``` + +### AddQueryParams + +

Adds query parameters to the given URL.

+ +Signature: + +```go +func AddQueryParams(urlStr string, params map[string][]string) (string, error) +``` + +Example:[Run](https://go.dev/play/p/JLXl1hZK7l4) + +```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 + // +} +``` diff --git a/docs/en/api/packages/pointer.md b/docs/en/api/packages/pointer.md new file mode 100644 index 00000000..2434d408 --- /dev/null +++ b/docs/en/api/packages/pointer.md @@ -0,0 +1,229 @@ +# Pointer + +pointer contains some util functions to operate go pointer. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/pointer/pointer.go](https://github.com/duke-git/lancet/blob/main/pointer/pointer.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/pointer" +) +``` + +
+ +## Index + +- [Of](#Of) +- [Unwrap](#Unwrap) +- [UnwrapOr](#UnwrapOr) +- [UnwrapOrDefault](#UnwrapOrDefault) +- [ExtractPointer](#ExtractPointer) + +
+ +## Documentation + +### Of + +

Returns a pointer to the pass value `v`.

+ +Signature: + +```go +func Of[T any](v T) *T +``` + +Example:[Run](https://go.dev/play/p/HFd70x4DrMj) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + result1 := pointer.Of(123) + result2 := pointer.Of("abc") + + fmt.Println(*result1) + fmt.Println(*result2) + + // Output: + // 123 + // abc +} +``` + + +### Unwrap + +

Returns the value from the pointer.

+ +Signature: + +```go +func Unwrap[T any](p *T) T +``` + +Example:[Run](https://go.dev/play/p/cgeu3g7cjWb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + result1 := pointer.Unwrap(&a) + result2 := pointer.Unwrap(&b) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 123 + // abc +} +``` + + +### UnwrapOr + +

Returns the value from the pointer or fallback if the pointer is nil.

+ +Signature: +```go +UnwrapOr[T any](p *T, fallback T) T +``` + +Example:[Run](https://go.dev/play/p/mmNaLC38W8C) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := pointer.UnwrapOr(&a, 456) + result2 := pointer.UnwrapOr(&b, "abc") + result3 := pointer.UnwrapOr(c, 456) + result4 := pointer.UnwrapOr(d, "def") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 456 + // def +} +``` + + +### UnwrapOrDefault + +

Returns the value from the pointer or the default value if the pointer is nil.

+ +Signature: +```go +UnwrapOrDefault[T any](p *T) T +``` + +Example:[Run](https://go.dev/play/p/ZnGIHf8_o4E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := pointer.UnwrapOrDefault(&a) + result2 := pointer.UnwrapOrDefault(&b) + result3 := pointer.UnwrapOrDefault(c) + result4 := pointer.UnwrapOrDefault(d) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 0 + // +} +``` + + +### ExtractPointer + +

Returns the underlying value by the given interface type

+ +Signature: + +```go +func ExtractPointer(value any) any +``` + +Example:[Run](https://go.dev/play/p/D7HFjeWU2ZP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/pointer" +) + +func main() { + a := 1 + b := &a + c := &b + d := &c + + result := pointer.ExtractPointer(d) + + fmt.Println(result) + + // Output: + // 1 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/random.md b/docs/en/api/packages/random.md new file mode 100644 index 00000000..9ef15926 --- /dev/null +++ b/docs/en/api/packages/random.md @@ -0,0 +1,553 @@ +# Random + +Package random implements some basic functions to generate random int and string. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/random" +) +``` + +
+ +## Index + +- [RandBytes](#RandBytes) +- [RandInt](#RandInt) +- [RandString](#RandString) +- [RandFromGivenSlice](#RandFromGivenSlice) +- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice) +- [RandUpper](#RandUpper) +- [RandLower](#RandLower) +- [RandNumeral](#RandNumeral) +- [RandNumeralOrLetter](#RandNumeralOrLetter) +- [RandSymbolChar](#RandSymbolChar) +- [UUIdV4](#UUIdV4) +- [RandIntSlice](#RandIntSlice) +- [RandUniqueIntSlice](#RandUniqueIntSlice) +- [RandFloat](#RandFloat) +- [RandFloats](#RandFloats) +- [RandStringSlice](#RandStringSlice) +- [RandBool](#RandBool) +- [RandBoolSlice](#RandBoolSlice) +- [RandNumberOfLength](#RandNumberOfLength) + +
+ +## Documentation + +### RandBytes + +

Generate random byte slice.

+ +Signature: + +```go +func RandBytes(length int) []byte +``` + +Example:[Run](https://go.dev/play/p/EkiLESeXf8d) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randBytes := random.RandBytes(4) + fmt.Println(randBytes) +} +``` + +### RandInt + +

Generate random int between min and max, may contain min, not max.

+ +Signature: + +```go +func RandInt(min, max int) int +``` + +Example:[Run](https://go.dev/play/p/pXyyAAI5YxD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + rInt := random.RandInt(1, 10) + fmt.Println(rInt) +} +``` + +### RandString + +

Generate random given length string. only contains letter (a-zA-Z)

+ +Signature: + +```go +func RandString(length int) string +``` + +Example:[Run](https://go.dev/play/p/W2xvRUXA7Mi) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandString(6) + fmt.Println(randStr) //pGWsze +} +``` + +### RandFromGivenSlice + +

Generate a random element from given slice.

+ +Signature: + +```go +func RandFromGivenSlice[T any](slice []T) T +``` + +Example:[Run](https://go.dev/play/p/UrkWueF6yYo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randomSet := []any{"a", 8, "hello", true, 1.1} + randElm := random.RandFromGivenSlice(randomSet) + fmt.Println(randElm) +} +``` + +### RandSliceFromGivenSlice + +

Generate a random slice of length num from given slice.

+ +Signature: + +```go +func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T +``` + +Example:[Run](https://go.dev/play/p/68UikN9d6VT) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange"} + + chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false) + + fmt.Println(chosen3goods) +} +``` + +### RandUpper + +

Generate a random upper case string

+ +Signature: + +```go +func RandUpper(length int) string +``` + +Example:[Run](https://go.dev/play/p/29QfOh0DVuh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandString(6) + fmt.Println(randStr) //PACWGF +} +``` + +### RandLower + +

Generate a random lower case string

+ +Signature: + +```go +func RandLower(length int) string +``` + +Example:[Run](https://go.dev/play/p/XJtZ471cmtI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandLower(6) + fmt.Println(randStr) //siqbew +} +``` + +### RandNumeral + +

Generate a random numeral string

+ +Signature: + +```go +func RandNumeral(length int) string +``` + +Example:[Run](https://go.dev/play/p/g4JWVpHsJcf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandNumeral(6) + fmt.Println(randStr) //035172 +} +``` + +### RandNumeralOrLetter + +

generate a random numeral or letter string

+ +Signature: + +```go +func RandNumeralOrLetter(length int) string +``` + +Example:[Run](https://go.dev/play/p/19CEQvpx2jD) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandNumeralOrLetter(6) + fmt.Println(randStr) //0aW7cQ +} +``` + +### RandSymbolChar + +

Generate a random symbol char of specified length.

+ +Signature: + +```go +func RandSymbolChar(length int) string +``` + +Example:[Run](https://go.dev/play/p/Im6ZJxAykOm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + randStr := random.RandSymbolChar(6) + fmt.Println(randStr) // random string like: @#(_") +} +``` + +### UUIdV4 + +

Generate a random UUID of version 4 according to RFC 4122.

+ +Signature: + +```go +func UUIdV4() (string, error) +``` + +Example:[Run](https://go.dev/play/p/_Z9SFmr28ft) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + uuid, err := random.UUIdV4() + if err != nil { + return + } + fmt.Println(uuid) +} +``` + +### RandIntSlice + +

Generate a slice of random int. Number range in [min, max)

+ +Signature: + +```go +func RandIntSlice(length, min, max int) []int +``` + +Example:[Run](https://go.dev/play/p/GATTQ5xTEG8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandIntSlice(5, 0, 10) + fmt.Println(result) //[1 4 7 1 5] (random) +} +``` + + +### RandUniqueIntSlice + +

Generate a slice of random int of length that do not repeat. Number range in [min, max)

+ +Signature: + +```go +func RandUniqueIntSlice(length, min, max int) []int +``` + +Example:[Run](https://go.dev/play/p/uBkRSOz73Ec) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandUniqueIntSlice(5, 0, 10) + fmt.Println(result) //[0 4 7 1 5] (random) +} +``` + +### RandFloat + +

Generate a random float64 number between [min, max) with specific precision.

+ +Signature: + +```go +func RandFloat(min, max float64, precision int) float64 +``` + +Example:[Run](https://go.dev/play/p/zbD_tuobJtr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + floatNumber := random.RandFloat(1.0, 5.0, 2) + fmt.Println(floatNumber) //2.14 (random number) +} +``` + +### RandFloats + +

Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)

+ +Signature: + +```go +func RandFloats(length int, min, max float64, precision int) []float64 +``` + +Example:[Run](https://go.dev/play/p/I3yndUQ-rhh) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + floatNumbers := random.RandFloats(5, 1.0, 5.0, 2) + fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random) +} +``` + + +### RandStringSlice + +

Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.

+ +Signature: + +```go +func RandStringSlice(charset string, sliceLen, strLen int) []string +``` + +Example:[Run](https://go.dev/play/p/2_-PiDv3tGn) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + strs := random.RandStringSlice(random.Letters, 4, 6) + fmt.Println(strs) + + // output random string slice like below: + //[CooSMq RUFjDz FAeMPf heRyGv] +} +``` + +### RandBool + +

Generate a random boolean value (true or false).

+ +Signature: + +```go +func RandBool() bool +``` + +Example:[Run](https://go.dev/play/p/to6BLc26wBv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandBool() + fmt.Println(result) // true or false (random) +} +``` + +### RandBoolSlice + +

Generates a random boolean slice of specified length.

+ +Signature: + +```go +func RandBoolSlice(length int) []bool +``` + +Example:[Run](https://go.dev/play/p/o-VSjPjnILI) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + result := random.RandBoolSlice(2) + fmt.Println(result) // [true false] (random) +} +``` + +### RandNumberOfLength + +

Generates a random int number of specified length.

+ +Signature: + +```go +func RandNumberOfLength(len int) int +``` + +Signature:[Run](https://go.dev/play/p/oyZbuV7bu7b) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/random" +) + +func main() { + i := random.RandNumberOfLength(2) + fmt.Println(i) // 21 (random number of length 2) +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/retry.md b/docs/en/api/packages/retry.md new file mode 100644 index 00000000..56392d6d --- /dev/null +++ b/docs/en/api/packages/retry.md @@ -0,0 +1,464 @@ +# Retry + +Package retry is for executing a function repeatedly until it was successful or canceled by the context. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/retry/retry.go](https://github.com/duke-git/lancet/blob/main/retry/retry.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/retry" +) +``` + +
+ +## Index + +- [Context](#Context) +- [Retry](#Retry) +- [RetryFunc](#RetryFunc) +- [RetryDuration](#RetryDuration) +- [RetryTimes](#RetryTimes) +- [BackoffStrategy](#BackoffStrategy) +- [RetryWithCustomBackoff](#RetryWithCustomBackoff) +- [RetryWithLinearBackoff](#RetryWithLinearBackoff) +- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff) + +
+ +## Documentation + +### Context + +

Set retry context config, can cancel the retry with context.

+ +Signature: + +```go +func Context(ctx context.Context) +``` + +Example:[Run](https://go.dev/play/p/xnAOOXv9GkS) + +```go +import ( + "context" + "errors" + "fmt" + "github.com/duke-git/lancet/v2/retry" + "time" +) + +func main() { + ctx, cancel := context.WithCancel(context.TODO()) + + number := 0 + increaseNumber := func() error { + number++ + if number > 3 { + cancel() + } + return errors.New("error occurs") + } + + duration := retry.RetryDuration(time.Microsecond*50) + + retry.Retry(increaseNumber, + duration, + retry.Context(ctx), + ) + + fmt.Println(number) + + // Output: + // 4 +} +``` + +### RetryFunc + +

Function that retry executes.

+ +Signature: + +```go +type RetryFunc func() error +``` + +Example:[Run](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + var increaseNumber retry.RetryFunc = func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + duration := retry.RetryDuration(time.Microsecond*50) + + err := retry.Retry(increaseNumber, duration) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### RetryTimes + +

Set times of retry. Default times is 5.

+ +Signature: + +```go +func RetryTimes(n uint) +``` + +Example:[Run](https://go.dev/play/p/ssfVeU2SwLO) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryTimes(2)) + if err != nil { + fmt.Println(err) + } + + // Output: + // function main.main.func1 run failed after 2 times retry +} +``` + +### RetryDuration + +

Set duration of retries. Default duration is 3 second.

+ +Signature: + +```go +func RetryDuration(d time.Duration) +``` + +Example:[Run](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + duration := retry.RetryDuration(time.Microsecond*50) + + err := retry.Retry(increaseNumber, duration) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### Retry + +

Executes the retryFunc repeatedly until it was successful or canceled by the context.

+ +Signature: + +```go +func Retry(retryFunc RetryFunc, opts ...Option) error +``` + +Example:[Run](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + duration := retry.RetryDuration(time.Microsecond*50) + + err := retry.Retry(increaseNumber, duration) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + + +### BackoffStrategy + +

An interface that defines a method for calculating backoff intervals.

+ +Signature: + +```go +// BackoffStrategy is an interface that defines a method for calculating backoff intervals. +type BackoffStrategy interface { + // CalculateInterval returns the time.Duration after which the next retry attempt should be made. + CalculateInterval() time.Duration +} +``` + +Example: + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +type ExampleCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50})) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + +### RetryWithCustomBackoff + +

Set abitary custom backoff strategy.

+ +Signature: + +```go +func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option +``` + +Example:[Run](https://go.dev/play/p/jIm_o2vb5Y4) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +type ExampleCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50})) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + + +### RetryWithLinearBackoff + +

Set linear strategy backoff.

+ +Signature: + +```go +func RetryWithLinearBackoff(interval time.Duration) Option +``` + +Example:[Run](https://go.dev/play/p/nk2XRmagfVF) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` + + +### RetryWithExponentialWithJitterBackoff + +

Set exponential strategy backoff.

+ +Signature: + +```go +func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option +``` + +Example:[Run](https://go.dev/play/p/xp1avQmn16X) + +```go +package main + +import ( + "fmt" + "errors" + "log" + "github.com/duke-git/lancet/v2/retry" +) + +func main() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} +``` diff --git a/docs/en/api/packages/slice.md b/docs/en/api/packages/slice.md new file mode 100644 index 00000000..243981c7 --- /dev/null +++ b/docs/en/api/packages/slice.md @@ -0,0 +1,3160 @@ +# Slice + +Package slice implements some functions to manipulate slice. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go) +- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/slice" +) +``` + +
+ +## Index + +- [AppendIfAbsent](#AppendIfAbsent) +- [Contain](#Contain) +- [ContainBy](#ContainBy) +- [ContainSubSlice](#ContainSubSlice) +- [Chunk](#Chunk) +- [Compact](#Compact) +- [Concat](#Concat) +- [Count](#Count) +- [CountBy](#CountBy) +- [Difference](#Difference) +- [DifferenceBy](#DifferenceBy) +- [DifferenceWith](#DifferenceWith) +- [DeleteAt](#DeleteAt) +- [DeleteRange](#DeleteRange) +- [Drop](#Drop) +- [DropRight](#DropRight) +- [DropWhile](#DropWhile) +- [DropRightWhile](#DropRightWhile) +- [Equal](#Equal) +- [EqualWith](#EqualWith) +- [EqualUnordered](#EqualUnordered) +- [Every](#Every) +- [Filter](#Filter) +- [FilterConcurrent](#FilterConcurrent) +- [Finddeprecated](#Find) +- [FindBy](#FindBy) +- [FindLastdeprecated](#FindLast) +- [FindLastBy](#FindLastBy) +- [Flatten](#Flatten) +- [FlattenDeep](#FlattenDeep) +- [ForEach](#ForEach) +- [ForEachConcurrent](#ForEachConcurrent) +- [ForEachWithBreak](#ForEachWithBreak) +- [GroupBy](#GroupBy) +- [GroupWith](#GroupWith) +- [IntSlicedeprecated](#IntSlice) +- [InterfaceSlicedeprecated](#InterfaceSlice) +- [Intersection](#Intersection) +- [InsertAt](#InsertAt) +- [IndexOf](#IndexOf) +- [LastIndexOf](#LastIndexOf) +- [Map](#Map) +- [MapConcurrent](#MapConcurrent) +- [FilterMap](#FilterMap) +- [FlatMap](#FlatMap) +- [Merge](#Merge) +- [Reverse](#Reverse) +- [ReverseCopy](#ReverseCopy) +- [Reducedeprecated](#Reduce) +- [ReduceConcurrent](#ReduceConcurrent) +- [ReduceBy](#ReduceBy) +- [ReduceRight](#ReduceRight) +- [Replace](#Replace) +- [ReplaceAll](#ReplaceAll) +- [Repeat](#Repeat) +- [Shuffle](#Shuffle) +- [ShuffleCopy](#ShuffleCopy) +- [IsAscending](#IsAscending) +- [IsDescending](#IsDescending) +- [IsSorted](#IsSorted) +- [IsSortedByKey](#IsSortedByKey) +- [Sort](#Sort) +- [SortBy](#SortBy) +- [SortByField](#SortByField) +- [Some](#Some) +- [StringSlicedeprecated](#StringSlice) +- [SymmetricDifference](#SymmetricDifference) +- [ToSlice](#ToSlice) +- [ToSlicePointer](#ToSlicePointer) +- [Unique](#Unique) +- [UniqueBy](#UniqueBy) +- [UniqueByComparator](#UniqueByComparator) +- [UniqueByField](#UniqueByField) +- [UniqueByConcurrent](#UniqueByConcurrent) +- [Union](#Union) +- [UnionBy](#UnionBy) +- [UpdateAt](#UpdateAt) +- [Without](#Without) +- [KeyBy](#KeyBy) +- [Join](#Join) +- [Partition](#Partition) +- [SetToDefaultIf](#SetToDefaultIf) +- [Break](#Break) +- [RightPadding](#RightPadding) +- [LeftPadding](#LeftPadding) +- [Frequency](#Frequency) +- [JoinFunc](#JoinFunc) +- [ConcatBy](#ConcatBy) + + + +
+ +## Documentation + +### AppendIfAbsent + +

If slice doesn't contain the item, append it to the slice.

+ +Signature: + +```go +func AppendIfAbsent[T comparable](slice []T, item T) []T +``` + +Example:[Run](https://go.dev/play/p/GNdv7Jg2Taj) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.AppendIfAbsent([]string{"a", "b"}, "b") + result2 := slice.AppendIfAbsent([]string{"a", "b"}, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b] + // [a b c] +} +``` + +### Contain + +

Check if the target value is in the slice or not.

+ +Signature: + +```go +func Contain[T comparable](slice []T, target T) bool +``` + +Example:[Run](https://go.dev/play/p/_454yEHcNjf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Contain([]string{"a", "b", "c"}, "a") + result2 := slice.Contain([]int{1, 2, 3}, 4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### ContainBy + +

returns true if predicate function return true.

+ +Signature: + +```go +func ContainBy[T any](slice []T, predicate func(item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/49tkHfX4GNc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type foo struct { + A string + B int + } + + array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}} + result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 }) + result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 }) + + array2 := []string{"a", "b", "c"} + result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" }) + result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // false + // true + // false +} +``` + +### ContainSubSlice + +

Check if the slice contain subslice or not.

+ +Signature: + +```go +func ContainSubSlice[T comparable](slice, subSlice []T) bool +``` + +Example:[Run](https://go.dev/play/p/bcuQ3UT6Sev) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"}) + result2 := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### Chunk + +

Creates an slice of elements split into groups the length of `size`.

+ +Signature: + +```go +func Chunk[T any](slice []T, size int) [][]T +``` + +Example:[Run](https://go.dev/play/p/b4Pou5j2L_C) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arr := []string{"a", "b", "c", "d", "e"} + + result1 := slice.Chunk(arr, 1) + result2 := slice.Chunk(arr, 2) + result3 := slice.Chunk(arr, 3) + result4 := slice.Chunk(arr, 4) + result5 := slice.Chunk(arr, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [[a] [b] [c] [d] [e]] + // [[a b] [c d] [e]] + // [[a b c] [d e]] + // [[a b c d] [e]] + // [[a b c d e]] +} +``` + +### Compact + +

Creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.

+ +Signature: + +```go +func Compact[T comparable](slice []T) []T +``` + +Example:[Run](https://go.dev/play/p/pO5AnxEr3TK) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Compact([]int{0}) + result2 := slice.Compact([]int{0, 1, 2, 3}) + result3 := slice.Compact([]string{"", "a", "b", "0"}) + result4 := slice.Compact([]bool{false, true, true}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [] + // [1 2 3] + // [a b 0] + // [true true] +} +``` + +### Concat + +

Concat creates a new slice concatenating slice with any additional slices.

+ +Signature: + +```go +func Concat[T any](slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/gPt-q7zr5mk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Concat([]int{1, 2}, []int{3, 4}) + result2 := slice.Concat([]string{"a", "b"}, []string{"c"}, []string{"d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [1 2 3 4] + // [a b c d] +} +``` + +### Count + +

Returns the number of occurrences of the given item in the slice.

+ +Signature: + +```go +func Count[T comparable](slice []T, item T) int +``` + +Example:[Run](https://go.dev/play/p/Mj4oiEnQvRJ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 3, 4} + + result1 := slice.Count(nums, 1) + result2 := slice.Count(nums, 3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 2 +} +``` + +### CountBy + +

Iterates over elements of slice with predicate function, returns the number of all matched elements.

+ +Signature: + +```go +func CountBy[T any](slice []T, predicate func(index int, item T) bool) int +``` + +Example:[Run](https://go.dev/play/p/tHOccTMDZCC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.CountBy(nums, isEven) + + fmt.Println(result) + + // Output: + // 2 +} +``` + +### Difference + +

Creates an slice of whose element not included in the other given slice.

+ +Signature: + +```go +func Difference[T comparable](slice, comparedSlice []T) []T +``` + +Example:[Run](https://go.dev/play/p/VXvadzLzhDa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{4, 5, 6} + + result := slice.Difference(s1, s2) + + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### DifferenceBy + +

DifferenceBy accepts iteratee func which is invoked for each element of slice and values to generate the criterion by which they're compared.

+ +Signature: + +```go +func DifferenceBy[T comparable](slice []T, comparedSlice []T, iteratee func(index int, item T) T) []T +``` + +Example:[Run](https://go.dev/play/p/DiivgwM5OnC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{3, 4, 5} + + addOne := func(i int, v int) int { + return v + 1 + } + + result := slice.DifferenceBy(s1, s2, addOne) + + fmt.Println(result) + + // Output: + // [1 2] +} +``` + +### DifferenceWith + +

DifferenceWith accepts comparator which is invoked to compare elements of slice to values. The order and references of result values are determined by the first slice.

+ +Signature: + +```go +func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(value, otherValue T) bool) []T +``` + +Example:[Run](https://go.dev/play/p/v2U2deugKuV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{4, 5, 6, 7, 8} + + isDouble := func(v1, v2 int) bool { + return v2 == 2*v1 + } + + result := slice.DifferenceWith(s1, s2, isDouble) + + fmt.Println(result) + + // Output: + // [1 5] +} +``` + +### DeleteAt + +

Delete delete the element of slice at index.

+ +Signature: + +```go +func DeleteAt[T any](slice []T, index int) +``` + +Example:[Run](https://go.dev/play/p/800B1dPBYyd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := slice.DeleteAt(chars, 0) + result2 := slice.DeleteAt(chars, 4) + result3 := slice.DeleteAt(chars, 5) + result4 := slice.DeleteAt(chars, 6) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [b c d e] + // [a b c d] + // [a b c d] + // [a b c d] +} +``` + +### DeleteRange + +

Delete the element of slice from start index to end index(exclude)

+ +Signature: + +```go +func DeleteRange[T any](slice []T, start, end int) []T +``` + +Example:[Run](https://go.dev/play/p/945HwiNrnle) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := slice.DeleteRange(chars, 0, 0) + result2 := slice.DeleteRange(chars, 0, 1) + result3 := slice.DeleteRange(chars, 0, 3) + result4 := slice.DeleteRange(chars, 0, 4) + result5 := slice.DeleteRange(chars, 0, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c d e] + // [b c d e] + // [d e] + // [e] + // [] + +} +``` + +### Drop + +

Drop n elements from the start of a slice.

+ +Signature: + +```go +func Drop[T any](slice []T, n int) []T +``` + +Example:[Run](https://go.dev/play/p/jnPO2yQsT8H) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.Drop([]string{"a", "b", "c"}, 0) + result2 := slice.Drop([]string{"a", "b", "c"}, 1) + result3 := slice.Drop([]string{"a", "b", "c"}, -1) + result4 := slice.Drop([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [b c] + // [a b c] + // [] +} +``` + +### DropRight + +

Drop n elements from the end of a slice.

+ +Signature: + +```go +func DropRight[T any](slice []T, n int) []T +``` + +Example:[Run](https://go.dev/play/p/8bcXvywZezG) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.DropRight([]string{"a", "b", "c"}, 0) + result2 := slice.DropRight([]string{"a", "b", "c"}, 1) + result3 := slice.DropRight([]string{"a", "b", "c"}, -1) + result4 := slice.DropRight([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [a b] + // [a b c] + // [] +} +``` + +### DropWhile + +

Drop n elements from the start of a slice while predicate function returns true.

+ +Signature: + +```go +func DropWhile[T any](slice []T, predicate func(item T) bool) []T +``` + +Example:[Run](https://go.dev/play/p/4rt252UV_qs) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.DropWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := slice.DropWhile(numbers, func(n int) bool { + return true + }) + result3 := slice.DropWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [2 3 4 5] + // [] + // [1 2 3 4 5] +} +``` + +### DropRightWhile + +

Drop n elements from the end of a slice while predicate function returns true.

+ +Signature: + +```go +func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T +``` + +Example:[Run](https://go.dev/play/p/6wyK3zMY56e) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 2, 3, 4, 5} + + result1 := slice.DropRightWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := slice.DropRightWhile(numbers, func(n int) bool { + return true + }) + result3 := slice.DropRightWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [1 2] + // [] + // [1 2 3 4 5] +} +``` + +### Equal + +

Check if two slices are equal: the same length and all elements' order and value are equal.

+ +Signature: + +```go +func Equal[T comparable](slice1, slice2 []T) bool +``` + +Example:[Run](https://go.dev/play/p/WcRQJ37ifPa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3} + s2 := []int{1, 2, 3} + s3 := []int{1, 3, 2} + + result1 := slice.Equal(s1, s2) + result2 := slice.Equal(s1, s3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### EqualWith + +

Check if two slices are equal with comparator func.

+ +Signature: + +```go +func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) bool +``` + +Example:[Run](https://go.dev/play/p/b9iygtgsHI1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + s1 := []int{1, 2, 3} + s2 := []int{2, 4, 6} + + isDouble := func(a, b int) bool { + return b == a*2 + } + + result := slice.EqualWith(s1, s2, isDouble) + + fmt.Println(result) + + // Output: + // true +} +``` + +### EqualUnordered + +

Checks if two slices are equal: the same length and all elements value are equal (unordered).

+ +Signature: + +```go +func EqualUnordered[T comparable](slice1, slice2 []T) bool +``` + +Example:[运行](https://go.dev/play/p/n8fSc2w8ZgX) + +```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 +} +``` + +### Every + +

Return true if all of the values in the slice pass the predicate function.

+ +Signature: + +```go +func Every[T any](slice []T, predicate func(index int, item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/R8U6Sl-j8cD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Every(nums, isEven) + + fmt.Println(result) + + // Output: + // false +} +``` + +### Filter + +

Return all elements which match the function.

+ +Signature: + +```go +func Filter[T any](slice []T, predicate func(index int, item T) bool) []T +``` + +Example:[Run](https://go.dev/play/p/SdPna-7qK4T) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Filter(nums, isEven) + + fmt.Println(result) + + // Output: + // [2 4] +} +``` + +### FilterConcurrent + +

Applies the provided filter function `predicate` to each element of the input slice concurrently.

+ +Signature: + +```go +func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T +``` + +Example:[Run](https://go.dev/play/p/t_pkwerIRVx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.FilterConcurrent(nums, isEven, 2) + + fmt.Println(result) + + // Output: + // [2 4] +} +``` + +### Find + +

Iterates over elements of slice, returning the first one that passes a truth test on function.

+ +> ⚠️ This function is deprecated. use `FindBy` instead. + +Signature: + +```go +func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) +``` + +Example:[Run](https://go.dev/play/p/CBKeBoHVLgq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.Find(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 2 + // true +} +``` + +### FindBy + +

Iterates over elements of slice, returning the first one that passes a truth test on predicate function.If return T is nil or zero value then no items matched the predicate func. In contrast to Find or FindLast, its return value no longer requires dereferencing.

+ +Signature: + +```go +func FindBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) +``` + +Example:[Run](https://go.dev/play/p/n1lysBYl-GB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 2 + // true +} +``` + +### FindLast + +

iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.

+ +> ⚠️ This function is deprecated. use `FindLastBy` instead. + +Signature: + +```go +func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) +``` + +Example:[Run](https://go.dev/play/p/FFDPV_j7URd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindLast(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### FindLastBy + +

FindLastBy iterates over elements of slice, returning the last one that passes a truth test on predicate function. If return T is nil or zero value then no items matched the predicate func. In contrast to Find or FindLast, its return value no longer requires dereferencing.

+ +Signature: + +```go +func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) +``` + +Example:[Run](https://go.dev/play/p/8iqomzyCl_s) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := slice.FindLastBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### Flatten + +

Flatten slice with one level.

+ +Signature: + +```go +func Flatten(slice any) any +``` + +Example:[Run](https://go.dev/play/p/hYa3cBEevtm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := slice.Flatten(arrs) + + fmt.Println(result) + + // Output: + // [[a b] [c d]] +} +``` + +### FlattenDeep + +

flattens slice recursive.

+ +Signature: + +```go +func FlattenDeep(slice any) any +``` + +Example:[Run](https://go.dev/play/p/yjYNHPyCFaF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := slice.FlattenDeep(arrs) + + fmt.Println(result) + + // Output: + // [a b c d] +} +``` + +### ForEach + +

Iterates over elements of slice and invokes function for each element.

+ +Signature: + +```go +func ForEach[T any](slice []T, iteratee func(index int, item T)) +``` + +Example:[Run](https://go.dev/play/p/DrPaa4YsHRF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + var result []int + addOne := func(_ int, v int) { + result = append(result, v+1) + } + + slice.ForEach(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} +``` + +### ForEachConcurrent + +

Applies the iteratee function to each item in the slice concurrently.

+ +Signature: + +```go +func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) +``` + +Example:[Run](https://go.dev/play/p/kT4XW7DKVoV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8} + + result := make([]int, len(nums)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + slice.ForEachConcurrent(nums, addOne, 4) + + fmt.Println(result) + + // Output: + // [2 3 4 5 6 7 8 9] +} +``` + +### ForEachWithBreak + +

Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.

+ +Signature: + +```go +func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) +``` + +Example:[Run](https://go.dev/play/p/qScs39f3D9W) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 2, 3, 4, 5} + + var sum int + + slice.ForEachWithBreak(numbers, func(_, n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + fmt.Println(sum) + + // Output: + // 6 +} +``` + +### GroupBy + +

Iterates over elements of the slice, each element will be group by criteria, returns two slices.

+ +Signature: + +```go +func GroupBy[T any](slice []T, groupFn func(index int, item T) bool) ([]T, []T) +``` + +Example:[Run](https://go.dev/play/p/QVkPxzPR0iA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + even, odd := slice.GroupBy(nums, isEven) + + fmt.Println(even) + fmt.Println(odd) + + // Output: + // [2 4] + // [1 3 5] +} +``` + +### GroupWith + +

Return a map composed of keys generated from the results of running each element of slice thru iteratee.

+ +Signature: + +```go +func GroupWith[T any, U comparable](slice []T, iteratee func(T) U) map[U][]T +``` + +Example:[Run](https://go.dev/play/p/ApCvMNTLO8a) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []float64{6.1, 4.2, 6.3} + + floor := func(num float64) float64 { + return math.Floor(num) + } + + result := slice.GroupWith(nums, floor) //map[float64][]float64 + + fmt.Println(result) + + // Output: + // map[4:[4.2] 6:[6.1 6.3]] +} +``` + +### IntSlice + +

Convert interface slice to int slice.

+ +> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement. + +Signature: + +```go +func IntSlice(slice any) []int +``` + +Example:[Run](https://go.dev/play/p/UQDj-on9TGN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []interface{}{1, 2, 3} + + result := slice.IntSlice(nums) //[]int{1, 2, 3} + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### InterfaceSlice + +

Convert value to interface slice.

+ +> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement. + +Signature: + +```go +func InterfaceSlice(slice any) []any +``` + +Example:[Run](https://go.dev/play/p/FdQXF0Vvqs-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c"} + + result := slice.InterfaceSlice(strs) //[]interface{}{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### Intersection + +

Creates a slice of unique values that included by all slices.

+ +Signature: + +```go +func Intersection[T comparable](slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/anJXfB5wq_t) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{2, 3, 4} + + result := slice.Intersection(nums1, nums2) + + fmt.Println(result) + + // Output: + // [2 3] +} +``` + +### InsertAt + +

insert the element into slice at index.

+ +Signature: + +```go +func InsertAt[T any](slice []T, index int, value any) []T +``` + +Example:[Run](https://go.dev/play/p/hMLNxPEGJVE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.InsertAt([]string{"a", "b", "c"}, 0, "1") + result2 := slice.InsertAt([]string{"a", "b", "c"}, 1, "1") + result3 := slice.InsertAt([]string{"a", "b", "c"}, 2, "1") + result4 := slice.InsertAt([]string{"a", "b", "c"}, 3, "1") + result5 := slice.InsertAt([]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [1 a b c] + // [a 1 b c] + // [a b 1 c] + // [a b c 1] + // [1 2 3 a b c] +} +``` + +### IndexOf + +

Returns the index at which the first occurrence of a item is found in a slice or return -1 if the item cannot be found.

+ +Signature: + +```go +func IndexOf[T comparable](slice []T, item T) int +``` + +Example:[Run](https://go.dev/play/p/MRN1f0FpABb) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "a", "b", "c"} + + result1 := slice.IndexOf(strs, "a") + result2 := slice.IndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} +``` + +### LastIndexOf + +

Returns the index at which the last occurrence of a item is found in a slice or return -1 if the item cannot be found.

+ +Signature: + +```go +func LastIndexOf[T comparable](slice []T, item T) int +``` + +Example:[Run](https://go.dev/play/p/DokM4cf1IKH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "a", "b", "c"} + + result1 := slice.LastIndexOf(strs, "a") + result2 := slice.LastIndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // -1 +} +``` + +### Map + +

Creates an slice of values by running each element in slice thru function.

+ +Signature: + +```go +func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U +``` + +Example:[Run](https://go.dev/play/p/biaTefqPquw) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + addOne := func(_ int, v int) int { + return v + 1 + } + + result := slice.Map(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} +``` + +### MapConcurrent + +

Applies the iteratee function to each item in the slice by concrrent.

+ +Signature: + +```go +func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U +``` + +Example:[Run](https://go.dev/play/p/H1ehfPkPen0) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6} + + result := slice.MapConcurrent(nums, func(_, n int) int { return n * n }, 4) + + fmt.Println(result) + + // Output: + // [1 4 9 16 25 36] +} +``` + +### FilterMap + +

Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.

+ +Signature: + +```go +func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U +``` + +Example:[Run](https://go.dev/play/p/J94SZ_9MiIe) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + getEvenNumStr := func(i, num int) (string, bool) { + if num%2 == 0 { + return strconv.FormatInt(int64(num), 10), true + } + return "", false + } + + result := slice.FilterMap(nums, getEvenNumStr) + + fmt.Printf("%#v", result) + + // Output: + // []string{"2", "4"} +} +``` + +### FlatMap + +

Manipulates a slice and transforms and flattens it to a slice of another type.

+ +Signature: + +```go +func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U +``` + +Example:[Run](https://go.dev/play/p/_QARWlWs1N_F) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4} + + result := slice.FlatMap(nums, func(i int, num int) []string { + s := "hi-" + strconv.FormatInt(int64(num), 10) + return []string{s} + }) + + fmt.Printf("%#v", result) + + // Output: + // []string{"hi-1", "hi-2", "hi-3", "hi-4"} +} +``` + +### Merge + +

Merge all given slices into one slice.

+ +> ⚠️ This function is deprecated. use `Concat` instead. + +Signature: + +```go +func Merge[T any](slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/lbjFp784r9N) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{3, 4} + + result := slice.Merge(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 2 3 3 4] +} +``` + +### Reverse + +

Reverse the elements order in slice.

+ +Signature: + +```go +func Reverse[T any](slice []T) +``` + +Example:[Run](https://go.dev/play/p/8uI8f1lwNrQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c", "d"} + + slice.Reverse(strs) + + fmt.Println(strs) + + // Output: + // [d c b a] +} +``` + +### ReverseCopy + +

Return a new slice of element order is reversed to the given slice.

+ +Signature: + +```go +func ReverseCopy[T any](slice []T) []T +``` + +Example:[Run](https://go.dev/play/p/c9arEaP7Cg-) + +```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] +} +``` + +### Reduce + +

Reduce slice.

+ +> ⚠️ This function is deprecated. use `ReduceBy` instead. + +Signature: + +```go +func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T +``` + +Example:[Run](https://go.dev/play/p/_RfXJJWIsIm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3} + + sum := func(_ int, v1, v2 int) int { + return v1 + v2 + } + + result := slice.Reduce(nums, sum, 0) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### ReduceConcurrent + +

Reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.

+ +Signature: + +```go +func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T +``` + +Example:[运行](https://go.dev/play/p/Tjwe6OtaG07) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 1) + + fmt.Println(result) + + // Output: + // 55 +} +``` + + +### ReduceBy + +

Produces a value from slice by accumulating the result of each element as passed through the reducer function.

+ +Signature: + +```go +func ReduceBy[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U +``` + +Example:[Run](https://go.dev/play/p/YKDpLi7gtee) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int { + return agg + item + }) + + result2 := slice.ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1234 +} +``` + +### ReduceRight + +

ReduceRight is like ReduceBy, but it iterates over elements of slice from right to left.

+ +Signature: + +```go +func ReduceRight[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U +``` + +Example:[Run](https://go.dev/play/p/qT9dZC03A1K) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ReduceRight([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result) + + // Output: + // 4321 +} +``` + +### Replace + +

Returns a copy of the slice with the first n non-overlapping instances of old replaced by new.

+ +Signature: + +```go +func Replace[T comparable](slice []T, old T, new T, n int) []T +``` + +Example:[Run](https://go.dev/play/p/P5mZp7IhOFo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "c", "a"} + + result1 := slice.Replace(strs, "a", "x", 0) + result2 := slice.Replace(strs, "a", "x", 1) + result3 := slice.Replace(strs, "a", "x", 2) + result4 := slice.Replace(strs, "a", "x", 3) + result5 := slice.Replace(strs, "a", "x", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c a] + // [x b c a] + // [x b c x] + // [x b c x] + // [x b c x] +} +``` + +### ReplaceAll + +

Returns a copy of the slice with the first n non-overlapping instances of old replaced by new.

+ +Signature: + +```go +func ReplaceAll[T comparable](slice []T, old T, new T) []T +``` + +Example:[Run](https://go.dev/play/p/CzqXMsuYUrx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ReplaceAll([]string{"a", "b", "c", "a"}, "a", "x") + + fmt.Println(result) + + // Output: + // [x b c x] +} +``` + +### Repeat + +

Creates a slice with length n whose elements are passed param item.

+ +Signature: + +```go +func Repeat[T any](item T, n int) []T +``` + +Example:[Run](https://go.dev/play/p/1CbOmtgILUU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Repeat("a", 3) + + fmt.Println(result) + + // Output: + // [a a a] +} +``` + +### Shuffle + +

Creates an slice of shuffled values.

+ +Signature: + +```go +func Shuffle[T any](slice []T) []T +``` + +Example:[Run](https://go.dev/play/p/YHvhnWGU3Ge) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + result := slice.Shuffle(nums) + + fmt.Println(res) + + // Output: + // [3 1 5 4 2] (random order) +} +``` + +### ShuffleCopy + +

Return a new slice with elements shuffled.

+ +Signature: + +```go +func ShuffleCopy[T any](slice []T) []T +``` + +Example:[Run](https://go.dev/play/p/vqDa-Gs1vT0) + +```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] +} +``` + +### IsAscending + +

Checks if a slice is ascending order.

+ +Signature: + +```go +func IsAscending[T constraints.Ordered](slice []T) bool +``` + +Example:[Run](https://go.dev/play/p/9CtsFjet4SH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsAscending([]int{1, 2, 3, 4, 5}) + result2 := slice.IsAscending([]int{5, 4, 3, 2, 1}) + result3 := slice.IsAscending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsDescending + +

Checks if a slice is descending order.

+ +Signature: + +```go +func IsDescending[T constraints.Ordered](slice []T) bool +``` + +Example:[Run](https://go.dev/play/p/U_LljFXma14) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsDescending([]int{5, 4, 3, 2, 1}) + result2 := slice.IsDescending([]int{1, 2, 3, 4, 5}) + result3 := slice.IsDescending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsSorted + +

Checks if a slice is sorted (ascending or descending).

+ +Signature: + +```go +func IsSorted[T constraints.Ordered](slice []T) bool +``` + +Example:[Run](https://go.dev/play/p/nCE8wPLwSA-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsSorted([]int{5, 4, 3, 2, 1}) + result2 := slice.IsSorted([]int{1, 2, 3, 4, 5}) + result3 := slice.IsSorted([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsSortedByKey + +

Checks if a slice is sorted by iteratee function.

+ +Signature: + +```go +func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool +``` + +Example:[Run](https://go.dev/play/p/tUoGB7DOHI4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int { + return len(s) + }) + result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int { + return len(s) + }) + result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int { + return len(s) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### Sort + +

Sorts a slice of any ordered type(number or string), use quick sort algrithm. Default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`. Ordered type: number(all ints uints floats) or string.

+ +Signature: + +```go +func Sort[T constraints.Ordered](slice []T, sortOrder ...string) +``` + +Example:[Run](https://go.dev/play/p/V9AVjzf_4Fk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 4, 3, 2, 5} + + slice.Sort(numbers) + fmt.Println(numbers) // [1 2 3 4 5] + + slice.Sort(numbers, "desc") + fmt.Println(numbers) // [5 4 3 2 1] + + strings := []string{"a", "d", "c", "b", "e"} + + slice.Sort(strings) + fmt.Println(strings) //[a b c d e} + + slice.Sort(strings, "desc") + fmt.Println(strings) //[e d c b a] +} +``` + +### SortBy + +

Sorts the slice in ascending order as determined by the less function. This sort is not guaranteed to be stable.

+ +Signature: + +```go +func SortBy[T any](slice []T, less func(a, b T) bool) +``` + +Example:[Run](https://go.dev/play/p/DAhLQSZEumm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + numbers := []int{1, 4, 3, 2, 5} + + slice.SortBy(numbers, func(a, b int) bool { + return a < b + }) + fmt.Println(numbers) // [1 2 3 4 5] + + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + slice.SortBy(users, func(a, b User) bool { + return a.Age < b.Age + }) + + fmt.Printf("sort users by age: %v", users) + + // output + // [{b 15} {a 21} {c 100}] +} +``` + +### SortByField + +

Sort struct slice by field. Slice element should be struct, `field` param type should be int, uint, string, or bool. Default sort type is ascending (asc), if descending order, set `sortType` param to desc.

+ +Signature: + +```go +func SortByField(slice any, field string, sortType ...string) error +``` + +Example:[Run](https://go.dev/play/p/fU1prOBP9p1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + err := slice.SortByField(users, "Age", "desc") + if err != nil { + return + } + + fmt.Println(users) + + // Output: + // [{c 100} {a 21} {b 15}] +} +``` + +### Some + +

Return true if any of the values in the list pass the predicate function.

+ +Signature: + +```go +func Some[T any](slice []T, predicate func(index int, item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/4pO9Xf9NDGS) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := slice.Some(nums, isEven) + + fmt.Println(result) + + // Output: + // true +} +``` + +### StringSlice + +

Convert interface slice to string slice.

+ +> ⚠️ This function is deprecated. use generic feature of go1.18+ for replacement + +Signature: + +```go +func StringSlice(slice any) []string +``` + +Example:[Run](https://go.dev/play/p/W0TZDWCPFcI) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []interface{}{"a", "b", "c"} + + result := slice.StringSlice(strs) //[]string{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### SymmetricDifference + +

Create a slice whose element is in given slices, but not in both slices.

+ +Signature: + +```go +func SymmetricDifference[T comparable](slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/1CbOmtgILUU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 2, 3} + nums2 := []int{1, 2, 4} + + result := slice.SymmetricDifference(nums1, nums2) + + fmt.Println(result) + + // Output: + // [3 4] +} +``` + +### ToSlice + +

Creates a slice of give items.

+ +Signature: + +```go +func ToSlice[T any](items ...T) []T +``` + +Example:[Run](https://go.dev/play/p/YzbzVq5kscN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.ToSlice("a", "b", "c") + + fmt.Println(result) + + // Output: + // [a b c] +} +``` + +### ToSlicePointer + +

Returns a pointer to the slices of a variable parameter transformation

+ +Signature: + +```go +func ToSlicePointer[T any](items ...T) []*T +``` + +Example:[Run](https://go.dev/play/p/gx4tr6_VXSF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + str1 := "a" + str2 := "b" + + result := slice.ToSlicePointer(str1, str2) + + expect := []*string{&str1, &str2} + + isEqual := reflect.DeepEqual(result, expect) + + fmt.Println(isEqual) + + // Output: + // true +} +``` + +### Unique + +

Remove duplicate elements in slice.

+ +Signature: + +```go +func Unique[T comparable](slice []T) []T +``` + +Example:[Run](https://go.dev/play/p/AXw0R3ZTE6a) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Unique([]string{"a", "a", "b"}) + fmt.Println(result) + + // Output: + // [a b] +} +``` + +### UniqueBy + +

Removes duplicate elements from the input slice based on the values returned by the iteratee function. this function maintains the order of the elements.

+ +Signature: + +```go +func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T +``` + +Example:[Run](https://go.dev/play/p/GY7JE4yikrl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5, 6} + result := slice.UniqueBy(nums, func(val int) int { + return val % 3 + }) + + fmt.Println(result) + + // Output: + // [1 2 3] +} +``` + +### UniqueByComparator + +

Removes duplicate elements from the input slice using the provided comparator function. The function maintains the order of the elements.

+ +Signature: + +```go +func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T +``` + +Example:[Run](https://go.dev/play/p/rwSacr-ZHsR) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool { + return item == other + }) + + caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool { + return strings.ToLower(item) == strings.ToLower(other) + }) + + fmt.Println(uniqueNums) + fmt.Println(caseInsensitiveStrings) + + // Output: + // [1 2 3 4 5 6] + // [apple banana cherry date] +} +``` + +### UniqueByConcurrent + +

Removes duplicate elements from the slice by parallel.

+ +Signature: + +```go +func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T +``` + +Example:[Runs](https://go.dev/play/p/wXZ7LcYRMGL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7} + comparator := func(item int, other int) bool { return item == other } + + result := slice.UniqueByConcurrent(nums,comparator, 4) + + fmt.Println(result) + // Output: + // [1 2 3 4 5 6 7] +} +``` + +### UniqueByField + +

Remove duplicate elements in struct slice by struct field.

+ +Signature: + +```go +func UniqueByField[T any](slice []T, field string) ([]T, error) +``` + +Example:[Runs](https://go.dev/play/p/6cifcZSPIGu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/slice" +) + +func main() { + type User struct { + ID int `json:"id"` + Name string `json:"name"` + } + + users := []User{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "b"}, + {ID: 1, Name: "c"}, + } + + result, err := slice.UniqueByField(users, "ID") + if err != nil { + } + + fmt.Println(result) + + // Output: + // [{1 a} {2 b}] +} +``` + +### Union + +

Creates a slice of unique values, in order, from all given slices. using == for equality comparisons.

+ +Signature: + +```go +func Union[T comparable](slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/hfXV1iRIZOf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums1 := []int{1, 3, 4, 6} + nums2 := []int{1, 2, 5, 6} + + result := slice.Union(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 3 4 6 2 5] +} +``` + +### UnionBy + +

UnionBy is like Union, what's more it accepts iteratee which is invoked for each element of each slice.

+ +Signature: + +```go +func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T +``` + +Example:[Run](https://go.dev/play/p/HGKHfxKQsFi) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4} + + divideTwo := func(n int) int { + return n / 2 + } + result := slice.UnionBy(divideTwo, nums) + + fmt.Println(result) + + // Output: + // [1 2 4] +} +``` + +### UpdateAt + +

Update the slice element at index. if param index < 0 or index <= len(slice), will return error.

+ +Signature: + +```go +func UpdateAt[T any](slice []T, index int, value T) []T +``` + +Example:[Run](https://go.dev/play/p/f3mh2KloWVm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result1 := slice.UpdateAt([]string{"a", "b", "c"}, -1, "1") + result2 := slice.UpdateAt([]string{"a", "b", "c"}, 0, "1") + result3 := slice.UpdateAt([]string{"a", "b", "c"}, 1, "1") + result4 := slice.UpdateAt([]string{"a", "b", "c"}, 2, "1") + result5 := slice.UpdateAt([]string{"a", "b", "c"}, 3, "1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c] + // [1 b c] + // [a 1 c] + // [a b 1] + // [a b c] +} +``` + +### Without + +

Creates a slice excluding all given items.

+ +Signature: + +```go +func Without[T comparable](slice []T, items ...T) []T +``` + +Example:[Run](https://go.dev/play/p/bwhEXEypThg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.Without([]int{1, 2, 3, 4}, 1, 2) + + fmt.Println(result) + + // Output: + // [3 4] +} +``` + +### KeyBy + +

Converts a slice to a map based on a callback function.

+ +Signature: + +```go +func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T +``` + +Example:[Run](https://go.dev/play/p/uXod2LWD1Kg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.KeyBy([]string{"a", "ab", "abc"}, func(str string) int { + return len(str) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:ab 3:abc] +} +``` + +### Join + +

Join the slice item with specify separator.

+ +Signature: + +```go +func Join[T any](s []T, separator string) string +``` + +Example:[Run](https://go.dev/play/p/huKzqwNDD7V) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + result1 := slice.Join(nums, ",") + result2 := slice.Join(nums, "-") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1,2,3,4,5 + // 1-2-3-4-5 +} +``` + +### Partition + +

Partition all slice elements with the evaluation of the given predicate functions.

+ +Signature: + +```go +func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T +``` + +Example:[Run](https://go.dev/play/p/lkQ3Ri2NQhV) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + result1 := slice.Partition(nums) + result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 }) + result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [[1 2 3 4 5]] + // [[2 4] [1 3 5]] + // [[1 2] [3 4] [5]] +} +``` + +### Random + +

Random get a random item of slice, return idx=-1 when slice is empty.

+ +Signature: + +```go +func Random[T any](slice []T) (val T, idx int) +``` + +Example:[Run](https://go.dev/play/p/UzpGQptWppw) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + + val, idx := slice.Random(nums) + if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) { + fmt.Println("okk") + } + // Output: + // okk +} +``` + +### SetToDefaultIf + +

Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items

+ +Signature: + +```go +func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) +``` + +Example:[Run](https://go.dev/play/p/9AXGlPRC0-A) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "a", "c", "d", "a"} + modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s }) + + fmt.Println(modifiedStrs) + fmt.Println(count) + + // Output: + // [ b c d ] + // 3 +} +``` + +### Break + +

Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.

+ +Signature: + +```go +func Break[T any](values []T, predicate func(T) bool) ([]T, []T) +``` + +Example:[Run](https://go.dev/play/p/yLYcBTyeQIz) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + even := func(n int) bool { return n%2 == 0 } + + resultEven, resultAfterFirstEven := slice.Break(nums, even) + + fmt.Println(resultEven) + fmt.Println(resultAfterFirstEven) + + // Output: + // [1] + // [2 3 4 5] +} +``` + +### RightPadding + +

RightPadding adds padding to the right end of a slice.

+ +Signature: + +```go +func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T +``` + +Example:[Run](https://go.dev/play/p/0_2rlLEMBXL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + padded := slice.RightPadding(nums, 0, 3) + fmt.Println(padded) + // Output: + // [1 2 3 4 5 0 0 0] +} +``` + +### LeftPadding + +

LeftPadding adds padding to the left begin of a slice.

+ +Signature: + +```go +func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T +``` + +Example:[Run](https://go.dev/play/p/jlQVoelLl2k) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + nums := []int{1, 2, 3, 4, 5} + padded := slice.LeftPadding(nums, 0, 3) + fmt.Println(padded) + // Output: + // [0 0 0 1 2 3 4 5] +} +``` + +### Frequency + +

Counts the frequency of each element in the slice.

+ +Signature: + +```go +func Frequency[T comparable](slice []T) map[T]int +``` + +Example:[Run](https://go.dev/play/p/CW3UVNdUZOq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + strs := []string{"a", "b", "b", "c", "c", "c"} + result := slice.Frequency(strs) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} +``` + +### JoinFunc + +

Joins the slice elements into a single string with the given separator.

+ +Signature: + +```go +func JoinFunc[T any](slice []T, sep string, transform func(T) T) string +``` + +Example:[Run](https://go.dev/play/p/55ib3SB5fM2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string { + return strings.ToUpper(s) + }) + + fmt.Println(result) + + // Output: + // A, B, C +} +``` + +### ConcatBy + +

Concats the elements of a slice into a single value using the provided separator and connector function.

+ +Signature: + +```go +func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T +``` + +Example:[Run](https://go.dev/play/p/6QcUpcY4UMW) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/slice" +) + +func main() { + type Person struct { + Name string + Age int + } + + people := []Person{ + {Name: "Alice", Age: 30}, + {Name: "Bob", Age: 25}, + {Name: "Charlie", Age: 35}, + } + + sep := Person{Name: " | ", Age: 0} + + personConnector := func(a, b Person) Person { + return Person{Name: a.Name + b.Name, Age: a.Age + b.Age} + } + + result := slice.ConcatBy(people, sep, personConnector) + + fmt.Println(result.Name) + fmt.Println(result.Age) + + // Output: + // Alice | Bob | Charlie + // 90 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/stream.md b/docs/en/api/packages/stream.md new file mode 100644 index 00000000..23642322 --- /dev/null +++ b/docs/en/api/packages/stream.md @@ -0,0 +1,1007 @@ +# Stream + +Package stream 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. it's feature is very limited. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/stream/stream.go](https://github.com/duke-git/lancet/blob/main/stream/stream.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/stream" +) +``` + +
+ +## Index + +- [Of](#Of) +- [FromSlice](#FromSlice) +- [FromChannel](#FromChannel) +- [FromRange](#FromRange) +- [Generate](#Generate) +- [Concat](#Concat) +- [Distinct](#Distinct) +- [Filter](#Filter) +- [Map](#Map) +- [Peek](#Peek) +- [Skip](#Skip) +- [Limit](#Limit) +- [Reverse](#Reverse) +- [Range](#Range) +- [Sorted](#Sorted) +- [ForEach](#ForEach) +- [Reduce](#Reduce) +- [FindFirst](#FindFirst) +- [FindLast](#FindLast) +- [Max](#Max) +- [Min](#Min) +- [AllMatch](#AllMatch) +- [AnyMatch](#AnyMatch) +- [NoneMatch](#NoneMatch) +- [Count](#Count) +- [ToSlice](#ToSlice) + +
+ + +## Documentation + +### Of + +

Creates a stream whose elements are the specified values.

+ +Signature: + +```go +func Of[T any](elems ...T) stream[T] +``` + +Example:[Run](https://go.dev/play/p/jI6_iZZuVFE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.Of(1, 2, 3) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromSlice + +

Creates a stream from slice.

+ +Signature: + +```go +func FromSlice[T any](source []T) stream[T] +``` + +Example:[Run](https://go.dev/play/p/wywTO0XZtI4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3}) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromChannel + +

Creates a stream from channel.

+ +Signature: + +```go +func FromChannel[T any](source <-chan T) stream[T] +``` + +Example:[Run](https://go.dev/play/p/9TZYugGMhXZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + ch := make(chan int) + go func() { + for i := 1; i < 4; i++ { + ch <- i + } + close(ch) + }() + + s := stream.FromChannel(ch) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### FromRange + +

Creates a number stream from start to end. both start and end are included. [start, end]

+ +Signature: + +```go +func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] +``` + +Example:[Run](https://go.dev/play/p/9Ex1-zcg-B-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromRange(1, 5, 1) + + data := s.ToSlice() + fmt.Println(data) + + // Output: + // [1 2 3 4 5] +} +``` + +### Generate + +

Creates a stream where each element is generated by the provided generater function.

+ +Signature: + +```go +func Generate[T any](generator func() func() (item T, ok bool)) stream[T] +``` + +Example:[Run](https://go.dev/play/p/rkOWL1yA3j9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + n := 0 + max := 4 + + generator := func() func() (int, bool) { + return func() (int, bool) { + n++ + return n, n < max + } + } + + s := stream.Generate(generator) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### Concat + +

Creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.

+ +Signature: + +```go +func Concat[T any](a, b stream[T]) stream[T] +``` + +Example:[Run](https://go.dev/play/p/HM4OlYk_OUC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s1 := stream.FromSlice([]int{1, 2, 3}) + s2 := stream.FromSlice([]int{4, 5, 6}) + + s := Concat(s1, s2) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3 4 5 6] +} +``` + +### Distinct + +

Creates returns a stream that removes the duplicated items. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Distinct() stream[T] +``` + +Example:[Run](https://go.dev/play/p/eGkOSrm64cB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 2, 3, 3, 3}) + distinct := original.Distinct() + + data1 := original.ToSlice() + data2 := distinct.ToSlice() + + fmt.Println(data1) + fmt.Println(data2) + + // Output: + // [1 2 2 3 3 3] + // [1 2 3] +} +``` + +### Filter + +

Returns a stream consisting of the elements of this stream that match the given predicate. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Filter(predicate func(item T) bool) stream[T] +``` + +Example:[Run](https://go.dev/play/p/MFlSANo-buc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4, 5}) + + isEven := func(n int) bool { + return n%2 == 0 + } + + even := original.Filter(isEven) + + fmt.Println(even.ToSlice()) + + // Output: + // [2 4] +} +``` + +### Map + +

Returns a stream consisting of the elements of this stream that apply the given function to elements of stream. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Map(mapper func(item T) T) stream[T] +``` + +Example:[Run](https://go.dev/play/p/OtNQUImdYko) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + addOne := func(n int) int { + return n + 1 + } + + increament := original.Map(addOne) + + fmt.Println(increament.ToSlice()) + + // Output: + // [2 3 4] +} +``` + +### Peek + +

Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Peek(consumer func(item T)) stream[T] +``` + +Example:[Run](https://go.dev/play/p/u1VNzHs6cb2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + data := []string{} + peekStream := original.Peek(func(n int) { + data = append(data, fmt.Sprint("value", n)) + }) + + fmt.Println(original.ToSlice()) + fmt.Println(peekStream.ToSlice()) + fmt.Println(data) + + // Output: + // [1 2 3] + // [1 2 3] + // [value1 value2 value3] +} +``` + +### Skip + +

Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Skip(n int) stream[T] +``` + +Example:[Run](https://go.dev/play/p/fNdHbqjahum) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Skip(-1) + s2 := original.Skip(0) + s3 := original.Skip(1) + s4 := original.Skip(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [2 3 4] + // [] +} +``` + +### Limit + +

Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Limit(maxSize int) stream[T] +``` + +Example:[Run](https://go.dev/play/p/qsO4aniDcGf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Limit(-1) + s2 := original.Limit(0) + s3 := original.Limit(1) + s4 := original.Limit(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [] + // [1] + // [1 2 3 4] +} +``` + +### Reverse + +

Returns a stream whose elements are reverse order of given stream. Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Reverse() stream[T] +``` + +Example:[Run](https://go.dev/play/p/A8_zkJnLHm4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + reverse := original.Reverse() + + fmt.Println(reverse.ToSlice()) + + // Output: + // [3 2 1] +} +``` + +### Range + +

Returns a stream whose elements are in the range from start(included) to end(excluded) original stream.Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Range(start, end int) stream[T] +``` + +Example:[Run](https://go.dev/play/p/indZY5V2f4j) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + s1 := original.Range(0, 0) + s2 := original.Range(0, 1) + s3 := original.Range(0, 3) + s4 := original.Range(1, 2) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [1] + // [1 2 3] + // [2] +} +``` + +### Sorted + +

Returns a stream consisting of the elements of this stream, sorted according to the provided less function.Support chainable operation

+ +Signature: + +```go +func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] +``` + +Example:[Run](https://go.dev/play/p/XXtng5uonFj) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + sorted := original.Sorted(func(a, b int) bool { return a < b }) + + fmt.Println(original.ToSlice()) + fmt.Println(sorted.ToSlice()) + + // Output: + // [4 2 1 3] + // [1 2 3 4] +} +``` + +### ForEach + +

Performs an action for each element of this stream.

+ +Signature: + +```go +func (s stream[T]) ForEach(action func(item T)) +``` + +Example:[Run](https://go.dev/play/p/Dsm0fPqcidk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result := 0 + original.ForEach(func(item int) { + result += item + }) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### Reduce + +

Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.

+ +Signature: + +```go +func (s stream[T]) Reduce(initial T, accumulator func(a, b T) T) T +``` + +Example:[Run](https://go.dev/play/p/6uzZjq_DJLU) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result := original.Reduce(0, func(a, b int) int { + return a + b + }) + + fmt.Println(result) + + // Output: + // 6 +} +``` + +### FindFirst + +

Returns the first element of this stream and true, or zero value and false if the stream is empty.

+ +Signature: + +```go +func (s stream[T]) FindFirst() (T, bool) +``` + +Example:[Run](https://go.dev/play/p/9xEf0-6C1e3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result, ok := original.FindFirst() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### FindLast + +

Returns the last element of this stream and true, or zero value and false if the stream is empty.

+ +Signature: + +```go +func (s stream[T]) FindLast() (T, bool) +``` + +Example:[Run](https://go.dev/play/p/WZD2rDAW-2h) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{3, 2, 1}) + + result, ok := original.FindLast() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### Max + +

Returns the maximum element of this stream according to the provided less function. less fuction: a > b

+ +Signature: + +```go +func (s stream[T]) Max(less func(a, b T) bool) (T, bool) +``` + +Example:[Run](https://go.dev/play/p/fm-1KOPtGzn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + max, ok := original.Max(func(a, b int) bool { return a > b }) + + fmt.Println(max) + fmt.Println(ok) + + // Output: + // 4 + // true +} +``` + +### Min + +

Returns the minimum element of this stream according to the provided less function. less fuction: a < b

+ +Signature: + +```go +func (s stream[T]) Min(less func(a, b T) bool) (T, bool) +``` + +Example:[Run](https://go.dev/play/p/vZfIDgGNRe_0) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{4, 2, 1, 3}) + + min, ok := original.Min(func(a, b int) bool { return a < b }) + + fmt.Println(min) + fmt.Println(ok) + + // Output: + // 1 + // true +} +``` + +### AllMatch + +

Returns whether all elements of this stream match the provided predicate.

+ +Signature: + +```go +func (s stream[T]) AllMatch(predicate func(item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/V5TBpVRs-Cx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.AllMatch(func(item int) bool { + return item > 0 + }) + + result2 := original.AllMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### AnyMatch + +

Returns whether any elements of this stream match the provided predicate.

+ +Signature: + +```go +func (s stream[T]) AnyMatch(predicate func(item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/PTCnWn4OxSn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.AnyMatch(func(item int) bool { + return item > 1 + }) + + result2 := original.AnyMatch(func(item int) bool { + return item > 3 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### NoneMatch + +

Returns whether no elements of this stream match the provided predicate.

+ +Signature: + +```go +func (s stream[T]) NoneMatch(predicate func(item T) bool) bool +``` + +Example:[Run](https://go.dev/play/p/iWS64pL1oo3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + original := stream.FromSlice([]int{1, 2, 3}) + + result1 := original.NoneMatch(func(item int) bool { + return item > 3 + }) + + result2 := original.NoneMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### Count + +

Returns the count of elements in the stream.

+ +Signature: + +```go +func (s stream[T]) Count() int +``` + +Example:[Run](https://go.dev/play/p/r3koY6y_Xo-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s1 := stream.FromSlice([]int{1, 2, 3}) + s2 := stream.FromSlice([]int{}) + + fmt.Println(s1.Count()) + fmt.Println(s2.Count()) + + // Output: + // 3 + // 0 +} +``` + +### ToSlice + +

Returns the elements in the stream.

+ +Signature: + +```go +func (s stream[T]) ToSlice() []T +``` + +Example:[Run](https://go.dev/play/p/jI6_iZZuVFE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.Of(1, 2, 3) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} +``` + +### IndexOf + +

Returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element.

+ +Signature: + +```go +func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int +``` + +Example:[Run](https://go.dev/play/p/tBV5Nc-XDX2) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3, 2}) + + result1 := s.IndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.IndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 1 +} +``` + +### LastIndexOf + +

Returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element.

+ +Signature: + +```go +func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int +``` + +Example:[Run](https://go.dev/play/p/CjeoNw2eac_G) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/stream" +) + +func main() { + s := stream.FromSlice([]int{1, 2, 3, 2}) + + result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 3 +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/struct.md b/docs/en/api/packages/struct.md new file mode 100644 index 00000000..51534e98 --- /dev/null +++ b/docs/en/api/packages/struct.md @@ -0,0 +1,577 @@ +# Structs + +Struct is abstract struct for provide several high level functions + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/structs/struct.go](https://github.com/duke-git/lancet/blob/main/structs/struct.go) + +- [https://github.com/duke-git/lancet/blob/main/structs/field.go](https://github.com/duke-git/lancet/blob/main/structs/field.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/structs" +) +``` + +
+ +## Index: + +- [New](#New) +- [ToMap](#ToMap) +- [Fields](#Fields) +- [Field](#Field) +- [IsStruct](#IsStruct) +- [Tag](#Tag) +- [Name](#Name) +- [Value](#Value) +- [Kind](#Kind) +- [IsEmbedded](#IsEmbedded) +- [IsExported](#IsExported) +- [IsZero](#IsZero) +- [IsSlice](#IsSlice) +- [IsTargetType](#IsTargetType) + +
+ +## Documentation: + +### New + +

The constructor function of the `Struct`

+ +Signature: + +```go +func New(value any, tagName ...string) *Struct +``` + +Example: + +```go +package main + +import ( + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + // to do something +} +``` + +### ToMap + +

convert a valid struct to a map

+ +Signature: + +```go +func (s *Struct) ToMap() (map[string]any, error) +``` + +> In addition, provided a convenient static function ToMap + +```go +func ToMap(v any) (map[string]any, error) +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + // use constructor function + s1 := structs.New(p1) + m1, _ := s1.ToMap() + + fmt.Println(m1) + + // use static function + m2, _ := structs.ToMap(p1) + + fmt.Println(m2) + + // Output: + // map[name:11] + // map[name:11] +} +``` + +### Fields + +

Get all fields of a given struct, that the fields are abstract struct field

+ +Signature: + +```go +func (s *Struct) Fields() []*Field +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + fields := s.Fields() + + fmt.Println(len(fields)) + + // Output: + // 1 +} +``` + +### Field + +

Get an abstract field of a struct by given field name

+ +Signature: + +```go +func (s *Struct) Field(name string) *Field +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + f := s.Field("Name") + + fmt.Println(f.Value()) + + // Output: + // 11 +} +``` + +### IsStruct + +

Check if the struct is valid

+ +Signature: + +```go +func (s *Struct) IsStruct() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type People struct { + Name string `json:"name"` + } + p1 := &People{Name: "11"} + s := structs.New(p1) + + fmt.Println(s.IsStruct()) + + // Output: + // true +} +``` + +### Tag + +

Get a `Tag` of the `Field`, `Tag` is a abstract struct field tag

+ +Signature: + +```go +func (f *Field) Tag() *Tag +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := structs.New(p1) + n, _ := s.Field("Name") + tag := n.Tag() + + fmt.Println(tag.Name) + + // Output: + // name +} +``` + +### Value + +

Get the `Field` underlying value

+ +Signature: + +```go +func (f *Field) Value() any +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := structs.New(p1) + n, _ := s.Field("Name") + + fmt.Println(n.Value()) + + // Output: + // 111 +} +``` + +### IsEmbedded + +

Check if the field is an embedded field

+ +Signature: + +```go +func (f *Field) IsEmbedded() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + } + type Child struct { + Parent + Age int + } + c1 := &Child{} + c1.Name = "111" + c1.Age = 11 + + s := structs.New(c1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.IsEmbedded()) + fmt.Println(a.IsEmbedded()) + + // Output: + // true + // false +} +``` + +### IsExported + +

Check if the field is exported

+ +Signature: + +```go +func (f *Field) IsExported() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + age int + } + p1 := &Parent{Name: "11", age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("age") + + fmt.Println(n.IsExported()) + fmt.Println(a.IsExported()) + + // Output: + // true + // false +} +``` + +### IsZero + +

Check if the field is zero value

+ +Signature: + +```go +func (f *Field) IsZero() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.IsZero()) + fmt.Println(a.IsZero()) + + // Output: + // true + // false +} +``` + +### Name + +

Get the field name

+ +Signature: + +```go +func (f *Field) Name() string +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.Name()) + fmt.Println(a.Name()) + + // Output: + // Name + // Age +} +``` + +### Kind + +

Get the field's kind

+ +Signature: + +```go +func (f *Field) Kind() reflect.Kind +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := structs.New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + fmt.Println(n.Kind()) + fmt.Println(a.Kind()) + + // Output: + // string + // int +} +``` + +### IsSlice + +

Check if the field is a slice

+ +Signature: + +```go +func (f *Field) IsSlice() bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{arr: []int{1, 2, 3}} + s := structs.New(p1) + a, _ := s.Field("arr") + + fmt.Println(a.IsSlice()) + + // Output: + // true +} +``` + +### IsTargetType + +

check if a struct field type is target type or not

+ +Signature: + +```go +func (f *Field) IsTargetType(targetType reflect.Kind) bool +``` + +Example: + +```go +package main + +import ( + "fmt" + "reflect" + "github.com/duke-git/lancet/v2/structs" +) + +func main() { + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{arr: []int{1, 2, 3}} + 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: + // true + // true +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/strutil.md b/docs/en/api/packages/strutil.md new file mode 100644 index 00000000..90e2d995 --- /dev/null +++ b/docs/en/api/packages/strutil.md @@ -0,0 +1,1793 @@ +# Strutil + +Package strutil contains some functions to manipulate string. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/strutil/string.go](https://github.com/duke-git/lancet/blob/main/strutil/string.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/strutil" +) +``` + +
+ +## Index + +- [After](#After) +- [AfterLast](#AfterLast) +- [Before](#Before) +- [BeforeLast](#BeforeLast) +- [CamelCase](#CamelCase) +- [Capitalize](#Capitalize) +- [IsString](#IsString) +- [KebabCase](#KebabCase) +- [UpperKebabCase](#UpperKebabCase) +- [LowerFirst](#LowerFirst) +- [UpperFirst](#UpperFirst) +- [Pad](#Pad) +- [PadStart](#PadStart) +- [PadEnd](#PadEnd) +- [Reverse](#Reverse) +- [SnakeCase](#SnakeCase) +- [UpperSnakeCase](#UpperSnakeCase) +- [SplitEx](#SplitEx) +- [Substring](#Substring) +- [Wrap](#Wrap) +- [Unwrap](#Unwrap) +- [SplitWords](#SplitWords) +- [WordCount](#WordCount) +- [RemoveNonPrintable](#RemoveNonPrintable) +- [StringToBytes](#StringToBytes) +- [BytesToString](#BytesToString) +- [IsBlank](#IsBlank) +- [IsNotBlank](#IsNotBlank) +- [HasPrefixAny](#HasPrefixAny) +- [HasSuffixAny](#HasSuffixAny) +- [IndexOffset](#IndexOffset) +- [ReplaceWithMap](#ReplaceWithMap) +- [Trim](#Trim) +- [SplitAndTrim](#SplitAndTrim) +- [HideString](#HideString) +- [ContainsAll](#ContainsAll) +- [ContainsAny](#ContainsAny) +- [RemoveWhiteSpace](#RemoveWhiteSpace) +- [SubInBetween](#SubInBetween) +- [HammingDistance](#HammingDistance) +- [Concat](#Concat) +- [Ellipsis](#Ellipsis) +- [Shuffle](#Shuffle) +- [Rotate](#Rotate) +- [TemplateReplace](#TemplateReplace) +- [RegexMatchAllGroups](#RegexMatchAllGroups) +- [ExtractContent](#ExtractContent) +- [FindAllOccurrences](#FindAllOccurrences) + + +
+ +## Documentation + +### After + +

Returns the substring after the first occurrence of a specified string in the source string.

+ +Signature: + +```go +func After(s, char string) string +``` + +Example:[Run](https://go.dev/play/p/RbCOQqCDA7m) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.After("foo", "") + result2 := strutil.After("foo", "foo") + result3 := strutil.After("foo/bar", "foo") + result4 := strutil.After("foo/bar", "/") + result5 := strutil.After("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // + // /bar + // bar + // bar/baz +} +``` + +### AfterLast + +

Returns the substring after the last occurrence of a specified string in the source string.

+ +Signature: + +```go +func AfterLast(s, char string) string +``` + +Example:[Run](https://go.dev/play/p/1TegARrb8Yn) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.AfterLast("foo", "") + result2 := strutil.AfterLast("foo", "foo") + result3 := strutil.AfterLast("foo/bar", "/") + result4 := strutil.AfterLast("foo/bar/baz", "/") + result5 := strutil.AfterLast("foo/bar/foo/baz", "foo") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // + // bar + // baz + // /baz +} +``` + +### Before + +

Returns the substring of the source string up to the first occurrence of the specified string.

+ +Signature: + +```go +func Before(s, char string) string +``` + +Example:[Run](https://go.dev/play/p/JAWTZDS4F5w) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Before("foo", "") + result2 := strutil.Before("foo", "foo") + result3 := strutil.Before("foo/bar", "/") + result4 := strutil.Before("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // + // foo + // foo +} +``` + +### BeforeLast + +

Returns the substring of the source string up to the last occurrence of the specified string.

+ +Signature: + +```go +func BeforeLast(s, char string) string +``` + +Example:[Run](https://go.dev/play/p/pJfXXAoG_Te) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.BeforeLast("foo", "") + result2 := strutil.BeforeLast("foo", "foo") + result3 := strutil.BeforeLast("foo/bar", "/") + result4 := strutil.BeforeLast("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // + // foo + // foo/bar +} +``` + +### CamelCase + +

Coverts string to camelCase string, non letters and numbers will be ignored.

+ +Signature: + +```go +func CamelCase(s string) string +``` + +Example:[Run](https://go.dev/play/p/9eXP3tn2tUy) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foobar", "&FOO:BAR$BAZ", "$foo%", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.CamelCase(v) + fmt.Println(s) + } + + // Output: + // + // foobar + // fooBarBaz + // foo + // foo11Bar +} +``` + +### KebabCase + +

KebabCase covert string to kebab-case, non letters and numbers will be ignored.

+ +Signature: + +```go +func KebabCase(s string) string +``` + +Example:[Run](https://go.dev/play/p/dcZM9Oahw-Y) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.KebabCase(v) + fmt.Println(s) + } + + // Output: + // + // foo-bar + // foo-bar + // foobar + // foo-1-1-bar +} +``` + +### UpperKebabCase + +

UpperKebabCase covert string to upper KEBAB-CASE, non letters and numbers will be ignored.

+ +Signature: + +```go +func UpperKebabCase(s string) string +``` + +Example:[Run](https://go.dev/play/p/zDyKNneyQXk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.UpperKebabCase(v) + fmt.Println(s) + } + + // Output: + // + // FOO-BAR + // FOO-BAR + // FOO-BAR + // FOO-1-1-BAR +} +``` + +### Capitalize + +

Convert the first character of a string to upper case.

+ +Signature: + +```go +func Capitalize(s string) string +``` + +Example:[Run](https://go.dev/play/p/2OAjgbmAqHZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "Foo", "_foo", "fooBar", "foo-bar"} + + for _, v := range strings { + s := strutil.Capitalize(v) + fmt.Println(s) + } + + // Output: + // + // Foo + // _foo + // Foobar + // Foo-bar +} +``` + +### IsString + +

Check if the value's data type is string.

+ +Signature: + +```go +func IsString(v any) bool +``` + +Example:[Run](https://go.dev/play/p/IOgq7oF9ERm) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsString("") + result2 := strutil.IsString("a") + result3 := strutil.IsString(1) + result4 := strutil.IsString(true) + result5 := strutil.IsString([]string{"a"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // false + // false + // false +} +``` + +### LowerFirst + +

Convert the first character of string to lower case.

+ +Signature: + +```go +func LowerFirst(s string) string +``` + +Example:[Run](https://go.dev/play/p/CbzAyZmtJwL) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "bar", "BAr", "Bar大"} + + for _, v := range strings { + s := strutil.LowerFirst(v) + fmt.Println(s) + } + + // Output: + // + // bar + // bAr + // bar大 +} +``` + +### UpperFirst + +

Convert the first character of string to upper case.

+ +Signature: + +```go +func UpperFirst(s string) string +``` + +Example:[Run](https://go.dev/play/p/sBbBxRbs8MM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "bar", "BAr", "bar大"} + + for _, v := range strings { + s := strutil.UpperFirst(v) + fmt.Println(s) + } + + // Output: + // + // Bar + // BAr + // Bar大 +} +``` + +### Pad + +

Pads string on the left and right side if it's shorter than size.

+ +Signature: + +```go +func Pad(source string, size int, padStr string) string +``` + +Example:[Run](https://go.dev/play/p/NzImQq-VF8q) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Pad("foo", 1, "bar") + result2 := strutil.Pad("foo", 2, "bar") + result3 := strutil.Pad("foo", 3, "bar") + result4 := strutil.Pad("foo", 4, "bar") + result5 := strutil.Pad("foo", 5, "bar") + result6 := strutil.Pad("foo", 6, "bar") + result7 := strutil.Pad("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + // Output: + // foo + // foo + // foo + // foob + // bfoob + // bfooba + // bafooba +} +``` + +### PadEnd + +

Pads string on the right side if it's shorter than size.

+ +Signature: + +```go +func PadEnd(source string, size int, padStr string) string +``` + +Example:[Run](https://go.dev/play/p/9xP8rN0vz--) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.PadEnd("foo", 1, "bar") + result2 := strutil.PadEnd("foo", 2, "bar") + result3 := strutil.PadEnd("foo", 3, "bar") + result4 := strutil.PadEnd("foo", 4, "bar") + result5 := strutil.PadEnd("foo", 5, "bar") + result6 := strutil.PadEnd("foo", 6, "bar") + result7 := strutil.PadEnd("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // foo + // foo + // foo + // foob + // fooba + // foobar + // foobarb +} +``` + +### PadStart + +

Pads string on the left side if it's shorter than size.

+ +Signature: + +```go +func PadStart(source string, size int, padStr string) string +``` + +Example:[Run](https://go.dev/play/p/xpTfzArDfvT) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.PadStart("foo", 1, "bar") + result2 := strutil.PadStart("foo", 2, "bar") + result3 := strutil.PadStart("foo", 3, "bar") + result4 := strutil.PadStart("foo", 4, "bar") + result5 := strutil.PadStart("foo", 5, "bar") + result6 := strutil.PadStart("foo", 6, "bar") + result7 := strutil.PadStart("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // foo + // foo + // foo + // bfoo + // bafoo + // barfoo + // barbfoo +} +``` + +### Reverse + +

Return string whose char order is reversed to the given string.

+ +Signature: + +```go +func Reverse(s string) string +``` + +Example:[Run](https://go.dev/play/p/adfwalJiecD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + s := "foo" + rs := strutil.Reverse(s) + + fmt.Println(s) + fmt.Println(rs) + + // Output: + // foo + // oof +} +``` + +### SnakeCase + +

Coverts string to snake_case, non letters and numbers will be ignored.

+ +Signature: + +```go +func SnakeCase(s string) string +``` + +Example:[Run](https://go.dev/play/p/tgzQG11qBuN) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.SnakeCase(v) + fmt.Println(s) + } + + // Output: + // + // foo_bar + // foo_bar + // foobar + // foo_1_1_bar +} +``` + +### UpperSnakeCase + +

Coverts string to upper snake_case, non letters and numbers will be ignored.

+ +Signature: + +```go +func UpperSnakeCase(s string) string +``` + +Example:[Run](https://go.dev/play/p/4COPHpnLx38) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := strutil.UpperSnakeCase(v) + fmt.Println(s) + } + + // Output: + // + // FOO_BAR + // FOO_BAR + // FOO_BAR + // FOO_1_1_BAR +} +``` + +### SplitEx + +

Split a given string whether the result contains empty string.

+ +Signature: + +```go +func SplitEx(s, sep string, removeEmptyString bool) []string +``` + +Example:[Run](https://go.dev/play/p/Us-ySSbWh-3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.SplitEx(" a b c ", "", true) + + result2 := strutil.SplitEx(" a b c ", " ", false) + result3 := strutil.SplitEx(" a b c ", " ", true) + + result4 := strutil.SplitEx("a = b = c = ", " = ", false) + result5 := strutil.SplitEx("a = b = c = ", " = ", true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [] + // [ a b c ] + // [a b c] + // [a b c ] +} +``` + +### Substring + +

Returns a substring of the specified length starting at the specified offset position.

+ +Signature: + +```go +func Substring(s string, offset int, length uint) string +``` + +Example:[Run](https://go.dev/play/p/q3sM6ehnPDp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Substring("abcde", 1, 3) + result2 := strutil.Substring("abcde", 1, 5) + result3 := strutil.Substring("abcde", -1, 3) + result4 := strutil.Substring("abcde", -2, 2) + result5 := strutil.Substring("abcde", -2, 3) + result6 := strutil.Substring("你好,欢迎你", 0, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // bcd + // bcde + // e + // de + // de + // 你好 +} +``` + +### Wrap + +

Wrap a string with given string.

+ +Signature: + +```go +func Wrap(str string, wrapWith string) string +``` + +Example:[Run](https://go.dev/play/p/KoZOlZDDt9y) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Wrap("foo", "") + result2 := strutil.Wrap("foo", "*") + result3 := strutil.Wrap("'foo'", "'") + result4 := strutil.Wrap("", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // foo + // *foo* + // ''foo'' + // +} +``` + +### Unwrap + +

Unwrap a given string from anther string. will change source string.

+ +Signature: + +```go +func Unwrap(str string, wrapToken string) string +``` + +Example:[Run](https://go.dev/play/p/Ec2q4BzCpG-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Unwrap("foo", "") + result2 := strutil.Unwrap("*foo*", "*") + result3 := strutil.Unwrap("*foo", "*") + result4 := strutil.Unwrap("foo*", "*") + result5 := strutil.Unwrap("**foo**", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // foo + // foo + // *foo + // foo* + // *foo* +} +``` + +### SplitWords + +

Splits a string into words, word only contains alphabetic characters.

+ +Signature: + +```go +func SplitWords(s string) []string +``` + +Example:[Run](https://go.dev/play/p/KLiX4WiysMM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.SplitWords("a word") + result2 := strutil.SplitWords("I'am a programmer") + result3 := strutil.SplitWords("Bonjour, je suis programmeur") + result4 := strutil.SplitWords("a -b-c' 'd'e") + result5 := strutil.SplitWords("你好,我是一名码农") + result6 := strutil.SplitWords("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // [a word] + // [I'am a programmer] + // [Bonjour je suis programmeur] + // [a b-c' d'e] + // [] + // [] +} +``` + +### WordCount + +

Return the number of meaningful word, word only contains alphabetic characters.

+ +Signature: + +```go +func WordCount(s string) int +``` + +Example:[Run](https://go.dev/play/p/bj7_odx3vRf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.WordCount("a word") + result2 := strutil.WordCount("I'am a programmer") + result3 := strutil.WordCount("Bonjour, je suis programmeur") + result4 := strutil.WordCount("a -b-c' 'd'e") + result5 := strutil.WordCount("你好,我是一名码农") + result6 := strutil.WordCount("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + + // Output: + // 2 + // 3 + // 4 + // 3 + // 0 + // 0 +} +``` + +### RemoveNonPrintable + +

Remove non-printable characters from a string.

+ +Signature: + +```go +func RemoveNonPrintable(str string) string +``` + +Example:[Run](https://go.dev/play/p/og47F5x_jTZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.RemoveNonPrintable("hello\u00a0 \u200bworld\n") + result2 := strutil.RemoveNonPrintable("你好😄") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // hello world + // 你好😄 +} +``` + +### StringToBytes + +

Converts a string to byte slice without a memory allocation.

+ +Signature: + +```go +func StringToBytes(str string) (b []byte) +``` + +Example:[Run](https://go.dev/play/p/7OyFBrf9AxA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.StringToBytes("abc") + result2 := reflect.DeepEqual(result1, []byte{'a', 'b', 'c'}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [97 98 99] + // true +} +``` + +### BytesToString + +

Converts a byte slice to string without a memory allocation.

+ +Signature: + +```go +func BytesToString(bytes []byte) string +``` + +Example:[Run](https://go.dev/play/p/6c68HRvJecH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + bytes := []byte{'a', 'b', 'c'} + result := strutil.BytesToString(bytes) + + fmt.Println(result) + + // Output: + // abc +} +``` + +### IsBlank + +

Checks if a string is whitespace or empty.

+ +Signature: + +```go +func IsBlank(str string) bool +``` + +Example:[Run](https://go.dev/play/p/6zXRH_c0Qd3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsBlank("") + result2 := strutil.IsBlank("\t\v\f\n") + result3 := strutil.IsBlank(" 中文") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsNotBlank + +

Checks if a string is not whitespace or not empty.

+ +Signature: + +```go +func IsNotBlank(str string) bool +``` + +Example:[Run](https://go.dev/play/p/e_oJW0RAquA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.IsNotBlank("") + result2 := strutil.IsNotBlank(" ") + result3 := strutil.IsNotBlank("\t\v\f\n") + result4 := strutil.IsNotBlank(" 中文") + result5 := strutil.IsNotBlank(" world ") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // false + // false + // false + // true + // true +} +``` + +### HasPrefixAny + +

Checks if a string starts with any of an array of specified strings.

+ +Signature: + +```go +func HasPrefixAny(str string, prefixes []string) bool +``` + +Example:[Run](https://go.dev/play/p/8UUTl2C5slo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.HasPrefixAny("foo bar", []string{"fo", "xyz", "hello"}) + result2 := strutil.HasPrefixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### HasSuffixAny + +

Checks if a string ends with any of an array of specified strings.

+ +Signature: + +```go +func HasSuffixAny(str string, suffixes []string) bool +``` + +Example:[Run](https://go.dev/play/p/sKWpCQdOVkx) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.HasSuffixAny("foo bar", []string{"bar", "xyz", "hello"}) + result2 := strutil.HasSuffixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IndexOffset + +

Returns the index of the first instance of substr in string after offsetting the string by `idxFrom`, or -1 if substr is not present in string.

+ +Signature: + +```go +func IndexOffset(str string, substr string, idxFrom int) int +``` + +Example:[Run](https://go.dev/play/p/qZo4lV2fomB) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "foo bar hello world" + + result1 := strutil.IndexOffset(str, "o", 5) + result2 := strutil.IndexOffset(str, "o", 0) + result3 := strutil.IndexOffset(str, "d", len(str)-1) + result4 := strutil.IndexOffset(str, "d", len(str)) + result5 := strutil.IndexOffset(str, "f", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 12 + // 1 + // 18 + // -1 + // -1 +} +``` + +### ReplaceWithMap + +

Returns a copy of `str`, which is replaced by a map in unordered way, case-sensitively.

+ +Signature: + +```go +func ReplaceWithMap(str string, replaces map[string]string) string +``` + +Example:[Run](https://go.dev/play/p/h3t7CNj2Vvu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "ac ab ab ac" + replaces := map[string]string{ + "a": "1", + "b": "2", + } + + result := strutil.ReplaceWithMap(str, replaces) + + fmt.Println(result) + // Output: + // 1c 12 12 1c +} +``` + +### Trim + +

Strips whitespace (or other characters) from the beginning and end of a string. The optional parameter `characterMask` specifies the additional stripped characters.

+ +Signature: + +```go +func Trim(str string, characterMask ...string) string +``` + +Example:[Run](https://go.dev/play/p/Y0ilP0NRV3j) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Trim("\nabcd") + + str := "$ ab cd $ " + + result2 := strutil.Trim(str) + result3 := strutil.Trim(str, "$") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abcd + // $ ab cd $ + // ab cd +} +``` + +### SplitAndTrim + +

Splits string `str` by a string `delimiter` to a slice, and calls Trim to every element of slice. It ignores the elements which are empty after Trim.

+ +Signature: + +```go +func SplitAndTrim(str, delimiter string, characterMask ...string) []string +``` + +Example:[Run](https://go.dev/play/p/ZNL6o4SkYQ7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := " a,b, c,d,$1 " + + result1 := strutil.SplitAndTrim(str, ",") + result2 := strutil.SplitAndTrim(str, ",", "$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b c d $1] + // [a b c d 1] +} +``` + +### HideString + +

Hide some chars in source string with param `replaceChar`. replace range is origin[start : end]. [start, end).

+ +Signature: + +```go +func HideString(origin string, start, end int, replaceChar string) string +``` + +Example:[Run](https://go.dev/play/p/pzbaIVCTreZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "13242658976" + + result1 := strutil.HideString(str, 3, 3, "*") + result2 := strutil.HideString(str, 3, 4, "*") + result3 := strutil.HideString(str, 3, 7, "*") + result4 := strutil.HideString(str, 7, 11, "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 13242658976 + // 132*2658976 + // 132****8976 + // 1324265**** +} +``` + +### ContainsAll + +

Return true if target string contains all the substrings.

+ +Signature: + +```go +func ContainsAll(str string, substrs []string) bool +``` + +Example:[Run](https://go.dev/play/p/KECtK2Os4zq) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "hello world" + + result1 := strutil.ContainsAll(str, []string{"hello", "world"}) + result2 := strutil.ContainsAll(str, []string{"hello", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### ContainsAny + +

Return true if target string contains any one of the substrings.

+ +Signature: + +```go +func ContainsAny(str string, substrs []string) bool +``` + +Example:[Run](https://go.dev/play/p/dZGSSMB3LXE) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "hello world" + + result1 := strutil.ContainsAny(str, []string{"hello", "world"}) + result2 := strutil.ContainsAny(str, []string{"hello", "abc"}) + result3 := strutil.ContainsAny(str, []string{"123", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### RemoveWhiteSpace + +

Remove whitespace characters from a string. when set repalceAll is true removes all whitespace, false only replaces consecutive whitespace characters with one space.

+ +Signature: + +```go +func RemoveWhiteSpace(str string, repalceAll bool) string +``` + +Example:[Run](https://go.dev/play/p/HzLC9vsTwkf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := " hello \r\n \t world" + + result1 := strutil.RemoveWhiteSpace(str, true) + result2 := strutil.RemoveWhiteSpace(str, false) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // helloworld + // hello world +} +``` + + +### SubInBetween + +

Return substring between the start and end position(excluded) of source string.

+ +Signature: + +```go +func SubInBetween(str string, start string, end string) string +``` + +Example:[Run](https://go.dev/play/p/EDbaRvjeNsv) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + str := "abcde" + + result1 := strutil.SubInBetween(str, "", "de") + result2 := strutil.SubInBetween(str, "a", "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // abc + // bc +} +``` + +### HammingDistance + +

HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.

+ +Signature: + +```go +func HammingDistance(a, b string) (int, error) +``` + +Example:[Run](https://go.dev/play/p/glNdQEA9HUi) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + + result1, _ := strutil.HammingDistance("de", "de") + result2, _ := strutil.HammingDistance("a", "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // 1 +} +``` + + +### Concat + +

Concatenates strings. length is the length of the concatenated string. If unsure, pass 0 or a negative number.

+ +Signature: + +```go +func Concat(length int, str ...string) string +``` + +Example:[Run](https://go.dev/play/p/gD52SZHr4Kp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Concat(12, "Hello", " ", "World", "!") + result2 := strutil.Concat(11, "Go", " ", "Language") + result3 := strutil.Concat(0, "An apple a ", "day,", "keeps the", " doctor away") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello World! + // Go Language + // An apple a day,keeps the doctor away +} +``` + +### Ellipsis + +

Truncates a string to a specified length and appends an ellipsis.

+ +Signature: + +```go +func Ellipsis(str string, length int) string +``` + +Example:[Run](https://go.dev/play/p/i1vbdQiQVRR) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := strutil.Ellipsis("hello world", 5) + result2 := strutil.Ellipsis("你好,世界!", 2) + result3 := strutil.Ellipsis("😀😃😄😁😆", 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // hello... + // 你好... + // 😀😃😄... +} +``` + +### Shuffle + +

Shuffle the order of characters of given string.

+ +Signature: + +```go +func Shuffle(str string) string +``` + +Example:[Run](https://go.dev/play/p/iStFwBwyGY7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result := strutil.Shuffle("hello") + fmt.Println(result) //olelh (random order) +} +``` + +### Rotate + +

Rotates the string by the specified number of characters.

+ +Signature: + +```go +func Rotate(str string, shift int) string +``` + +Example:[Run](https://go.dev/play/p/Kf03iOeT5bd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result1 := Rotate("Hello", 0) + result2 := Rotate("Hello", 1) + result3 := Rotate("Hello", 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello + // oHell + // loHel +} +``` +### TemplateReplace + +

Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".

+ +Signature: + +```go +func TemplateReplace(template string, data map[string]string string +``` + +example:[Run](https://go.dev/play/p/cXSuFvyZqv9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + template := `Hello, my name is {name}, I'm {age} years old.` + data := map[string]string{ + "name": "Bob", + "age": "20", + } + + result := strutil.TemplateReplace(template, data) + + fmt.Println(result) + + // Output: + // Hello, my name is Bob, I'm 20 years old. +} +``` + +### RegexMatchAllGroups + +

Matches all subgroups in a string using a regular expression and returns the result.

+ +Signature: + +```go +func RegexMatchAllGroups(pattern, str string) [][]string +``` + +Example:[Run](https://go.dev/play/p/JZiu0RXpgN-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + pattern := `(\w+\.+\w+)@(\w+)\.(\w+)` + str := "Emails: john.doe@example.com and jane.doe@example.com" + + result := strutil.RegexMatchAllGroups(pattern, str) + + fmt.Println(result[0]) + fmt.Println(result[1]) + + // Output: + // [john.doe@example.com john.doe example com] + // [jane.doe@example.com jane.doe example com] +} +``` + +### ExtractContent + +

Extracts the content between the start and end strings in the source string.

+ +Signature: + +```go +func ExtractContent(s, start, end string) []string +``` + +Example:[Run](https://go.dev/play/p/Ay9UIk7Rum9) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + html := `content1aacontent2bbcontent1` + + result := strutil.ExtractContent(html, "", "") + + fmt.Println(result) + + // Output: + // [content1 content2 content1] +} +``` + +### FindAllOccurrences + +

Returns the positions of all occurrences of a substring in a string.

+ +Signature: + +```go +func FindAllOccurrences(str, substr string) []int +``` + +Example:[Run](https://go.dev/play/p/uvyA6azGLB1) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + result := strutil.FindAllOccurrences("ababab", "ab") + + fmt.Println(result) + + // Output: + // [0 2 4] +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/system.md b/docs/en/api/packages/system.md new file mode 100644 index 00000000..290aa317 --- /dev/null +++ b/docs/en/api/packages/system.md @@ -0,0 +1,445 @@ +# System + +Package system contains some functions about os, runtime, shell command. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/system/os.go](https://github.com/duke-git/lancet/blob/main/system/os.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/system" +) +``` + +
+ +## Index + +- [IsWindows](#IsWindows) +- [IsLinux](#IsLinux) +- [IsMac](#IsMac) +- [GetOsEnv](#GetOsEnv) +- [SetOsEnv](#SetOsEnv) +- [RemoveOsEnv](#RemoveOsEnv) +- [CompareOsEnv](#CompareOsEnv) +- [ExecCommand](#ExecCommand) +- [GetOsBits](#GetOsBits) +- [StartProcess](#StartProcess) +- [StopProcess](#StopProcess) +- [KillProcess](#KillProcess) +- [GetProcessInfo](#GetProcessInfo) + + +
+ + +## Documentation + +### IsWindows + +

Check if current os is windows.

+ +Signature: + +```go +func IsWindows() bool +``` + +Example:[Run](https://go.dev/play/p/zIflQgZNuxD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsWindows := system.IsWindows() + fmt.Println(isOsWindows) +} +``` + +### IsLinux + +

Check if current os is linux.

+ +Signature: + +```go +func IsLinux() bool +``` + +Example:[Run](https://go.dev/play/p/zIflQgZNuxD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsLinux := system.IsLinux() + fmt.Println(isOsLinux) +} +``` + +### IsMac + +

Check if current os is macos.

+ +Signature: + +```go +func IsMac() bool +``` + +Example:[Run](https://go.dev/play/p/Mg4Hjtyq7Zc) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + isOsMac := system.IsMac() + fmt.Println(isOsMac) +} +``` + +### GetOsEnv + +

Gets the value of the environment variable named by the key.

+ +Signature: + +```go +func GetOsEnv(key string) string +``` + +Example:[Run](https://go.dev/play/p/D88OYVCyjO-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + result := system.GetOsEnv("foo") + + fmt.Println(err) + fmt.Println(result) + // Output: + // + // abc +} +``` + +### SetOsEnv + +

Sets the value of the environment variable named by the key.

+ +Signature: + +```go +func SetOsEnv(key, value string) error +``` + +Example:[Run](https://go.dev/play/p/D88OYVCyjO-) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + result := system.GetOsEnv("foo") + + fmt.Println(err) + fmt.Println(result) + // Output: + // + // abc +} +``` + +### RemoveOsEnv + +

Remove a single environment variable.

+ +Signature: + +```go +func RemoveOsEnv(key string) error +``` + +Example:[Run](https://go.dev/play/p/fqyq4b3xUFQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err1 := system.SetOsEnv("foo", "abc") + result1 := GetOsEnv("foo") + + err2 := system.RemoveOsEnv("foo") + result2 := GetOsEnv("foo") + + fmt.Println(err1) + fmt.Println(err2) + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // + // + // abc + // +} +``` + +### CompareOsEnv + +

Get env named by the key and compare it with comparedEnv.

+ +Signature: + +```go +func CompareOsEnv(key, comparedEnv string) bool +``` + +Example:[Run](https://go.dev/play/p/BciHrKYOHbp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + err := system.SetOsEnv("foo", "abc") + if err != nil { + return + } + + result := system.CompareOsEnv("foo", "abc") + + fmt.Println(result) + + // Output: + // true +} +``` + +### ExecCommand + +

Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command. +The second parameter of the function is the cmd option control parameter. The type is func(*exec.Cmd). You can set the cmd attribute through this parameter.

+ +Signature: + +```go +type ( + Option func(*exec.Cmd) +) +func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error) +``` + +Example:[Run](https://go.dev/play/p/n-2fLyZef-4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + // linux or mac + stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) { + cmd.Dir = "/tmp" + }) + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + assert.Equal("", stderr) + + // windows + stdout, stderr, err = system.ExecCommand("dir") + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + + // error command + stdout, stderr, err = system.ExecCommand("abc") + fmt.Println("std out: ", stdout) + fmt.Println("std err: ", stderr) + if err != nil { + fmt.Println(err.Error()) + } +} +``` + +### GetOsBits + +

Get current os bits, 32bit or 64bit. return 32 or 64.

+ +Signature: + +```go +func GetOsBits() int +``` + +Example:[Run](https://go.dev/play/p/ml-_XH3gJbW) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + osBit := system.GetOsBits() + fmt.Println(osBit) // 32 or 64 +} +``` + +### StartProcess + +

Start a new process with the specified name and arguments.

+ +Signature: + +```go +func StartProcess(command string, args ...string) (int, error) +``` + +Example:[Run](https://go.dev/play/p/5GVol6ryS_X) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "2") + if err != nil { + return + } + + fmt.Println(pid) +} +``` + +### StopProcess + +

Stop a process by pid.

+ +Signature: + +```go +func StopProcess(pid int) error +``` + +Example:[Run](https://go.dev/play/p/jJZhRYGGcmD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "10") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = system.StopProcess(pid) + + fmt.Println(err) + + // Output: + // +} +``` + +### KillProcess + +

Kill a process by pid.

+ +Signature: + +```go +func KillProcess(pid int) error +``` + +Example:[Run](https://go.dev/play/p/XKmvV-ExBWa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("sleep", "10") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = system.KillProcess(pid) + + fmt.Println(err) + + // Output: + // +} +``` + +### GetProcessInfo + +

Retrieves detailed process information by pid.

+ +Signature: + +```go +func GetProcessInfo(pid int) (*ProcessInfo, error) +``` + +Example:[Run](https://go.dev/play/p/NQDVywEYYx7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("ls", "-a") + if err != nil { + return + } + + processInfo, err := system.GetProcessInfo(pid) + if err != nil { + return + } + + fmt.Println(processInfo) +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/tuple.md b/docs/en/api/packages/tuple.md new file mode 100644 index 00000000..2936567e --- /dev/null +++ b/docs/en/api/packages/tuple.md @@ -0,0 +1,1198 @@ +# Tuple + +tuple package implements tuple data type and some operations on it. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/tuple/tuple.go](https://github.com/duke-git/lancet/blob/main/tuple/tuple.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/pointer" +) +``` + +
+ +## Index + +- [Tuple2](#Tuple2) +- [Tuple2_Unbox](#Tuple2_Unbox) +- [Zip2](#Zip2) +- [Unzip2](#Unzip2) +- [Tuple3](#Tuple3) +- [Tuple3_Unbox](#Tuple3_Unbox) +- [Zip3](#Zip3) +- [Unzip3](#Unzip3) +- [Tuple4](#Tuple4) +- [Tuple4_Unbox](#Tuple4_Unbox) +- [Zip4](#Zip4) +- [Unzip4](#Unzip4) +- [Tuple5](#Tuple5) +- [Tuple5_Unbox](#Tuple5_Unbox) +- [Zip5](#Zip5) +- [Unzip5](#Unzip5) +- [Tuple6](#Tuple6) +- [Tuple6_Unbox](#Tuple6_Unbox) +- [Zip6](#Zip6) +- [Unzip6](#Unzip6) +- [Tuple7](#Tuple7) +- [Tuple7_Unbox](#Tuple7_Unbox) +- [Zip7](#Zip7) +- [Unzip7](#Unzip7) +- [Tuple8](#TTuple8uple6) +- [Tuple8_Unbox](#Tuple8_Unbox) +- [Zip8](#Zip8) +- [Unzip8](#Unzip8) +- [Tuple9](#Tuple9) +- [Tuple9_Unbox](#Tuple9_Unbox) +- [Zip9](#Zip9) +- [Unzip9](#Unzip9) +- [Tuple10](#Tuple10) +- [Tuple10_Unbox](#Tuple10_Unbox) +- [Zip10](#Zip10) +- [Unzip10](#Unzip10) + +
+ + +## Documentation + +### Tuple2 + +

Tuple2 represents a 2 elemnets tuple.

+ +Signature: + +```go +type Tuple2[A any, B any] struct { + FieldA A + FieldB B +} + +func NewTuple2[A any, B any](a A, b B) Tuple2[A, B] +``` + +Example:[Run](https://go.dev/play/p/3sHVqBQpLYN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple2(1, 0.1) + fmt.Printf("%v %v", t.FieldA, t.FieldB) + + // Output: 1 0.1 +} +``` + +### Tuple2_Unbox + +

Tuple2 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple2[A, B]) Unbox() (A, B) +``` + +Example:[Run](https://go.dev/play/p/0fD1qfCVwjm) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple2(1, 0.1) + v1, v2 := t.Unbox() + fmt.Printf("%v %v", v1, v2) + + // Output: 1 0.1 +} +``` + +### Zip2 + +

Create a slice of Tuple2, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] +``` + +Example:[Run](https://go.dev/play/p/4ncWJJ77Xio) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip2([]int{1}, []float64{0.1}) + fmt.Println(result) + + // Output: [{1 0.1}] +} +``` + +### Unzip2 + +

Create a group of slice from a slice of Tuple2.

+ +Signature: + +```go +func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) +``` + +Example:[Run](https://go.dev/play/p/KBecr60feXb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2 := tuple.Unzip2([]tuple.Tuple2[int, float64]{{FieldA: 1, FieldB: 0.1}}) + + fmt.Printf("%v %v", v1, v2) + + // Output: [1] [0.1] +} +``` + +### Tuple3 + +

Tuple3 represents a 3 elemnets tuple.

+ +Signature: + +```go +type Tuple3[A any, B any, C any] struct { + FieldA A + FieldB B + FieldC C +} + +func NewTuple3[A any, B any, C any](a A, b B c C) Tuple3[A, B, C] +``` + +Example:[Run](https://go.dev/play/p/FtH2sdCLlCf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple3(1, 0.1, "a") + fmt.Printf("%v %v %v", t.FieldA, t.FieldB, t.FieldC) + + // Output: 1 0.1 a +} +``` + +### Tuple3_Unbox + +

Tuple3 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple3[A, B, C]) Unbox() (A, B, C) +``` + +Example:[Run](https://go.dev/play/p/YojLy-id1BS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple3(1, 0.1, "a") + v1, v2, v3 := t.Unbox() + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: 1 0.1 a +} +``` + +### Zip3 + +

Create a slice of Tuple3, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] +``` + +Example:[Run](https://go.dev/play/p/97NgmsTILfu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip3([]int{1}, []float64{0.1}, []string{"a"}) + fmt.Println(result) + + // Output: [{1 0.1 a}] +} +``` + +### Unzip3 + +

Create a group of slice from a slice of Tuple3.

+ +Signature: + +```go +func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) +``` + +Example:[Run](https://go.dev/play/p/bba4cpAa7KO) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3 := tuple.Unzip3([]tuple.Tuple3[int, float64, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a"}, + }) + + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: [1] [0.1] [a] +} +``` + +### Tuple4 + +

Tuple4 represents a 4 elemnets tuple.

+ +Signature: + +```go +type Tuple4[A any, B any, C any, D any] struct { + FieldA A + FieldB B + FieldC C + FieldD D +} + +func NewTuple4[A any, B any, C any, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] +``` + +Example:[Run](https://go.dev/play/p/D2EqDz096tk) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple4(1, 0.1, "a", true) + fmt.Printf("%v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD) + + // Output: 1 0.1 a true +} +``` + +### Tuple4_Unbox + +

Tuple4 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple4[A, B, C, D]) Unbox() (A, B, C, D) +``` + +Example:[Run](https://go.dev/play/p/ACj9YuACGgW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple4(1, 0.1, "a", true) + v1, v2, v3, v4 := t.Unbox() + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: 1 0.1 a true +} +``` + +### Zip4 + +

Create a slice of Tuple4, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] +``` + +Example:[Run](https://go.dev/play/p/PEmTYVK5hL4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip4([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}) + fmt.Println(result) + + // Output: [{1 0.1 a true}] +} +``` + +### Unzip4 + +

Create a group of slice from a slice of Tuple4.

+ +Signature: + +```go +func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) +``` + +Example:[Run](https://go.dev/play/p/rb8z4gyYSRN) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4 := tuple.Unzip4([]tuple.Tuple4[int, float64, string, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true}, + }) + + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: [1] [0.1] [a] [true] +} +``` + +### Tuple5 + +

Tuple5 represents a 5 elemnets tuple.

+ +Signature: + +```go +type Tuple5[A any, B any, C any, D any, E any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E +} + +func NewTuple5[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] +``` + +Example:[Run](https://go.dev/play/p/2WndmVxPg-r) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple5(1, 0.1, "a", true, 2) + fmt.Printf("%v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE) + + // Output: 1 0.1 a true 2 +} +``` + +### Tuple5_Unbox + +

Tuple5 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple5[A, B, C, D, E]) Unbox() (A, B, C, D, E) +``` + +Example:[Run](https://go.dev/play/p/GyIyZHjCvoS) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple5(1, 0.1, "a", true, 2) + v1, v2, v3, v4, v5 := t.Unbox() + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: 1 0.1 a true 2 +} +``` + +### Zip5 + +

Create a slice of Tuple5, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip5[A any, B any, C any, D any, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] +``` + +Example:[Run](https://go.dev/play/p/fCAAJLMfBIP) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip5([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2}] +} +``` + +### Unzip5 + +

Create a group of slice from a slice of Tuple5.

+ +Signature: + +```go +func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) +``` + +Example:[Run](https://go.dev/play/p/gyl6vKfhqPb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5 := tuple.Unzip5([]tuple.Tuple5[int, float64, string, bool, int]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2}, + }) + + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: [1] [0.1] [a] [true] [2] +} +``` + +### Tuple6 + +

Tuple6 represents a 6 elemnets tuple.

+ +Signature: + +```go +type Tuple6[A any, B any, C any, D any, E any, F any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F +} + +func NewTuple6[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] +``` + +Example:[Run](https://go.dev/play/p/VjqcCwEJZbs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple6(1, 0.1, "a", true, 2, 2.2) + fmt.Printf("%v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF) + + // Output: 1 0.1 a true 2 2.2 +} +``` + +### Tuple6_Unbox + +

Tuple6 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple6[A, B, C, D, E, F]) Unbox() (A, B, C, D, E, F) +``` + +Example:[Run](https://go.dev/play/p/FjIHV7lpxmW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple6(1, 0.1, "a", true, 2, 2.2) + v1, v2, v3, v4, v5, v6 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: 1 0.1 a true 2 2.2 +} +``` + +### Zip6 + +

Create a slice of Tuple6, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip6[A any, B any, C any, D any, E any, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] +``` + +Example:[Run](https://go.dev/play/p/oWPrnUYuFHo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip6([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2}] +} +``` + +### Unzip6 + +

Create a group of slice from a slice of Tuple6.

+ +Signature: + +```go +func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) +``` + +Example:[Run](https://go.dev/play/p/l41XFqCyh5E) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6 := tuple.Unzip6([]tuple.Tuple6[int, float64, string, bool, int, float32]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2}, + }) + + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: [1] [0.1] [a] [true] [2] [2.2] +} +``` + +### Tuple7 + +

Tuple7 represents a 7 elemnets tuple.

+ +Signature: + +```go +type Tuple7[A any, B any, C any, D any, E any, F any, G any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G +} + +func NewTuple7[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] +``` + +Example:[Run](https://go.dev/play/p/dzAgv_Ezub9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + fmt.Printf("%v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG) + + // Output: 1 0.1 a true 2 2.2 b +} +``` + +### Tuple7_Unbox + +

Tuple7 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple7[A, B, C, D, E, F, G]) Unbox() (A, B, C, D, E, F, G) +``` + +Example:[Run](https://go.dev/play/p/R9I8qeDk0zs) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + v1, v2, v3, v4, v5, v6, v7 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: 1 0.1 a true 2 2.2 b +} +``` + +### Zip7 + +

Create a slice of Tuple7, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip7[A any, B any, C any, D any, E any, F any, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] +``` + +Example:[Run](https://go.dev/play/p/WUJuo897Egf) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip7([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b}] +} +``` + +### Unzip7 + +

Create a group of slice from a slice of Tuple7.

+ +Signature: + +```go +func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) +``` + +Example:[Run](https://go.dev/play/p/hws_P1Fr2j3) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7 := tuple.Unzip7([]tuple.Tuple7[int, float64, string, bool, int, float32, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] +} +``` + +### Tuple8 + +

Tuple8 represents a 8 elemnets tuple.

+ +Signature: + +```go +type Tuple8[A any, B any, C any, D any, E any, F any, G any, H any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H +} + +func NewTuple8[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H) Tuple8[A, B, C, D, E, F, G, H] +``` + +Example:[Run](https://go.dev/play/p/YA9S0rz3dRz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + fmt.Printf("%v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH) + + // Output: 1 0.1 a true 2 2.2 b c +} +``` + +### Tuple8_Unbox + +

Tuple8 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple8[A, B, C, D, E, F, G, H]) Unbox() (A, B, C, D, E, F, G, H) +``` + +Example:[Run](https://go.dev/play/p/PRxLBBb4SMl) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + v1, v2, v3, v4, v5, v6, v7, v8 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: 1 0.1 a true 2 2.2 b c +} +``` + +### Zip8 + +

Create a slice of Tuple8, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip8[A any, B any, C any, D any, E any, F any, G any, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] +``` + +Example:[Run](https://go.dev/play/p/8V9jWkuJfaQ) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip8([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c}] +} +``` + +### Unzip8 + +

Create a group of slice from a slice of Tuple8.

+ +Signature: + +```go +func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) +``` + +Example:[Run](https://go.dev/play/p/1SndOwGsZB4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8 := tuple.Unzip8([]tuple.Tuple8[int, float64, string, bool, int, float32, string, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] +} +``` + +### Tuple9 + +

Tuple9 represents a 9 elemnets tuple.

+ +Signature: + +```go + +type Tuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I +} + +func NewTuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I) Tuple9[A, B, C, D, E, F, G, H, I] + +``` + +Example:[Run](https://go.dev/play/p/yS2NGGtZpQr) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + fmt.Printf("%v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} +``` + +### Tuple9_Unbox + +

Tuple9 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unbox() (A, B, C, D, E, F, G, H, I) +``` + +Example:[Run](https://go.dev/play/p/oFJFGTAuOa8) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + t := tuple.NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + v1, v2, v3, v4, v5, v6, v7, v8, v9 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} +``` + +### Zip9 + +

Create a slice of Tuple9, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] +``` + +Example:[Run](https://go.dev/play/p/cgsL15QYnfz) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip9([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3}] +} +``` + +### Unzip9 + +

Create a group of slice from a slice of Tuple9.

+ +Signature: + +```go +func Unzip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) +``` + +Example:[Run](https://go.dev/play/p/91-BU_KURSA) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8, v9 := tuple.Unzip9([]tuple.Tuple9[int, float64, string, bool, int, float32, string, string, int64]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] +} +``` + +### Tuple10 + +

Tuple10 represents a 10 elemnets tuple.

+ +Signature: + +```go + +type Tuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I + FieldJ J +} + +func NewTuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a A, b B, c C, d D, e E, f F, g G, h H, i I, j J) Tuple10[A, B, C, D, E, F, G, H, I, J] + +``` + +Example:[Run](https://go.dev/play/p/799qqZg0hUv) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + type foo struct { + A string + } + t := tuple.NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI, t.FieldJ) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} +``` + +### Tuple10_Unbox + +

Tuple10 Unbox returns values in tuple.

+ +Signature: + +```go +func (t Tuple10[A, B, C, D, E, F, G, H, I, J]) Unbox() (A, B, C, D, E, F, G, H, I, J) +``` + +Example:[Run](https://go.dev/play/p/qfyx3x_X0Cu) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + type foo struct { + A string + } + t := tuple.NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} +``` + +### Zip10 + +

Create a slice of Tuple10, whose elements are correspond to the given slice elements.

+ +Signature: + +```go +func Zip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, j []J) []Tuple10[A, B, C, D, E, F, G, H, I, J] +``` + +Example:[Run](https://go.dev/play/p/YSR-2cXnrY4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + result := tuple.Zip10([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}, []bool{false}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3 false}] +} +``` + +### Unzip10 + +

Create a group of slice from a slice of Tuple10.

+ +Signature: + +```go +func Unzip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](tuples []Tuple10[A, B, C, D, E, F, G, H, I, J]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I, []J) +``` + +Example:[Run](https://go.dev/play/p/-taQB6Wfre_z) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/tuple" +) + +func main() { + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := tuple.Unzip10([]tuple.Tuple10[int, float64, string, bool, int, float32, string, string, int64, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3, FieldJ: false}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] [false] +} +``` diff --git a/docs/en/api/packages/validator.md b/docs/en/api/packages/validator.md new file mode 100644 index 00000000..2ab510dc --- /dev/null +++ b/docs/en/api/packages/validator.md @@ -0,0 +1,1571 @@ +# Validator + +Package validator contains some functions for data validation. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/validator/validator.go](https://github.com/duke-git/lancet/blob/main/validator/validator.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/validator" +) +``` + +
+ +## Index + +- [ContainChinese](#ContainChinese) +- [ContainLetter](#ContainLetter) +- [ContainLower](#ContainLower) +- [ContainUpper](#ContainUpper) +- [IsAlpha](#IsAlpha) +- [IsAllUpper](#IsAllUpper) +- [IsAllLower](#IsAllLower) +- [IsASCII](#IsASCII) +- [IsBase64](#IsBase64) +- [IsChineseMobile](#IsChineseMobile) +- [IsChineseIdNum](#IsChineseIdNum) +- [IsChinesePhone](#IsChinesePhone) +- [IsCreditCard](#IsCreditCard) +- [IsDns](#IsDns) +- [IsEmail](#IsEmail) +- [IsEmptyString](#IsEmptyString) +- [IsInt](#IsInt) +- [IsFloat](#IsFloat) +- [IsNumber](#IsNumber) +- [IsIntStr](#IsIntStr) +- [IsFloatStr](#IsFloatStr) +- [IsNumberStr](#IsNumberStr) +- [IsJSON](#IsJSON) +- [IsRegexMatch](#IsRegexMatch) +- [IsIp](#IsIp) +- [IsIpV4](#IsIpV4) +- [IsIpV6](#IsIpV6) +- [IsIpPort](#IsIpPort) +- [IsStrongPassword](#IsStrongPassword) +- [IsUrl](#IsUrl) +- [IsWeakPassword](#IsWeakPassword) +- [IsZeroValue](#IsZeroValue) +- [IsGBK](#IsGBK) +- [IsPrintable](#IsPrintable) +- [IsBin](#IsBin) +- [IsHex](#IsHex) +- [IsBase64URL](#IsBase64URL) +- [IsJWT](#IsJWT) +- [IsVisa](#IsVisa) +- [IsMasterCard](#IsMasterCard) +- [IsAmericanExpress](#IsAmericanExpress) +- [IsUnionPay](#IsUnionPay) +- [IsChinaUnionPay](#IsChinaUnionPay) + +
+ + + +## Documentation + +### ContainChinese + +

Check if the string contain mandarin chinese.

+ +Signature: + +```go +func ContainChinese(s string) bool +``` + +Example:[Run](https://go.dev/play/p/7DpU0uElYeM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainChinese("你好") + result2 := validator.ContainChinese("你好hello") + result3 := validator.ContainChinese("hello") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### ContainLetter + +

Check if the string contain at least one letter.

+ +Signature: + +```go +func ContainLetter(str string) bool +``` + +Example:[Run](https://go.dev/play/p/lqFD04Yyewp) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainLetter("你好") + result2 := validator.ContainLetter("&@#$%^&*") + result3 := validator.ContainLetter("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // false + // false + // true +} +``` + +### ContainLower + +

Check if the string contain at least one lower case letter a-z.

+ +Signature: + +```go +func ContainLower(str string) bool +``` + +Example:[Run](https://go.dev/play/p/Srqi1ItvnAA) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainLower("abc") + result2 := validator.ContainLower("aBC") + result3 := validator.ContainLower("ABC") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### ContainUpper + +

Check if the string contain at least one upper case letter A-Z.

+ +Signature: + +```go +func ContainUpper(str string) bool +``` + +Example:[Run](https://go.dev/play/p/CmWeBEk27-z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.ContainUpper("ABC") + result2 := validator.ContainUpper("abC") + result3 := validator.ContainUpper("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsAlpha + +

Check if the string contains only letters (a-zA-Z).

+ +Signature: + +```go +func IsAlpha(s string) bool +``` + +Example:[Run](https://go.dev/play/p/7Q5sGOz2izQ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAlpha("abc") + result2 := validator.IsAlpha("ab1") + result3 := validator.IsAlpha("") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsAllUpper + +

Check if string is all upper case letters A-Z.

+ +Signature: + +```go +func IsAllUpper(str string) bool +``` + +Example:[Run](https://go.dev/play/p/ZHctgeK1n4Z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAllUpper("ABC") + result2 := validator.IsAllUpper("ABc") + result3 := validator.IsAllUpper("AB1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsAllLower + +

Check if string is all lower case letters a-z.

+ +Signature: + +```go +func IsAllLower(str string) bool +``` + +Example:[Run](https://go.dev/play/p/GjqCnOfV6cM) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAllLower("abc") + result2 := validator.IsAllLower("abC") + result3 := validator.IsAllLower("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsASCII + +

Checks if string is all ASCII char.

+ +Signature: + +```go +func IsASCII(str string) bool +``` + +Example:[Run](https://go.dev/play/p/hfQNPLX0jNa) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsASCII("ABC") + result2 := validator.IsASCII("123") + result3 := validator.IsASCII("") + result4 := validator.IsASCII("😄") + result5 := validator.IsASCII("你好") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // false + // false +} +``` + +### IsBase64 + +

Check if the string is base64 string.

+ +Signature: + +```go +func IsBase64(base64 string) bool +``` + +Example:[Run](https://go.dev/play/p/sWHEySAt6hl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBase64("aGVsbG8=") + result2 := validator.IsBase64("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChineseMobile + +

Check if the string is valid chinese mobile number.

+ +Signature: + +```go +func IsChineseMobile(mobileNum string) bool +``` + +Example:[Run](https://go.dev/play/p/GPYUlGTOqe3) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChineseMobile("13263527980") + result2 := validator.IsChineseMobile("434324324") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChineseIdNum + +

Check if the string is chinese id number.

+ +Signature: + +```go +func IsChineseIdNum(id string) bool +``` + +Example:[Run](https://go.dev/play/p/d8EWhl2UGDF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChineseIdNum("210911192105130715") + result2 := validator.IsChineseIdNum("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChinesePhone + +

Check if the string is chinese phone number.

+ +Signature: + +```go +func IsChinesePhone(phone string) bool +``` + +Example:[Run](https://go.dev/play/p/RUD_-7YZJ3I) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChinesePhone("010-32116675") + result2 := validator.IsChinesePhone("123-87562") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsCreditCard + +

Check if the string is credit card.

+ +Signature: + +```go +func IsCreditCard(creditCart string) bool +``` + +Example:[Run](https://go.dev/play/p/sNwwL6B0-v4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsCreditCard("4111111111111111") + result2 := validator.IsCreditCard("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsDns + +

Check if the string is valid dns.

+ +Signature: + +```go +func IsDns(dns string) bool +``` + +Example:[Run](https://go.dev/play/p/jlYApVLLGTZ) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsDns("abc.com") + result2 := validator.IsDns("a.b.com") + result3 := validator.IsDns("http://abc.com") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsEmail + +

Check if the string is email address.

+ +Signature: + +```go +func IsEmail(email string) bool +``` + +Example:[Run](https://go.dev/play/p/Os9VaFlT33G) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsEmail("abc@xyz.com") + result2 := validator.IsEmail("a.b@@com") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsEmptyString + +

Check if the string is empty or not.

+ +Signature: + +```go +func IsEmptyString(s string) bool +``` + +Example:[Run](https://go.dev/play/p/dpzgUjFnBCX) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsEmptyString("") + result2 := validator.IsEmptyString(" ") + result3 := validator.IsEmptyString("\t") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} +``` + +### IsInt + +

Check if the value is integer(int, unit) or not.

+ +Signature: + +```go +func IsInt(v any) bool +``` + +Example:[Run](https://go.dev/play/p/eFoIHbgzl-z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsInt("") + result2 := validator.IsInt("3") + result3 := validator.IsInt(0.1) + result4 := validator.IsInt(0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### IsFloat + +

Check if the value is float(float32, float34) or not.

+ +Signature: + +```go +func IsFloat(v any) bool +``` + +Example:[Run](https://go.dev/play/p/vsyG-sxr99_Z) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsFloat("") + result2 := validator.IsFloat("3") + result3 := validator.IsFloat(0) + result4 := validator.IsFloat(0.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} +``` + +### IsNumber + +

Check if the value is number(integer, float) or not.

+ +Signature: + +```go +func IsNumber(v any) bool +``` + +Example:[Run](https://go.dev/play/p/mdJHOAvtsvF) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsNumber("") + result2 := validator.IsNumber("3") + result3 := validator.IsNumber(0.1) + result4 := validator.IsNumber(0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // true + // true +} +``` + +### IsIntStr + +

Check if the string can convert to a integer.

+ +Signature: + +```go +func IsIntStr(s string) bool +``` + +Example:[Run](https://go.dev/play/p/jQRtFv-a0Rk) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIntStr("+3") + result2 := validator.IsIntStr("-3") + result3 := validator.IsIntStr("3.") + result4 := validator.IsIntStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsFloatStr + +

Check if the string can convert to a float.

+ +Signature: + +```go +func IsFloatStr(s string) bool +``` + +Example:[Run](https://go.dev/play/p/LOYwS_Oyl7U) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsFloatStr("3.") + result2 := validator.IsFloatStr("+3.") + result3 := validator.IsFloatStr("12") + result4 := validator.IsFloatStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### IsNumberStr + +

Check if the string can convert to a number.

+ +Signature: + +```go +func IsNumberStr(s string) bool +``` + +Example:[Run](https://go.dev/play/p/LzaKocSV79u) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsNumberStr("3.") + result2 := validator.IsNumberStr("+3.") + result3 := validator.IsNumberStr("+3e2") + result4 := validator.IsNumberStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} +``` + +### IsAlphaNumeric + +

Check if the string is alphanumeric.

+ +Signature: + +```go +func IsAlphaNumeric(s string) bool +``` + +Example:[Run](https://go.dev/play/p/RHeESLrLg9c) + +```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 +} +``` + +### IsJSON + +

Check if the string is valid JSON.

+ +Signature: + +```go +func IsJSON(str string) bool +``` + +Example:[Run](https://go.dev/play/p/8Kip1Itjiil) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsJSON("{}") + result2 := validator.IsJSON("{\"name\": \"test\"}") + result3 := validator.IsJSON("") + result4 := validator.IsJSON("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsRegexMatch + +

Check if the string match the regexp.

+ +Signature: + +```go +func IsRegexMatch(s, regex string) bool +``` + +Example:[Run](https://go.dev/play/p/z_XeZo_litG) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsRegexMatch("abc", `^[a-zA-Z]+$`) + result2 := validator.IsRegexMatch("ab1", `^[a-zA-Z]+$`) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsIp + +

Check if the string is a ip address.

+ +Signature: + +```go +func IsIp(ipstr string) bool +``` + +Example:[Run](https://go.dev/play/p/FgcplDvmxoD) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIp("127.0.0.1") + result2 := validator.IsIp("::0:0:0:0:0:0:1") + result3 := validator.IsIp("127.0.0") + result4 := validator.IsIp("::0:0:0:0:") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsIpV4 + +

Check if the string is a ipv4 address.

+ +Signature: + +```go +func IsIpV4(ipstr string) bool +``` + +Example:[Run](https://go.dev/play/p/zBGT99EjaIu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIpV4("127.0.0.1") + result2 := validator.IsIpV4("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsIpV6 + +

Check if the string is a ipv6 address.

+ +Signature: + +```go +func IsIpV6(ipstr string) bool +``` + +Example:[Run](https://go.dev/play/p/AHA0r0AzIdC) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsIpV6("127.0.0.1") + result2 := validator.IsIpV6("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} +``` + +### IsIpPort + +

Check if the string is ip:port

+ +Signature: + +```go +func IsIpPort(str string) bool +``` + +Example:[Run](https://go.dev/play/p/xUmls_b9L29) + +```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 +} +``` + +### IsStrongPassword + +

Check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?gt<)).

+ +Signature: + +```go +func IsStrongPassword(password string, length int) bool +``` + +Example:[Run](https://go.dev/play/p/QHdVcSQ3uDg) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsStrongPassword("abcABC", 6) + result2 := validator.IsStrongPassword("abcABC123@#$", 10) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} +``` + +### IsUrl + +

Check if the string is url.

+ +Signature: + +```go +func IsUrl(str string) bool +``` + +Example:[Run](https://go.dev/play/p/pbJGa7F98Ka) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsUrl("abc.com") + result2 := validator.IsUrl("http://abc.com") + result3 := validator.IsUrl("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} +``` + +### IsWeakPassword + +

Checks if the string is weak password(only letter or only number or letter + number) +.

+ +Signature: + +```go +func IsWeakPassword(password string, length int) bool +``` + +Example:[Run](https://go.dev/play/p/wqakscZH5gH) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsWeakPassword("abcABC") + result2 := validator.IsWeakPassword("abc123@#$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsZeroValue + +

Checks if passed value is a zero value.

+ +Signature: + +```go +func IsZeroValue(value any) bool +``` + +Example:[Run](https://go.dev/play/p/UMrwaDCi_t4) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsZeroValue("") + result2 := validator.IsZeroValue(0) + result3 := validator.IsZeroValue("abc") + result4 := validator.IsZeroValue(1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsGBK + +

Checks if data encoding is gbk(Chinese character internal code extension specification). this function is implemented by whether double bytes fall within the encoding range of gbk,while each byte of utf-8 encoding format falls within the encoding range of gbk.Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding and then call IsGBK() to check gbk encoding. like the example.

+ +Signature: + +```go +func IsGBK(data []byte) bool +``` + +Example:[Run](https://go.dev/play/p/E2nt3unlmzP) + +```go +import ( + "fmt" + "golang.org/x/text/encoding/simplifiedchinese" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + str := "你好" + gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str)) + + result := validator.IsGBK(gbkData) + + fmt.Println(result) + + // Output: + // true +} +``` + +### IsPrintable + +

Checks if string is all printable chars.

+ +Signature: + +```go +func IsPrintable(str string) bool +``` + +Example:[Run](https://go.dev/play/p/Pe1FE2gdtTP) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsPrintable("ABC") + result2 := validator.IsPrintable("{id: 123}") + result3 := validator.IsPrintable("") + result4 := validator.IsPrintable("😄") + result5 := validator.IsPrintable("\u0000") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // true + // false +} +``` + +### IsBin + +

Checks if a give string is a valid binary value or not.

+ +Signature: + +```go +func IsBin(v string) bool +``` + +Example:[Run](https://go.dev/play/p/ogPeg2XJH4P) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBin("0101") + result2 := validator.IsBin("0b1101") + result3 := validator.IsBin("b1101") + result4 := validator.IsBin("1201") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsHex + +

Checks if a give string is a valid hexadecimal value or not.

+ +Signature: + +```go +func IsHex(v string) bool +``` + +Example:[Run](https://go.dev/play/p/M2qpHbEwmm7) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsHex("0xabcde") + result2 := validator.IsHex("0XABCDE") + result3 := validator.IsHex("cdfeg") + result4 := validator.IsHex("0xcdfeg") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsBase64URL + +

Checks if a give string is a valid URL-safe Base64 encoded string.

+ +Signature: + +```go +func IsBase64URL(v string) bool +``` + +Example:[Run](https://go.dev/play/p/vhl4mr8GZ6S) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ") + result2 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ==") + result3 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ=") + result4 := validator.IsBase64URL("SAGsbG8sIHdvcmxkIQ===") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} +``` + +### IsJWT + +

Checks if a give string is is a valid JSON Web Token (JWT).

+ +Signature: + +```go +func IsJWT(v string) bool +``` + +Example:[Run](https://go.dev/play/p/R6Op7heJbKI) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910") + result2 := validator.IsJWT("abc") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsVisa + +

Checks if a give string is a valid visa card nubmer or not.

+ +Signature: + +```go +func IsVisa(v string) bool +``` + +Example:[Run](https://go.dev/play/p/SdS2keOyJsl) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsVisa("4111111111111111") + result2 := validator.IsVisa("123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsMasterCard + +

Checks if a give string is a valid mastercard nubmer or not.

+ +Signature: + +```go +func IsMasterCard(v string) bool +``` + +Example:[Run](https://go.dev/play/p/CwWBFRrG27b) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsMasterCard("5425233430109903") + result2 := validator.IsMasterCard("4111111111111111") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsAmericanExpress + +

Checks if a give string is a valid american express nubmer or not.

+ +Signature: + +```go +func IsAmericanExpress(v string) bool +``` + +Example:[Run](https://go.dev/play/p/HIDFpcOdpkd) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsAmericanExpress("342883359122187") + result2 := validator.IsAmericanExpress("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsVisa + +

Checks if a give string is a valid union pay nubmer or not.

+ +Signature: + +```go +func IsUnionPay(v string) bool +``` + +Example:[Run](https://go.dev/play/p/CUHPEwEITDf) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsUnionPay("6221263430109903") + result2 := validator.IsUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### IsChinaUnionPay + +

Checks if a give string is a valid china union pay nubmer or not.

+ +Signature: + +```go +func IsChinaUnionPay(v string) bool +``` + +Example:[Run](https://go.dev/play/p/yafpdxLiymu) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/validator" +) + +func main() { + result1 := validator.IsChinaUnionPay("6250941006528599") + result2 := validator.IsChinaUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` diff --git a/docs/en/api/packages/xerror.md b/docs/en/api/packages/xerror.md new file mode 100644 index 00000000..364e821f --- /dev/null +++ b/docs/en/api/packages/xerror.md @@ -0,0 +1,543 @@ +# Xerror + +Package xerror implements helpers for errors. + +
+ +## Source: + +- [https://github.com/duke-git/lancet/blob/main/xerror/xerror.go](https://github.com/duke-git/lancet/blob/main/xerror/xerror.go) + +
+ +## Usage: + +```go +import ( + "github.com/duke-git/lancet/v2/xerror" +) +``` + +
+ +## Index + +- [New](#New) +- [Wrap](#Wrap) +- [Unwrap](#Unwrap) +- [XError_Wrap](#XError_Wrap) +- [XError_Unwrap](#XError_Unwrap) +- [XError_With](#XError_With) +- [XError_Is](#XError_Is) +- [XError_Id](#XError_Id) +- [XError_Values](#XError_Values) +- [XError_StackTrace](#XError_StackTrace) +- [XError_Info](#XError_Info) +- [XError_Error](#XError_Error) +- [TryUnwrap](#TryUnwrap) +- [TryCatch](#TryCatch) + +
+ +## Documentation + +### New + +

Creates a new XError pointer instance with message.

+ +Signature: + +```go +type XError struct { + id string + message string + stack *stack + cause error + values map[string]any +} + +func New(format string, args ...any) *XError +``` + +Example:[Run](https://go.dev/play/p/w4oWZts7q7f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + fmt.Println(err.Error()) + + // Output: + // error +} +``` + +### Wrap + +

Creates a new XError pointer instance based on error object, and add message.

+ +Signature: + +```go +func Wrap(cause error, message ...any) *XError +``` + +Example:[Run](https://go.dev/play/p/5385qT2dCi4) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("wrong password") + wrapErr := xerror.Wrap(err, "error") + + fmt.Println(wrapErr.Error()) + + // Output: + // error: wrong password +} +``` + +### Unwrap + +

Returns unwrapped XError from err by errors.As. If no XError, returns nil.

+ +Signature: + +```go +func Unwrap(err error) *XError +``` + +Example:[Run](https://go.dev/play/p/LKMLep723tu) + +```go +package main + +import ( + "fmt" + "github.com/pkg/errors" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + wrapErr := errors.Wrap(err1, "oops") + + err := xerror.Unwrap(wrapErr) + + values := err.Values() + fmt.Println(values["level"]) + + // Output: + // high +} +``` + +### XError_Wrap + +

Creates a new XError and copy message and id to new one.

+ +Signature: + +```go +func (e *XError) Wrap(cause error) *XError +``` + +Example:[Run](https://go.dev/play/p/RpjJ5u5sc97) + +```go +package main + +import ( + "fmt" + "errors" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + fmt.Println(err2.Error()) + + // Output: + // error: invalid username +} +``` + +### XError_Unwrap + +

Compatible with github.com/pkg/errors.

+ +Signature: + +```go +func (e *XError) Unwrap() error +``` + +Example:[Run](https://go.dev/play/p/VUXJ8BST4c6) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + err := err2.Unwrap() + + fmt.Println(err.Error()) + + // Output: + // invalid username +} +``` + +### XError_With + +

Adds key and value related to the XError object.

+ +Signature: + +```go +func (e *XError) With(key string, value any) *XError +``` + +Example:[Run](https://go.dev/play/p/ow8UISXX_Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} +``` + +### XError_Id + +

Sets XError object id to check equality in XError.Is.

+ +Signature: + +```go +func (e *XError) Id(id string) *XError +``` + +Example:[Run](https://go.dev/play/p/X6HBlsy58U9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").Id("e001") + err2 := xerror.New("error").Id("e001") + err3 := xerror.New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} +``` + +### XError_Is + +

Checks if target error is XError and Error.id of two errors are matched.

+ +Signature: + +```go +func (e *XError) Is(target error) bool +``` + +Example:[Run](https://go.dev/play/p/X6HBlsy58U9) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err1 := xerror.New("error").Id("e001") + err2 := xerror.New("error").Id("e001") + err3 := xerror.New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} +``` + +### XError_Values + +

Returns map of key and value that is set by With. All wrapped xerror.XError key and values will be merged. Key and values of wrapped error is overwritten by upper xerror.XError.

+ +Signature: + +```go +func (e *XError) Values() map[string]any +``` + +Example:[Run](https://go.dev/play/p/ow8UISXX_Dp) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} +``` + + +### XError_StackTrace + +

Returns stack trace which is compatible with pkg/errors.

+ +Signature: + +```go +func (e *XError) StackTrace() StackTrace +``` + +Example:[Run](https://go.dev/play/p/6FAvSQpa7pc) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + + stacks := err.Stacks() + + fmt.Println(stacks[0].Func) + fmt.Println(stacks[0].Line) + + containFile := strings.Contains(stacks[0].File, "xxx.go") + fmt.Println(containFile) +} +``` + + +### XError_Info + +

Returns information of xerror, which can be printed.

+ +Signature: + +```go +func (e *XError) Info() *errInfo +``` + +Example:[Run](https://go.dev/play/p/1ZX0ME1F-Jb) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + cause := errors.New("error") + err := xerror.Wrap(cause, "invalid username").Id("e001").With("level", "high") + + errInfo := err.Info() + + fmt.Println(errInfo.Id) + fmt.Println(errInfo.Cause) + fmt.Println(errInfo.Values["level"]) + fmt.Println(errInfo.Message) + + // Output: + // e001 + // error + // high + // invalid username +} +``` + + +### XError_Error + +

Error implements standard error interface.

+ +Signature: + +```go +func (e *XError) Error() string +``` + +Example:[Run](https://go.dev/play/p/w4oWZts7q7f) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + err := xerror.New("error") + fmt.Println(err.Error()) + + // Output: + // error +} +``` +### TryUnwrap + +

TryUnwrap if err is nil then it returns a valid value. If err is not nil, Unwrap panics with err.

+ +Signature: + +```go +func TryUnwrap[T any](val T, err error) T +``` + +Example:[Run](https://go.dev/play/p/acyZVkNZEeW) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + result1 := xerror.TryUnwrap(strconv.Atoi("42")) + fmt.Println(result1) + + _, err := strconv.Atoi("4o2") + defer func() { + v := recover() + result2 := reflect.DeepEqual(err.Error(), v.(*strconv.NumError).Error()) + fmt.Println(result2) + }() + + xerror.TryUnwrap(strconv.Atoi("4o2")) + + // Output: + // 42 + // true +} +``` + +### TryCatch + +

Simple simulation of Java-style try-catch. It does not align with Go's error-handling philosophy. It is recommended to use it with caution.

+ +Signature: + +```go +func NewTryCatch(ctx context.Context) *TryCatch + +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch + +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch + +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch + +func (tc *TryCatch) Do() +``` + +Example:[Run](https://go.dev/play/p/D5Mdb0mRj0P) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + calledFinally := false + calledCatch := false + + tc := xerror.NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error message ") + }).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() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +} +``` diff --git a/docs/en/guide/contribution_guide.md b/docs/en/guide/contribution_guide.md new file mode 100644 index 00000000..f16cfb2a --- /dev/null +++ b/docs/en/guide/contribution_guide.md @@ -0,0 +1,37 @@ +# Lancet Contribution Guide + +Hi! Thank you for choosing Lancet. + +Lancet is a powerful, efficient, and reusable util function library of go. It makes Go dev easier by taking the hassle out of working with concurrency, net, math, slice, string, etc. + +We are excited that you are interested in contributing to lancet. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. + +## Issue Guidelines + +- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. + +- Before submitting an issue, please check if similar problems have already been issued. + +- Please specify which version of Lancet and Go you are using, and provide OS information. [Go Playground](https://go.dev/play/) is recommended to build a live demo so that your issue can be reproduced clearly. + +## Pull Request Guidelines + +- Fork this repository to your own account. Do not create branches here. + +- Commit info should be formatted as `type(scope): info about commit`. eg. `fix(package): [scrollbar] fix xxx bug`. + + 1. type: type must be one of [chore, docs, feat, fix, refactor, release, test]. + + 2. scope: scope must be one of [package, file, internal]. + + 3. header: header must not be longer than 72 characters. + +- Rebase before creating a PR to keep commit history clear. + +- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass. + +- Make sure PRs are created to `rc` branch instead of other branch. + +- If your PR fixes a bug, please provide a description about the related bug. + +- If the PR is for a new feature, make sure to complete the relevant documentation (/lancet/docs/en/api/packages). diff --git a/docs/en/guide/contributors.md b/docs/en/guide/contributors.md new file mode 100644 index 00000000..59a7c1c3 --- /dev/null +++ b/docs/en/guide/contributors.md @@ -0,0 +1,6 @@ +# Contributors +Thank you to all the people who contributed to lancet! + + + + \ No newline at end of file diff --git a/docs/en/guide/getting_started.md b/docs/en/guide/getting_started.md new file mode 100644 index 00000000..3994be80 --- /dev/null +++ b/docs/en/guide/getting_started.md @@ -0,0 +1,50 @@ +--- +outline: deep +--- + +# Installation + +1. For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions was rewriten with generics of go1.18. + +```go +go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x +``` + +2. For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.6. + +```go +go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x +``` + + +## Usage + +Lancet organizes the code into package structure, and you need to import the corresponding package name when use it. For example, if you use string-related functions, just import the strutil package like below: + +```go +import "github.com/duke-git/lancet/v2/strutil" +``` + +## Example + +Here takes the string function `Reverse` (reverse order string) as an example, and the strutil package needs to be imported. + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + s := "hello" + rs := strutil.Reverse(s) + fmt.Println(rs) //olleh +} +``` + + +## More + +Check out the [API](https://www.golancet.cn/en/api/overview.html) for details. diff --git a/docs/en/guide/introduction.md b/docs/en/guide/introduction.md new file mode 100644 index 00000000..b82e12af --- /dev/null +++ b/docs/en/guide/introduction.md @@ -0,0 +1,18 @@ +--- +outline: deep +--- + +# What is lancet? + +Lancet is a powerful, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js. + + +## Why lancet? + +Lancet makes Go dev easier by taking the hassle out of working with concurrency, net, math, slice, string, etc. +Lancet's utility methods are great for: + +- Iterating slice and array. +- Manipulating strings. +- Work with net and http. +- Other tools, eg. random, crypto, stream, retry, etc. diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 00000000..0f3fe3ee --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,53 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "Lancet" + text: "A powerful util function library of Go" + tagline: Simple, powerful, and efficient. + actions: + - theme: brand + text: Get Started + link: /en/guide/getting_started + - theme: alt + text: View on GitHub + link: https://github.com/duke-git/lancet + + image: + src: /lancet_logo.png + alt: lancet + +features: + - title: Powerful + icon: 💪 + details: support 600+ go util functions. inclueds string, slice, datetime, net, crypto, concurrency, etc. + - title: Modular by design + icon: 🏗 + details: Each module is designed as a package with no coupling between modules. + - title: Pure + icon: 💅 + details: Only depends on two kinds of libraries, go standard library and golang.org/x. + - title: Simple + icon: 👏 + details: Well structure, test for every exported function. +--- + +

+ + +

+ +Become a sponsor \ No newline at end of file diff --git a/docs/en/sponsor/sponsor.md b/docs/en/sponsor/sponsor.md new file mode 100644 index 00000000..1efe5d04 --- /dev/null +++ b/docs/en/sponsor/sponsor.md @@ -0,0 +1,41 @@ + + +### Sponsor me + +Hello, I am a software developer and have been engaged in development work for 15 years. Love open source software. And be willing to put in the energy for it. I am the creator of project [lancet](https://github.com/duke-git/lancet). Since Lancet was released as open source in 2021, it has been used by more than 1700 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship. + + + + + +*Donated funds will be used to maintain this website and pay for cloud server costs. Or just buy me a cup of ☕️ when I'm sleepy writing code.* \ No newline at end of file diff --git a/docs/fileutil.md b/docs/fileutil.md deleted file mode 100644 index 61a7b4de..00000000 --- a/docs/fileutil.md +++ /dev/null @@ -1,444 +0,0 @@ -# Fileutil -Package fileutil implements some basic functions for file operations. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/fileutil/file.go](https://github.com/duke-git/lancet/blob/main/fileutil/file.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/fileutil" -) -``` - -
- -## Index -- [ClearFile](#ClearFile) -- [CreateFile](#CreateFile) -- [CopyFile](#CopyFile) -- [FileMode](#FileMode) -- [MiMeType](#MiMeType) -- [IsExist](#IsExist) -- [IsLink](#IsLink) -- [IsDir](#IsDir) -- [ListFileNames](#ListFileNames) -- [RemoveFile](#RemoveFile) -- [ReadFileToString](#ReadFileToString) -- [ReadFileByLine](#ReadFileByLine) -- [Zip](#Zip) - -- [UnZip](#UnZip) - -
- -## Documentation - - - -### ClearFile -

Clear the file content, write empty string to the file.

- -Signature: - -```go -func ClearFile(path string) error -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.ClearFile("./test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - -### CreateFile -

Create file in path. return true if create succeed.

- -Signature: - -```go -func CreateFile(path string) bool -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isCreatedSucceed := fileutil.CreateFile("./test.txt") - fmt.Println(isCreatedSucceed) -} -``` - - -### CopyFile -

Copy src file to dest file. If dest file exist will overwrite it.

- -Signature: - -```go -func CopyFile(srcFilePath string, dstFilePath string) error -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.CopyFile("./test.txt", "./test_copy.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### FileMode -

Return file mode infomation.

- -Signature: - -```go -func FileMode(path string) (fs.FileMode, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - mode, err := fileutil.FileMode("./test.txt") - if err != nil { - fmt.Println(err) - } - fmt.Println(mode) -} -``` - - - -### MiMeType -

Get file mime type, 'file' param's type should be string or *os.File.

- -Signature: - -```go -func MiMeType(file interface{}) string -``` -Example: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - type1 := fileutil.MiMeType("./test.txt") - fmt.Println(type1) //text/plain; charset=utf-8 - - f, _ := os.Open("./file.go") - type2 := fileutil.MiMeType(f) - fmt.Println(type2) //text/plain; charset=utf-8 -} -``` - - - - -### IsExist -

Checks if a file or directory exists.

- -Signature: - -```go -func IsExist(path string) bool -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - fileutil.CreateFile("./test.txt") - isFileExist := fileutil.IsExist("./test.txt") - fmt.Println(isFileExist) //true -} -``` - - - -### IsLink -

Checks if a file is symbol link or not.

- -Signature: - -```go -func IsLink(path string) bool -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isLinkFile := fileutil.IsLink("./test.txt") - fmt.Println(isLinkFile) //false -} -``` - - - -### IsDir -

Checks if the path is directy or not.

- -Signature: - -```go -func IsDir(path string) bool -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isDir := fileutil.IsDir("./") - fmt.Println(isDir) //true - - isDir = fileutil.IsDir("./test.txt") - fmt.Println(isDir) //false -} -``` - - - -### ListFileNames -

List all file names in given path.

- -Signature: - -```go -func ListFileNames(path string) ([]string, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - fileNames, _ := fileutil.ListFileNames("./") - fmt.Println(fileNames) -} -``` - - - -### RemoveFile -

Remove the file of path.

- -Signature: - -```go -func RemoveFile(path string) error -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.RemoveFile("./test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - -### ReadFileToString -

Return string of file content.

- -Signature: - -```go -func ReadFileToString(path string) (string, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - path := "./test.txt" - fileutil.CreateFile(path) - - f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) - f.WriteString("hello world") - - content, _ := fileutil.ReadFileToString(path) - fmt.Println(content) //hello world -} -``` - - - -### ReadFileByLine -

Read file line by line, and return slice of lines

- -Signature: - -```go -func ReadFileByLine(path string)([]string, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - path := "./text.txt" - fileutil.CreateFile(path) - - f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) - defer f.Close() - f.WriteString("hello\nworld") - - contents, _ := fileutil.ReadFileByLine(path) - fmt.Println(contents) //[]string{"hello", "world"} -} -``` - - - -### Zip -

Create a zip file of fpath, fpath could be a file or a directory.

- -Signature: - -```go -func Zip(fpath string, destPath string) error -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.Zip("./test.txt", "./test.zip") - if err != nil { - fmt.Println(err) - } -} -``` - - - - -### UnZip -

Unzip the file and save it to dest path.

- -Signature: - -```go -func UnZip(zipFile string, destPath string) error -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.Zip("./test.zip", "./unzip/test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - - - - diff --git a/docs/fileutil_zh-CN.md b/docs/fileutil_zh-CN.md deleted file mode 100644 index d25ce566..00000000 --- a/docs/fileutil_zh-CN.md +++ /dev/null @@ -1,444 +0,0 @@ -# Fileutil -fileutil包支持文件基本操作。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/fileutil/file.go](https://github.com/duke-git/lancet/blob/main/fileutil/file.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/fileutil" -) -``` - -
- -## 目录 -- [ClearFile](#ClearFile) -- [CreateFile](#CreateFile) -- [CopyFile](#CopyFile) -- [FileMode](#FileMode) -- [MiMeType](#MiMeType) -- [IsExist](#IsExist) -- [IsLink](#IsLink) -- [IsDir](#IsDir) - -- [ListFileNames](#ListFileNames) -- [RemoveFile](#RemoveFile) -- [ReadFileToString](#ReadFileToString) -- [ReadFileByLine](#ReadFileByLine) -- [Zip](#Zip) -- [UnZip](#UnZip) - -
- -## 文档 - - - -### ClearFile -

清空文件内容

- -函数签名: - -```go -func ClearFile(path string) error -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.ClearFile("./test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - -### CreateFile -

创建文件,创建成功返回true, 否则返回false

- -函数签名: - -```go -func CreateFile(path string) bool -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isCreatedSucceed := fileutil.CreateFile("./test.txt") - fmt.Println(isCreatedSucceed) -} -``` - - -### CopyFile -

拷贝文件,会覆盖原有的拷贝文件

- -函数签名: - -```go -func CopyFile(srcFilePath string, dstFilePath string) error -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.CopyFile("./test.txt", "./test_copy.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### FileMode -

获取文件mode信息

- -函数签名: - -```go -func FileMode(path string) (fs.FileMode, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - mode, err := fileutil.FileMode("./test.txt") - if err != nil { - fmt.Println(err) - } - fmt.Println(mode) -} -``` - - - -### MiMeType -

获取文件mime类型, 'file'参数的类型必须是string或者*os.File

- -函数签名: - -```go -func MiMeType(file interface{}) string -``` -例子: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - type1 := fileutil.MiMeType("./test.txt") - fmt.Println(type1) //text/plain; charset=utf-8 - - f, _ := os.Open("./file.go") - type2 := fileutil.MiMeType(f) - fmt.Println(type2) //text/plain; charset=utf-8 -} -``` - - - - -### IsExist -

判断文件或目录是否存在

- -函数签名: - -```go -func IsExist(path string) bool -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - fileutil.CreateFile("./test.txt") - isFileExist := fileutil.IsExist("./test.txt") - fmt.Println(isFileExist) //true -} -``` - - - -### IsLink -

判断文件是否是符号链接

- -函数签名: - -```go -func IsLink(path string) bool -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isLinkFile := fileutil.IsLink("./test.txt") - fmt.Println(isLinkFile) //false -} -``` - - - -### IsDir -

判断目录是否存在

- -函数签名: - -```go -func IsDir(path string) bool -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - isDir := fileutil.IsDir("./") - fmt.Println(isDir) //true - - isDir = fileutil.IsDir("./test.txt") - fmt.Println(isDir) //false -} -``` - - - -### ListFileNames -

返回目录下所有文件名

- -函数签名: - -```go -func ListFileNames(path string) ([]string, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - fileNames, _ := fileutil.ListFileNames("./") - fmt.Println(fileNames) -} -``` - - - -### RemoveFile -

删除文件

- -函数签名: - -```go -func RemoveFile(path string) error -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.RemoveFile("./test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - -### ReadFileToString -

读取文件内容并返回字符串

- -函数签名: - -```go -func ReadFileToString(path string) (string, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - path := "./test.txt" - fileutil.CreateFile(path) - - f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) - f.WriteString("hello world") - - content, _ := fileutil.ReadFileToString(path) - fmt.Println(content) //hello world -} -``` - - - -### ReadFileByLine -

按行读取文件内容,返回字符串切片包含每一行

- -函数签名: - -```go -func ReadFileByLine(path string)([]string, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "os" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - path := "./text.txt" - fileutil.CreateFile(path) - - f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) - defer f.Close() - f.WriteString("hello\nworld") - - contents, _ := fileutil.ReadFileByLine(path) - fmt.Println(contents) //[]string{"hello", "world"} -} -``` - - - -### Zip -

zip压缩文件, fpath参数可以是文件或目录

- -函数签名: - -```go -func Zip(fpath string, destPath string) error -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.Zip("./test.txt", "./test.zip") - if err != nil { - fmt.Println(err) - } -} -``` - - - - -### UnZip -

zip解压缩文件并保存在目录中

- -Signature: - -```go -func UnZip(zipFile string, destPath string) error -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/fileutil" -) - -func main() { - err := fileutil.Zip("./test.zip", "./unzip/test.txt") - if err != nil { - fmt.Println(err) - } -} -``` - - - - - diff --git a/docs/formatter.md b/docs/formatter.md deleted file mode 100644 index c07e9408..00000000 --- a/docs/formatter.md +++ /dev/null @@ -1,53 +0,0 @@ -# Formatter -formatter contains some functions for data formatting. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/formatter/formatter.go](https://github.com/duke-git/lancet/blob/main/formatter/formatter.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/formatter" -) -``` - -
- -## Index -- [Comma](#Comma) - -
- -## Documentation - - - -### Comma -

Add comma to number by every 3 numbers from right. ahead by symbol char. -Param should be number or numberic string.

- -Signature: - -```go -func Comma(v interface{}, symbol string) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/formatter" -) - -func main() { - fmt.Println(formatter.Comma("12345", "")) // "12,345" - fmt.Println(formatter.Comma(12345.67, "¥")) // "¥12,345.67" -} -``` diff --git a/docs/formatter_zh-CN.md b/docs/formatter_zh-CN.md deleted file mode 100644 index 4b178ae7..00000000 --- a/docs/formatter_zh-CN.md +++ /dev/null @@ -1,52 +0,0 @@ -# Formatter -formatter格式化器包含一些数据格式化处理方法。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/formatter/formatter.go](https://github.com/duke-git/lancet/blob/main/formatter/formatter.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/formatter" -) -``` - -
- -## 目录 -- [Comma](#Comma) - -
- -## 文档 - - - -### Comma -

用逗号每隔3位分割数字/字符串,签名添加符号。参数必须是数字或者可以转为数字的字符串

- -函数签名: - -```go -func Comma(v interface{}, symbol string) string -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/formatter" -) - -func main() { - fmt.Println(formatter.Comma("12345", "")) // "12,345" - fmt.Println(formatter.Comma(12345.67, "¥")) // "¥12,345.67" -} -``` diff --git a/docs/function.md b/docs/function.md deleted file mode 100644 index 8ca3d9aa..00000000 --- a/docs/function.md +++ /dev/null @@ -1,364 +0,0 @@ -# Function -Package function can control the flow of function execution and support part of functional programming. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go) -[https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/function" -) -``` - -
- -## Index -- [After](#After) -- [Before](#Before) -- [Curry](#Curry) -- [Compose](#Compose) -- [Debounced](#Debounced) -- [Delay](#Delay) -- [Watcher](#Watcher) - -
- -## Documentation - - - -### After -

Creates a function that invokes given func once it's called n or more times.

- -Signature: - -```go -func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - arr := []string{"a", "b"} - f := function.After(len(arr), func(i int) int { - fmt.Println("last print") - return i - }) - - type cb func(args ...interface{}) []reflect.Value - print := func(i int, s string, fn cb) { - fmt.Printf("arr[%d] is %s \n", i, s) - fn(i) - } - - fmt.Println("arr is", arr) - for i := 0; i < len(arr); i++ { - print(i, arr[i], f) - } - - //output: - // arr is [a b] - // arr[0] is a - // arr[1] is b - // last print -} -``` - - - -### Before - -

creates a function that invokes func once it's called less than n times.

- -Signature: - -```go -func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" - "github.com/duke-git/lancet/internal" -) - -func main() { - assert := internal.NewAssert(t, "TestBefore") - - arr := []string{"a", "b", "c", "d", "e"} - f := function.Before(3, func(i int) int { - return i - }) - - var res []int64 - type cb func(args ...interface{}) []reflect.Value - appendStr := func(i int, s string, fn cb) { - v := fn(i) - res = append(res, v[0].Int()) - } - - for i := 0; i < len(arr); i++ { - appendStr(i, arr[i], f) - } - - expected := []int64{0, 1, 2, 2, 2} - assert.Equal(expected, res) -} -``` - - - -### Curry - -

Make a curry function.

- -Signature: - -```go -type Fn func(...interface{}) interface{} -func (f Fn) Curry(i interface{}) func(...interface{}) interface{} -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - add := func(a, b int) int { - return a + b - } - var addCurry function.Fn = func(values ...interface{}) interface{} { - return add(values[0].(int), values[1].(int)) - } - add1 := addCurry.Curry(1) - result := add1(2) - fmt.Println(result) //3 -} -``` - - - -### Compose - -

Compose the function list from right to left, then return the composed function.

- -Signature: - -```go -func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - add1 := func(v ...interface{}) interface{} { - return v[0].(int) + 1 - } - add2 := func(v ...interface{}) interface{} { - return v[0].(int) + 2 - } - - add3 := function.Compose(add1, add2) - result := add3(1) - - fmt.Println(result) //4 -} -``` - - - -### Debounced - -

Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.

- -Signature: - -```go -func Debounced(fn func(), duration time.Duration) func() -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - count := 0 - add := func() { - count++ - } - - debouncedAdd := function.Debounced(add, 50*time.Microsecond) - function.debouncedAdd() - function.debouncedAdd() - function.debouncedAdd() - function.debouncedAdd() - - time.Sleep(100 * time.Millisecond) - fmt.Println(count) //1 - - function.debouncedAdd() - time.Sleep(100 * time.Millisecond) - fmt.Println(count) //2 -} -``` - - - -### Delay - -

Invoke function after delayed time.

- -Signature: - -```go -func Delay(delay time.Duration, fn interface{}, args ...interface{}) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - var print = func(s string) { - fmt.Println(count) //test delay - } - function.Delay(2*time.Second, print, "test delay") -} -``` - - - -### Schedule - -

Invoke function every duration time, until close the returned bool chan.

- -Signature: - -```go -func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - var res []string - appendStr := func(s string) { - res = append(res, s) - } - - stop := function.Schedule(1*time.Second, appendStr, "*") - time.Sleep(5 * time.Second) - close(stop) - - fmt.Println(res) //[* * * * *] -} -``` - - - -### Watcher - -

Watcher is used for record code excution time. can start/stop/reset the watch timer. get the elapsed time of function execution.

- -Signature: - -```go -type Watcher struct { - startTime int64 - stopTime int64 - excuting bool -} -func (w *Watcher) Start() //start the watcher -func (w *Watcher) Stop() //stop the watcher -func (w *Watcher) Reset() //reset the watcher -func (w *Watcher) GetElapsedTime() time.Duration //get the elapsed time of function execution -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - w := &function.Watcher{} - w.Start() - - longRunningTask() - - fmt.Println(w.excuting) //true - - w.Stop() - - eapsedTime := w.GetElapsedTime().Milliseconds() - fmt.Println(eapsedTime) - - w.Reset() - - fmt.Println(w.excuting) //false - - fmt.Println(w.startTime) //0 - fmt.Println(w.stopTime) //0 -} - -func longRunningTask() { - var slice []int64 - for i := 0; i < 10000000; i++ { - slice = append(slice, int64(i)) - } -} - -``` - - - diff --git a/docs/function_zh-CN.md b/docs/function_zh-CN.md deleted file mode 100644 index 8fdfaaa1..00000000 --- a/docs/function_zh-CN.md +++ /dev/null @@ -1,364 +0,0 @@ -# Function -function函数包控制函数执行流程,包含部分函数式编程。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go) -[https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/function" -) -``` - -
- -## 目录 -- [After](#After) -- [Before](#Before) -- [Curry](#Curry) -- [Compose](#Compose) -- [Debounced](#Debounced) -- [Delay](#Delay) -- [Watcher](#Watcher) - -
- -## 文档 - - - -### After -

创建一个函数,当他被调用n或更多次之后将马上触发fn

- -函数签名: - -```go -func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - arr := []string{"a", "b"} - f := function.After(len(arr), func(i int) int { - fmt.Println("last print") - return i - }) - - type cb func(args ...interface{}) []reflect.Value - print := func(i int, s string, fn cb) { - fmt.Printf("arr[%d] is %s \n", i, s) - fn(i) - } - - fmt.Println("arr is", arr) - for i := 0; i < len(arr); i++ { - print(i, arr[i], f) - } - - //output: - // arr is [a b] - // arr[0] is a - // arr[1] is b - // last print -} -``` - - - -### Before - -

创建一个函数,调用次数不超过n次,之后再调用这个函数,将返回一次最后调用fn的结果

- -函数签名: - -```go -func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" - "github.com/duke-git/lancet/internal" -) - -func main() { - assert := internal.NewAssert(t, "TestBefore") - - arr := []string{"a", "b", "c", "d", "e"} - f := function.Before(3, func(i int) int { - return i - }) - - var res []int64 - type cb func(args ...interface{}) []reflect.Value - appendStr := func(i int, s string, fn cb) { - v := fn(i) - res = append(res, v[0].Int()) - } - - for i := 0; i < len(arr); i++ { - appendStr(i, arr[i], f) - } - - expected := []int64{0, 1, 2, 2, 2} - assert.Equal(expected, res) -} -``` - - - -### Curry - -

创建一个柯里化的函数

- -函数签名: - -```go -type Fn func(...interface{}) interface{} -func (f Fn) Curry(i interface{}) func(...interface{}) interface{} -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - add := func(a, b int) int { - return a + b - } - var addCurry function.Fn = func(values ...interface{}) interface{} { - return add(values[0].(int), values[1].(int)) - } - add1 := addCurry.Curry(1) - result := add1(2) - fmt.Println(result) //3 -} -``` - - - -### Compose - -

从右至左组合函数列表fnList, 返回组合后的函数

- -函数签名: - -```go -func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - add1 := func(v ...interface{}) interface{} { - return v[0].(int) + 1 - } - add2 := func(v ...interface{}) interface{} { - return v[0].(int) + 2 - } - - add3 := function.Compose(add1, add2) - result := add3(1) - - fmt.Println(result) //4 -} -``` - - - -### Debounced - -

创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。

- -函数签名: - -```go -func Debounced(fn func(), duration time.Duration) func() -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - count := 0 - add := func() { - count++ - } - - debouncedAdd := function.Debounced(add, 50*time.Microsecond) - function.debouncedAdd() - function.debouncedAdd() - function.debouncedAdd() - function.debouncedAdd() - - time.Sleep(100 * time.Millisecond) - fmt.Println(count) //1 - - function.debouncedAdd() - time.Sleep(100 * time.Millisecond) - fmt.Println(count) //2 -} -``` - - - -### Delay - -

延迟delay时间后调用函数

- -函数签名: - -```go -func Delay(delay time.Duration, fn interface{}, args ...interface{}) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - var print = func(s string) { - fmt.Println(count) //test delay - } - function.Delay(2*time.Second, print, "test delay") -} -``` - - - -### Schedule - -

每次持续时间调用函数,直到关闭返回的 bool chan

- -函数签名: - -```go -func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - var res []string - appendStr := func(s string) { - res = append(res, s) - } - - stop := function.Schedule(1*time.Second, appendStr, "*") - time.Sleep(5 * time.Second) - close(stop) - - fmt.Println(res) //[* * * * *] -} -``` - - - -### Watcher - -

Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。

- -函数签名: - -```go -type Watcher struct { - startTime int64 - stopTime int64 - excuting bool -} -func (w *Watcher) Start() //start the watcher -func (w *Watcher) Stop() //stop the watcher -func (w *Watcher) Reset() //reset the watcher -func (w *Watcher) GetElapsedTime() time.Duration //get the elapsed time of function execution -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/function" -) - -func main() { - w := &function.Watcher{} - w.Start() - - longRunningTask() - - fmt.Println(w.excuting) //true - - w.Stop() - - eapsedTime := w.GetElapsedTime().Milliseconds() - fmt.Println(eapsedTime) - - w.Reset() - - fmt.Println(w.excuting) //false - - fmt.Println(w.startTime) //0 - fmt.Println(w.stopTime) //0 -} - -func longRunningTask() { - var slice []int64 - for i := 0; i < 10000000; i++ { - slice = append(slice, int64(i)) - } -} - -``` - - - diff --git a/docs/guide/contribution_guide.md b/docs/guide/contribution_guide.md new file mode 100644 index 00000000..2921e215 --- /dev/null +++ b/docs/guide/contribution_guide.md @@ -0,0 +1,37 @@ +# Lancet 贡献指南 + +Hi! 首先感谢你使用 Lancet。 + +lancet(柳叶刀)是一个功能强大、全面、高效、可复用的go语言工具函数库。它消除了处理并发、网络、数学、切片、字符串等的麻烦,使 Go 开发变得更容易。 + +Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代码或提供建议,请阅读以下内容。 + +## Issue 规范 + +- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。 + +- 在提交 issue 之前,请搜索相关内容是否已被提出。 + +- 请说明 Lancet 和 Go 的版本号,并提供操作系统信息。推荐使用 [Go Playground](https://go.dev/play/) 生成在线 demo,这能够更直观地重现问题。 + +## Pull Request 规范 + +- 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。 + +- commit 信息要以 `type(scope): 描述信息` 的形式填写,例如 `fix(package): [scrollbar] fix xxx bug`。 + + 1. type: 必须是 chore, docs, feat, fix, refactor, release, test 其中的一个。 + + 2. scope: 必须是 package, file, internal 其中的一个。 + + 3. header: 描述信息不要超过 72 个字符。 + +- 提交 PR 前请 rebase,确保 commit 记录的整洁。 + +- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。 + +- 确保 PR 是提交到 `rc` 分支,而不是其他分支。 + +- 如果是修复 bug,请在 PR 中给出描述信息。 + +- 如果PR是新功能,确保完成相关文档(/lancet/docs/api/packages/)。 \ No newline at end of file diff --git a/docs/guide/contributors.md b/docs/guide/contributors.md new file mode 100644 index 00000000..aabf17ee --- /dev/null +++ b/docs/guide/contributors.md @@ -0,0 +1,7 @@ +# 贡献者 + +感谢所有为lancet贡献过代码的人! + + + + \ No newline at end of file diff --git a/docs/guide/getting_started.md b/docs/guide/getting_started.md new file mode 100644 index 00000000..96ad72c3 --- /dev/null +++ b/docs/guide/getting_started.md @@ -0,0 +1,48 @@ +--- +outline: deep +--- + +# 安装 + +1. 使用 go1.18 及以上版本的用户,建议安装 v2.x.x。 因为 v2.x.x 应用 go1.18 的泛型重写了大部分函数。 + +```go +go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x +``` + +2. 使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.6。 + +```go +go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x +``` + +## 用法 + +lancet 是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入 strutil 包: + +```go +import "github.com/duke-git/lancet/v2/strutil" +``` + +## 示例 + +此处以字符串工具函数 Reverse(逆序字符串)为例,需要导入 strutil 包: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/strutil" +) + +func main() { + s := "hello" + rs := strutil.Reverse(s) + fmt.Println(rs) //olleh +} +``` + +## 更多 + +更多特性请参考[API](https://www.golancet.cn/api/overview.html). diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md new file mode 100644 index 00000000..a77674a0 --- /dev/null +++ b/docs/guide/introduction.md @@ -0,0 +1,18 @@ +--- +outline: deep +--- + +# lancet是什么? + +lancet(柳叶刀)是一个功能强大、全面、高效、可复用的go语言工具函数库。lancet受到了java apache common包和lodash.js的启发。 + + +## 为什么选择lancet? + +Lancet 消除了处理并发、网络、数学、切片、字符串等的麻烦,使 Go 开发变得更容易。 +Lancet 的实用方法非常适合: + +- 迭代切片和数组。 +- 操作字符串。 +- 处理网络和http请求。 +- 其他工具,例如。 随机、加密、流、重试等。 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..fedd927f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,53 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: 'Lancet' + text: '一个强大的Go语言工具函数库' + tagline: '简洁, 强大, 高效' + actions: + - theme: brand + text: 开始使用 + link: /guide/getting_started + - theme: alt + text: 在GitHub中查看 + link: https://github.com/duke-git/lancet + + image: + src: /lancet_logo.png + alt: lancet + +features: + - title: 全面 + icon: 💪 + details: 功能全面,支持600+ go util函数。字符串、切片、日期时间、网络、加密、并发... + - title: 模块化设计 + icon: 🏗 + details: 每个模块设计成一个包,模块之间无耦合。 + - title: 纯净 + icon: 💅 + details: 只依赖go标准库和golang.org/x。 + - title: 简洁 + icon: 👏 + details: 结构良好,测试每个导出的函数。 +--- + +

+ + +

+ +赞助 diff --git a/docs/mathutil.md b/docs/mathutil.md deleted file mode 100644 index b911f382..00000000 --- a/docs/mathutil.md +++ /dev/null @@ -1,233 +0,0 @@ -# Mathutil -Package mathutil implements some functions for math calculation. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go) - - -
- -## Example: -```go -import ( - "github.com/duke-git/lancet/mathutil" -) -``` - -
- -## Index -- [Exponent](#Exponent) -- [Fibonacci](#Fibonacci) -- [Factorial](#Factorial) -- [Percent](#Percent) -- [RoundToFloat](#RoundToFloat) -- [RoundToString](#RoundToString) -- [TruncRound](#TruncRound) - -
- -## Documentation - - -### Exponent -

Calculate x to the nth power.

- -Signature: - -```go -func Exponent(x, n int64) int64 -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Exponent(10, 0)) //1 - fmt.Println(mathutil.Exponent(10, 1)) //10 - fmt.Println(mathutil.Exponent(10, 2)) //100 -} -``` - - - -### Fibonacci -

Calculate the nth number of fibonacci sequence.

- -Signature: - -```go -func Fibonacci(first, second, n int) int -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Fibonacci(1, 1, 1)) //1 - fmt.Println(mathutil.Fibonacci(1, 1, 2)) //1 - fmt.Println(mathutil.Fibonacci(1, 1, 3)) //2 - fmt.Println(mathutil.Fibonacci(1, 1, 4)) //3 - fmt.Println(mathutil.Fibonacci(1, 1, 5)) //5 -} -``` - - - -### Factorial -

Calculate the factorial of x.

- -Signature: - -```go -func Factorial(x uint) uint -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Factorial(0)) //1 - fmt.Println(mathutil.Factorial(1)) //1 - fmt.Println(mathutil.Factorial(2)) //2 - fmt.Println(mathutil.Factorial(3)) //6 -} -``` - - - -### Percent -

calculate the percentage of val to total, retain n decimal places.

- -Signature: - -```go -func Percent(val, total float64, n int) float64 -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Percent(1, 2, 2)) //1 - fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //33.33 -} -``` - - - -### RoundToFloat -

Round float up to n decimal places.

- -Signature: - -```go -func RoundToFloat(x float64, n int) float64 -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.RoundToFloat(0, 0)) //0 - fmt.Println(mathutil.RoundToFloat(0, 1)) //0 - fmt.Println(mathutil.RoundToFloat(0.124, 2)) //0.12 - fmt.Println(mathutil.RoundToFloat(0.125, 2)) //0.13 - fmt.Println(mathutil.RoundToFloat(0.125, 3)) //0.125 -} -``` - - - - -### RoundToString -

Round float up to n decimal places. will return string.

- -Signature: - -```go -func RoundToString(x float64, n int) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.RoundToString(0, 0)) //"0" - fmt.Println(mathutil.RoundToString(0, 1)) //"0.0: - fmt.Println(mathutil.RoundToString(0.124, 2)) //"0.12" - fmt.Println(mathutil.RoundToString(0.125, 2)) //"0.13" - fmt.Println(mathutil.RoundToString(0.125, 3)) //"0.125" -} -``` - - - -### TruncRound -

Round float off n decimal places.

- -Signature: - -```go -func TruncRound(x float64, n int) float64 -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.TruncRound(0, 0)) //0 - fmt.Println(mathutil.TruncRound(0, 1)) //0 - fmt.Println(mathutil.TruncRound(0.124, 2)) //0.12 - fmt.Println(mathutil.TruncRound(0.125, 2)) //0.12 - fmt.Println(mathutil.TruncRound(0.125, 3)) //0.125 -} -``` - - - diff --git a/docs/mathutil_zh-CN.md b/docs/mathutil_zh-CN.md deleted file mode 100644 index a0ceb6a9..00000000 --- a/docs/mathutil_zh-CN.md +++ /dev/null @@ -1,234 +0,0 @@ -# Mathutil -mathutil包实现了一些数学计算的函数. - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/main/mathutil/mathutil.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/mathutil" -) -``` - -
- -## 目录 -- [Exponent](#Exponent) -- [Fibonacci](#Fibonacci) -- [Factorial](#Factorial) - -- [Percent](#Percent) -- [RoundToFloat](#RoundToFloat) -- [RoundToString](#RoundToString) -- [TruncRound](#TruncRound) - -
- -## Documentation - - -### Exponent -

指数计算(x的n次方)

- -函数签名: - -```go -func Exponent(x, n int64) int64 -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Exponent(10, 0)) //1 - fmt.Println(mathutil.Exponent(10, 1)) //10 - fmt.Println(mathutil.Exponent(10, 2)) //100 -} -``` - - - -### Fibonacci -

计算斐波那契数列的第n个数

- -函数签名: - -```go -func Fibonacci(first, second, n int) int -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Fibonacci(1, 1, 1)) //1 - fmt.Println(mathutil.Fibonacci(1, 1, 2)) //1 - fmt.Println(mathutil.Fibonacci(1, 1, 3)) //2 - fmt.Println(mathutil.Fibonacci(1, 1, 4)) //3 - fmt.Println(mathutil.Fibonacci(1, 1, 5)) //5 -} -``` - - - -### Factorial -

计算阶乘

- -函数签名: - -```go -func Factorial(x uint) uint -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Factorial(0)) //1 - fmt.Println(mathutil.Factorial(1)) //1 - fmt.Println(mathutil.Factorial(2)) //2 - fmt.Println(mathutil.Factorial(3)) //6 -} -``` - - - -### Percent -

计算百分比,保留n位小数

- -函数签名: - -```go -func Percent(val, total float64, n int) float64 -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.Percent(1, 2, 2)) //1 - fmt.Println(mathutil.Percent(0.1, 0.3, 2)) //33.33 -} -``` - - - -### RoundToFloat -

四舍五入,保留n位小数

- -函数签名: - -```go -func RoundToFloat(x float64, n int) float64 -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.RoundToFloat(0, 0)) //0 - fmt.Println(mathutil.RoundToFloat(0, 1)) //0 - fmt.Println(mathutil.RoundToFloat(0.124, 2)) //0.12 - fmt.Println(mathutil.RoundToFloat(0.125, 2)) //0.13 - fmt.Println(mathutil.RoundToFloat(0.125, 3)) //0.125 -} -``` - - - - -### RoundToString -

四舍五入,保留n位小数,返回字符串

- -函数签名: - -```go -func RoundToString(x float64, n int) string -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.RoundToString(0, 0)) //"0" - fmt.Println(mathutil.RoundToString(0, 1)) //"0.0: - fmt.Println(mathutil.RoundToString(0.124, 2)) //"0.12" - fmt.Println(mathutil.RoundToString(0.125, 2)) //"0.13" - fmt.Println(mathutil.RoundToString(0.125, 3)) //"0.125" -} -``` - - - -### TruncRound -

截短n位小数(不进行四舍五入)

- -函数签名: - -```go -func TruncRound(x float64, n int) float64 -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/mathutil" -) - -func main() { - fmt.Println(mathutil.TruncRound(0, 0)) //0 - fmt.Println(mathutil.TruncRound(0, 1)) //0 - fmt.Println(mathutil.TruncRound(0.124, 2)) //0.12 - fmt.Println(mathutil.TruncRound(0.125, 2)) //0.12 - fmt.Println(mathutil.TruncRound(0.125, 3)) //0.125 -} -``` - - - diff --git a/docs/netutil.md b/docs/netutil.md deleted file mode 100644 index ac8eb1ff..00000000 --- a/docs/netutil.md +++ /dev/null @@ -1,511 +0,0 @@ -# Netutil -Package netutil contains functions to get net information and send http request. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/netutil/net.go](https://github.com/duke-git/lancet/blob/main/netutil/net.go) - -[https://github.com/duke-git/lancet/blob/main/netutil/http.go](https://github.com/duke-git/lancet/blob/main/netutil/http.go) - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/netutil" -) -``` - -
- -## Index -- [ConvertMapToQueryString](#ConvertMapToQueryString) -- [GetInternalIp](#GetInternalIp) -- [GetIps](#GetIps) -- [GetMacAddrs](#GetMacAddrs) -- [GetPublicIpInfo](#GetPublicIpInfo) -- [IsPublicIP](#IsPublicIP) -- [HttpGet](#HttpGet) -- [HttpDelete](#HttpDelete) -- [HttpPost](#HttpPost) -- [HttpPut](#HttpPut) - -- [HttpPatch](#HttpPatch) -- [ParseHttpResponse](#ParseHttpResponse) - -
- -## Documentation - - -### ConvertMapToQueryString -

Convert map to url query string.

- -Signature: - -```go -func ConvertMapToQueryString(param map[string]interface{}) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/netutil" -) - -func main() { - var m = map[string]interface{}{ - "c": 3, - "a": 1, - "b": 2, - } - qs := netutil.ConvertMapToQueryString(m) - - fmt.Println(qs) //a=1&b=2&c=3 -} -``` - - - -### GetInternalIp -

Get internal ip information.

- -Signature: - -```go -func GetInternalIp() string -``` -Example: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - internalIp := netutil.GetInternalIp() - ip := net.ParseIP(internalIp) - - fmt.Println(ip) //192.168.1.9 -} -``` - - - -### GetIps -

Get all ipv4 list.

- -Signature: - -```go -func GetIps() []string -``` -Example: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - ips := netutil.GetIps() - fmt.Println(ips) //[192.168.1.9] -} -``` - - - -### GetMacAddrs -

Get all mac addresses list.

- -Signature: - -```go -func GetMacAddrs() []string { -``` -Example: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - addrs := netutil.GetMacAddrs() - fmt.Println(addrs) -} -``` - - - -### GetPublicIpInfo -

Get public ip information.

- -Signature: - -```go -func GetPublicIpInfo() (*PublicIpInfo, error) -type PublicIpInfo struct { - Status string `json:"status"` - Country string `json:"country"` - CountryCode string `json:"countryCode"` - Region string `json:"region"` - RegionName string `json:"regionName"` - City string `json:"city"` - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - Isp string `json:"isp"` - Org string `json:"org"` - As string `json:"as"` - Ip string `json:"query"` -} -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/netutil" -) - -func main() { - publicIpInfo, err := netutil.GetPublicIpInfo() - if err != nil { - fmt.Println(err) - } - - fmt.Println(publicIpInfo) -} -``` - - - -### IsPublicIP -

Checks if a ip is public or not.

- -Signature: - -```go -func IsPublicIP(IP net.IP) bool -``` -Example: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - ip1 := net.ParseIP("192.168.0.1") - ip2 := net.ParseIP("36.112.24.10") - - fmt.Println(netutil.IsPublicIP(ip1)) //false - fmt.Println(netutil.IsPublicIP(ip2)) //true -} -``` - - - - -### HttpGet -

Send http get request.

- -Signature: - -```go -// params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, -// params[2] is post body which type should be []byte. -// params[3] is http client which type should be http.Client. -func HttpGet(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - - resp, err := netutil.HttpGet(url, header) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPost -

Send http post request.

- -Signature: - -```go -// params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, -// params[2] is post body which type should be []byte. -// params[3] is http client which type should be http.Client. -func HttpPost(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, "TestAddToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPost(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPut -

Send http put request.

- -Signature: - -```go -// params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, -// params[2] is post body which type should be []byte. -// params[3] is http client which type should be http.Client. -func HttpPut(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, 1, "TestPutToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPut(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpDelete -

Send http delete request.

- -Signature: - -```go -// params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, -// params[2] is post body which type should be []byte. -// params[3] is http client which type should be http.Client. -func HttpDelete(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - resp, err := netutil.HttpDelete(url) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPatch -

Send http patch request.

- -Signature: - -```go -// params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, -// params[2] is post body which type should be []byte. -// params[3] is http client which type should be http.Client. -func HttpPatch(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, 1, "TestPatchToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPatch(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### ParseHttpResponse -

Decode http response to specified interface.

- -Signature: - -```go -func ParseHttpResponse(resp *http.Response, obj interface{}) error -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - - resp, err := netutil.HttpGet(url, header) - if err != nil { - log.Fatal(err) - } - - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - Completed bool `json:"completed"` - } - - toDoResp := &Todo{} - err = netutil.ParseHttpResponse(resp, toDoResp) - if err != nil { - log.Fatal(err) - } - - fmt.Println(toDoResp) -} -``` - diff --git a/docs/netutil_zh-CN.md b/docs/netutil_zh-CN.md deleted file mode 100644 index 8cbe3f3e..00000000 --- a/docs/netutil_zh-CN.md +++ /dev/null @@ -1,510 +0,0 @@ -# Netutil -netutil网络包支持获取ip地址,发送http请求。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/netutil/net.go](https://github.com/duke-git/lancet/blob/main/netutil/net.go) - -[https://github.com/duke-git/lancet/blob/main/netutil/http.go](https://github.com/duke-git/lancet/blob/main/netutil/http.go) - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/netutil" -) -``` - -
- -## 目录 -- [ConvertMapToQueryString](#ConvertMapToQueryString) -- [GetInternalIp](#GetInternalIp) -- [GetIps](#GetIps) -- [GetMacAddrs](#GetMacAddrs) -- [GetPublicIpInfo](#GetPublicIpInfo) -- [IsPublicIP](#IsPublicIP) -- [HttpGet](#HttpGet) -- [HttpDelete](#HttpDelete) -- [HttpPost](#HttpPost) -- [HttpPut](#HttpPut) - -- [HttpPatch](#HttpPatch) -- [ParseHttpResponse](#ParseHttpResponse) - -
- -## 文档 - - -### ConvertMapToQueryString -

将map转换成http查询字符串.

- -函数签名: - -```go -func ConvertMapToQueryString(param map[string]interface{}) string -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/netutil" -) - -func main() { - var m = map[string]interface{}{ - "c": 3, - "a": 1, - "b": 2, - } - qs := netutil.ConvertMapToQueryString(m) - - fmt.Println(qs) //a=1&b=2&c=3 -} -``` - - - -### GetInternalIp -

获取内部ip

- -函数签名: - -```go -func GetInternalIp() string -``` -例子: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - internalIp := netutil.GetInternalIp() - ip := net.ParseIP(internalIp) - - fmt.Println(ip) //192.168.1.9 -} -``` - - -### GetIps -

获取ipv4地址列表

- -函数签名: - -```go -func GetIps() []string -``` -例子: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - ips := netutil.GetIps() - fmt.Println(ips) //[192.168.1.9] -} -``` - - - -### GetMacAddrs -

获取mac地址列

- -函数签名: - -```go -func GetMacAddrs() []string { -``` -例子: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - addrs := netutil.GetMacAddrs() - fmt.Println(addrs) -} -``` - - - -### GetPublicIpInfo -

获取公网ip信息

- -函数签名: - -```go -func GetPublicIpInfo() (*PublicIpInfo, error) -type PublicIpInfo struct { - Status string `json:"status"` - Country string `json:"country"` - CountryCode string `json:"countryCode"` - Region string `json:"region"` - RegionName string `json:"regionName"` - City string `json:"city"` - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - Isp string `json:"isp"` - Org string `json:"org"` - As string `json:"as"` - Ip string `json:"query"` -} -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/netutil" -) - -func main() { - publicIpInfo, err := netutil.GetPublicIpInfo() - if err != nil { - fmt.Println(err) - } - - fmt.Println(publicIpInfo) -} -``` - - - -### IsPublicIP -

判断ip是否是公共ip

- -函数签名: - -```go -func IsPublicIP(IP net.IP) bool -``` -例子: - -```go -package main - -import ( - "fmt" - "net" - "github.com/duke-git/lancet/netutil" -) - -func main() { - ip1 := net.ParseIP("192.168.0.1") - ip2 := net.ParseIP("36.112.24.10") - - fmt.Println(netutil.IsPublicIP(ip1)) //false - fmt.Println(netutil.IsPublicIP(ip2)) //true -} -``` - - - - -### HttpGet -

发送http get请求

- -函数签名: - -```go -// params[0] http请求header,类型必须是http.Header或者map[string]string -// params[1] http查询字符串,类型必须是url.Values或者map[string]interface{} -// params[2] post请求体,类型必须是[]byte -// params[3] http client,类型必须是http.Client -func HttpGet(url string, params ...interface{}) (*http.Response, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - - resp, err := netutil.HttpGet(url, header) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPost -

发送http post请求

- -函数签名: - -```go -// params[0] http请求header,类型必须是http.Header或者map[string]string -// params[1] http查询字符串,类型必须是url.Values或者map[string]interface{} -// params[2] post请求体,类型必须是[]byte -// params[3] http client,类型必须是http.Client -func HttpPost(url string, params ...interface{}) (*http.Response, error) -``` -例子: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, "TestAddToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPost(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPut -

发送http put请求

- -函数签名: - -```go -// params[0] http请求header,类型必须是http.Header或者map[string]string -// params[1] http查询字符串,类型必须是url.Values或者map[string]interface{} -// params[2] post请求体,类型必须是[]byte -// params[3] http client,类型必须是http.Client -func HttpPut(url string, params ...interface{}) (*http.Response, error) -``` -Example: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, 1, "TestPutToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPut(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpDelete -

发送http delete请求

- -函数签名: - -```go -// params[0] http请求header,类型必须是http.Header或者map[string]string -// params[1] http查询字符串,类型必须是url.Values或者map[string]interface{} -// params[2] post请求体,类型必须是[]byte -// params[3] http client,类型必须是http.Client -func HttpDelete(url string, params ...interface{}) (*http.Response, error) -``` -例子: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - resp, err := netutil.HttpDelete(url) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### HttpPatch -

发送http patch请求

- -函数签名: - -```go -// params[0] http请求header,类型必须是http.Header或者map[string]string -// params[1] http查询字符串,类型必须是url.Values或者map[string]interface{} -// params[2] post请求体,类型必须是[]byte -// params[3] http client,类型必须是http.Client -func HttpPatch(url string, params ...interface{}) (*http.Response, error) -``` -例子: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - } - todo := Todo{1, 1, "TestPatchToDo"} - bodyParams, _ := json.Marshal(todo) - - resp, err := netutil.HttpPatch(url, header, nil, bodyParams) - if err != nil { - log.Fatal(err) - } - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(body) -} -``` - - - -### ParseHttpResponse -

将http请求响应解码成特定struct值

- -函数签名: - -```go -func ParseHttpResponse(resp *http.Response, obj interface{}) error -``` -例子: - -```go -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "github.com/duke-git/lancet/netutil" -) - -func main() { - url := "https://jsonplaceholder.typicode.com/todos/1" - header := map[string]string{ - "Content-Type": "application/json", - } - - resp, err := netutil.HttpGet(url, header) - if err != nil { - log.Fatal(err) - } - - type Todo struct { - Id int `json:"id"` - UserId int `json:"userId"` - Title string `json:"title"` - Completed bool `json:"completed"` - } - - toDoResp := &Todo{} - err = netutil.ParseHttpResponse(resp, toDoResp) - if err != nil { - log.Fatal(err) - } - - fmt.Println(toDoResp) -} -``` - diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..541ab656 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,1628 @@ +{ + "name": "lancet-docs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lancet-docs", + "devDependencies": { + "vitepress": "^1.2.3" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "dev": true, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==", + "dev": true + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==", + "dev": true + }, + "node_modules/@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "dev": true, + "dependencies": { + "@algolia/logger-common": "4.23.3" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==", + "dev": true + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.7", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", + "dev": true + }, + "node_modules/@docsearch/js": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.6.0.tgz", + "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", + "dev": true, + "dependencies": { + "@docsearch/react": "3.6.0", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-1.6.3.tgz", + "integrity": "sha512-QnJKHFUW95GnlJLJGP6QLx4M69HM0KlXk+R2Y8lr/x4nAx1Yb/lsuxq4XwybuUjTxbJk+BT0g/kvn0bcsjGGHg==", + "dev": true + }, + "node_modules/@shikijs/transformers": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-1.6.3.tgz", + "integrity": "sha512-ptBuP/IIeqCzK3zZO/knFICZWs58uZWzbv7ND+bKOewe5NcCjZfSiMyzFwOyl23ewPJ1APjRBwLi6Asrodmmxw==", + "dev": true, + "dependencies": { + "shiki": "1.6.3" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz", + "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@vue/shared": "3.4.27", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz", + "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.4.27", + "@vue/shared": "3.4.27" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz", + "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@vue/compiler-core": "3.4.27", + "@vue/compiler-dom": "3.4.27", + "@vue/compiler-ssr": "3.4.27", + "@vue/shared": "3.4.27", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz", + "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.4.27", + "@vue/shared": "3.4.27" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.2.1.tgz", + "integrity": "sha512-6oNCtyFOrNdqm6GUkFujsCgFlpbsHLnZqq7edeM/+cxAbMyCWvsaCsIMUaz7AiluKLccCGEM8fhOsjaKgBvb7g==", + "dev": true, + "dependencies": { + "@vue/devtools-kit": "^7.2.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.2.1.tgz", + "integrity": "sha512-Wak/fin1X0Q8LLIfCAHBrdaaB+R6IdpSXsDByPHbQ3BmkCP0/cIo/oEGp9i0U2+gEqD4L3V9RDjNf1S34DTzQQ==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^7.2.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.2.1.tgz", + "integrity": "sha512-PCJF4UknJmOal68+X9XHyVeQ+idv0LFujkTOIW30+GaMJqwFVN9LkQKX4gLqn61KkGMdJTzQ1bt7EJag3TI6AA==", + "dev": true, + "dependencies": { + "rfdc": "^1.3.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.27.tgz", + "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==", + "dev": true, + "dependencies": { + "@vue/shared": "3.4.27" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.27.tgz", + "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==", + "dev": true, + "dependencies": { + "@vue/reactivity": "3.4.27", + "@vue/shared": "3.4.27" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz", + "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==", + "dev": true, + "dependencies": { + "@vue/runtime-core": "3.4.27", + "@vue/shared": "3.4.27", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.27.tgz", + "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==", + "dev": true, + "dependencies": { + "@vue/compiler-ssr": "3.4.27", + "@vue/shared": "3.4.27" + }, + "peerDependencies": { + "vue": "3.4.27" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.27.tgz", + "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==", + "dev": true + }, + "node_modules/@vueuse/core": { + "version": "10.10.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.10.0.tgz", + "integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.10.0", + "@vueuse/shared": "10.10.0", + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations": { + "version": "10.10.0", + "resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-10.10.0.tgz", + "integrity": "sha512-vHGeK7X6mkdkpcm1eE9t3Cpm21pNVfZRwrjwwbrEs9XftnSgszF4831G2rei8Dt9cIYJIfFV+iyx/29muimJPQ==", + "dev": true, + "dependencies": { + "@vueuse/core": "10.10.0", + "@vueuse/shared": "10.10.0", + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "*", + "axios": "*", + "change-case": "*", + "drauu": "*", + "focus-trap": "*", + "fuse.js": "*", + "idb-keyval": "*", + "jwt-decode": "*", + "nprogress": "*", + "qrcode": "*", + "sortablejs": "*", + "universal-cookie": "*" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "10.10.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.10.0.tgz", + "integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "10.10.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.10.0.tgz", + "integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmmirror.com/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dev": true, + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true + }, + "node_modules/minisearch": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/minisearch/-/minisearch-6.3.0.tgz", + "integrity": "sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==", + "dev": true + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.22.0", + "resolved": "https://registry.npmmirror.com/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.14.0", + "resolved": "https://registry.npmmirror.com/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "dev": true, + "peer": true + }, + "node_modules/shiki": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/shiki/-/shiki-1.6.3.tgz", + "integrity": "sha512-lE1/YGlzFY0hQSyEfsZj18xGrTWxyhFQkaiILALqTBZPbJeYFWpbUhlmTGPOupYB/qC+H6sV4UznJzcEh3WMHQ==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.6.3" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, + "node_modules/vite": { + "version": "5.2.13", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.2.13.tgz", + "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.2.3.tgz", + "integrity": "sha512-GvEsrEeNLiDE1+fuwDAYJCYLNZDAna+EtnXlPajhv/MYeTjbNK6Bvyg6NoTdO1sbwuQJ0vuJR99bOlH53bo6lg==", + "dev": true, + "dependencies": { + "@docsearch/css": "^3.6.0", + "@docsearch/js": "^3.6.0", + "@shikijs/core": "^1.6.2", + "@shikijs/transformers": "^1.6.2", + "@types/markdown-it": "^14.1.1", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/devtools-api": "^7.2.1", + "@vue/shared": "^3.4.27", + "@vueuse/core": "^10.10.0", + "@vueuse/integrations": "^10.10.0", + "focus-trap": "^7.5.4", + "mark.js": "8.11.1", + "minisearch": "^6.3.0", + "shiki": "^1.6.2", + "vite": "^5.2.12", + "vue": "^3.4.27" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.4.27", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.27.tgz", + "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.4.27", + "@vue/compiler-sfc": "3.4.27", + "@vue/runtime-dom": "3.4.27", + "@vue/server-renderer": "3.4.27", + "@vue/shared": "3.4.27" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..78cddb59 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,13 @@ +{ + "name": "lancet-docs", + "private": true, + "type": "module", + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build --max_old_space_size=8192", + "docs:preview": "vitepress preview" + }, + "devDependencies": { + "vitepress": "^1.2.3" + } +} diff --git a/docs/public/lancet_logo.png b/docs/public/lancet_logo.png new file mode 100644 index 00000000..481f8906 Binary files /dev/null and b/docs/public/lancet_logo.png differ diff --git a/docs/public/lancet_logo_mini.png b/docs/public/lancet_logo_mini.png new file mode 100644 index 00000000..9c20d1d2 Binary files /dev/null and b/docs/public/lancet_logo_mini.png differ diff --git a/docs/public/sponsor_btn.png b/docs/public/sponsor_btn.png new file mode 100644 index 00000000..19d36f30 Binary files /dev/null and b/docs/public/sponsor_btn.png differ diff --git a/docs/public/wechat_pay.png b/docs/public/wechat_pay.png new file mode 100644 index 00000000..18ba504e Binary files /dev/null and b/docs/public/wechat_pay.png differ diff --git a/docs/random.md b/docs/random.md deleted file mode 100644 index 4cf260c4..00000000 --- a/docs/random.md +++ /dev/null @@ -1,138 +0,0 @@ -# Random -Package random implements some basic functions to generate random int and string. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/random" -) -``` - -
- -## Index -- [RandBytes](#RandBytes) -- [RandInt](#RandInt) -- [RandString](#RandString) -- [UUIdV4](#UUIdV4) - -
- -## Documentation - - -### RandBytes -

Generate random byte slice.

- -Signature: - -```go -func RandBytes(length int) []byte -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - randBytes := random.RandBytes(4) - fmt.Println(randBytes) -} -``` - - -### RandInt -

Generate random int between min and max, may contain min, not max.

- -Signature: - -```go -func RandInt(min, max int) int -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - rInt := random.RandInt(1, 10) - fmt.Println(rInt) -} -``` - - - -### RandInt -

Generate random given length string.

- -Signature: - -```go -func RandString(length int) string -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - randStr := random.RandString(6) - fmt.Println(randStr) -} -``` - - - - -### UUIdV4 -

Generate a random UUID of version 4 according to RFC 4122.

- -Signature: - -```go -func UUIdV4() (string, error) -``` -Example: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - uuid, err := random.UUIdV4() - if err != nil { - return - } - fmt.Println(uuid) -} -``` - - diff --git a/docs/random_zh-CN.md b/docs/random_zh-CN.md deleted file mode 100644 index e3ff93ea..00000000 --- a/docs/random_zh-CN.md +++ /dev/null @@ -1,138 +0,0 @@ -# Random -random随机数生成器包,可以生成随机[]bytes, int, string。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/random/random.go](https://github.com/duke-git/lancet/blob/main/random/random.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/random" -) -``` - -
- -## 目录 -- [RandBytes](#RandBytes) -- [RandInt](#RandInt) -- [RandString](#RandString) -- [UUIdV4](#UUIdV4) - - -
- -## 文档 - - -### RandBytes -

生成随机字节切片

- -函数签名: - -```go -func RandBytes(length int) []byte -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - randBytes := random.RandBytes(4) - fmt.Println(randBytes) -} -``` - - -### RandInt -

生成随机int, 范围[min, max)

- -函数签名: - -```go -func RandInt(min, max int) int -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - rInt := random.RandInt(1, 10) - fmt.Println(rInt) -} -``` - - - -### RandInt -

生成随机给定长度的随机字符串

- -函数签名: - -```go -func RandString(length int) string -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - randStr := random.RandString(6) - fmt.Println(randStr) -} -``` - - - -### UUIdV4 -

生成UUID v4字符串

- -函数签名: - -```go -func UUIdV4() (string, error) -``` -例子: - -```go -package main - -import ( - "fmt" - "github.com/duke-git/lancet/random" -) - -func main() { - uuid, err := random.UUIdV4() - if err != nil { - return - } - fmt.Println(uuid) -} -``` - - diff --git a/docs/retry.md b/docs/retry.md deleted file mode 100644 index abfd8581..00000000 --- a/docs/retry.md +++ /dev/null @@ -1,236 +0,0 @@ -# Retry -Package retry is for executing a function repeatedly until it was successful or canceled by the context. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/retry/retry.go](https://github.com/duke-git/lancet/blob/main/retry/retry.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/retry" -) -``` - -
- -## Index -- [Context](#Context) -- [Retry](#Retry) -- [RetryFunc](#RetryFunc) -- [RetryDuration](#RetryDuration) -- [RetryTimes](#RetryTimes) - -
- -## Documentation - - -### Context -

Set retry context config, can cancel the retry with context.

- -Signature: - -```go -func Context(ctx context.Context) -``` -Example: - -```go -import ( - "context" - "errors" - "fmt" - "github.com/duke-git/lancet/retry" - "time" -) - -func main() { - ctx, cancel := context.WithCancel(context.TODO()) - var number int - increaseNumber := func() error { - number++ - if number > 3 { - cancel() - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, - retry.RetryDuration(time.Microsecond*50), - retry.Context(ctx), - ) - - if err != nil { - fmt.Println(err) //retry is cancelled - } -} -``` - - - - -### RetryFunc -

Function that retry executes.

- -Signature: - -```go -type RetryFunc func() error -``` -Example: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - var increaseNumber retry.RetryFunc - increaseNumber = func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` - - - -### RetryTimes -

Set times of retry. Default times is 5.

- -Signature: - -```go -func RetryTimes(n uint) -``` -Example: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryTimes(2)) - if err != nil { - log.Fatal(err) //2022/02/01 18:42:25 function main.main.func1 run failed after 2 times retry exit status 1 - } -} -``` - - - -### RetryDuration -

Set duration of retries. Default duration is 3 second.

- -Signature: - -```go -func RetryDuration(d time.Duration) -``` -Example: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` - - -### Retry -

Executes the retryFunc repeatedly until it was successful or canceled by the context.

- -Signature: - -```go -func Retry(retryFunc RetryFunc, opts ...Option) error -``` -Example: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` diff --git a/docs/retry_zh-CN.md b/docs/retry_zh-CN.md deleted file mode 100644 index 23f136ea..00000000 --- a/docs/retry_zh-CN.md +++ /dev/null @@ -1,238 +0,0 @@ -# Retry -retry重试执行函数直到函数运行成功或被context cancel。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/retry/retry.go](https://github.com/duke-git/lancet/blob/main/retry/retry.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/retry" -) -``` - -
- -## 目录 -- [Context](#Context) -- [Retry](#Retry) -- [RetryFunc](#RetryFunc) -- [RetryDuration](#RetryDuration) -- [RetryTimes](#RetryTimes) - - -
- - -## Document文档 - - -### Context -

设置重试context参数

- -函数签名: - -```go -func Context(ctx context.Context) -``` -例子: - -```go -import ( - "context" - "errors" - "fmt" - "lancet-demo/retry" - "time" -) - -func main() { - ctx, cancel := context.WithCancel(context.TODO()) - var number int - increaseNumber := func() error { - number++ - if number > 3 { - cancel() - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, - retry.RetryDuration(time.Microsecond*50), - retry.Context(ctx), - ) - - if err != nil { - fmt.Println(err) //retry is cancelled - } -} -``` - - - - -### RetryFunc -

被重试执行的函数

- -函数签名: - -```go -type RetryFunc func() error -``` -例子: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - var increaseNumber retry.RetryFunc - increaseNumber = func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` - - - -### RetryTimes -

设置重试次数,默认5

- -函数签名: - -```go -func RetryTimes(n uint) -``` -例子: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryTimes(2)) - if err != nil { - log.Fatal(err) //2022/02/01 18:42:25 function main.main.func1 run failed after 2 times retry exit status 1 - } -} -``` - - - -### RetryDuration -

设置重试间隔时间,默认3秒

- -函数签名: - -```go -func RetryDuration(d time.Duration) -``` -例子: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` - - -### Retry -

重试执行函数retryFunc,直到函数运行成功,或被context停止

- -函数签名: - -```go -func Retry(retryFunc RetryFunc, opts ...Option) error -``` -例子: - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/retry" -) - -func main() { - var number int - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50)) - if err != nil { - log.Fatal(err) - } - - fmt.Println(number) //3 -} -``` diff --git a/docs/slice.md b/docs/slice.md deleted file mode 100644 index 27cac1a2..00000000 --- a/docs/slice.md +++ /dev/null @@ -1,968 +0,0 @@ -# Slice -Package slice implements some functions to manipulate slice. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/slice" -) -``` - -
- -## Index -- [Contain](#Contain) -- [ContainSubSlice](#ContainSubSlice) -- [Chunk](#Chunk) -- [Compact](#Compact) -- [Concat](#Concat) -- [Count](#Count) -- [Difference](#Difference) -- [DifferenceBy](#DifferenceBy) -- [DeleteByIndex](#DeleteByIndex) -- [Drop](#Drop) -- [Every](#Every) -- [Filter](#Filter) -- [Find](#Find) -- [FindLast](#FindLast) -- [FlattenDeep](#FlattenDeep) -- [ForEach](#ForEach) - -- [GroupBy](#GroupBy) -- [IntSlice](#IntSlice) -- [InterfaceSlice](#InterfaceSlice) -- [Intersection](#Intersection) -- [InsertByIndex](#InsertByIndex) -- [Map](#Map) -- [ReverseSlice](#ReverseSlice) -- [Reduce](#Reduce) -- [Shuffle](#Shuffle) -- [SortByField](#SortByField) -- [Some](#Some) -- [StringSlice](#StringSlice) -- [Unique](#Unique) -- [Union](#Union) -- [UpdateByIndex](#UpdateByIndex) -- [Without](#Without) - -
- -## Documentation - -## Note: -1. param which type is interface{} in below functions should be slice. - -### Contain -

Check if the value is in the slice or not. iterableType param can be string, map or slice.

- -Signature: - -```go -func Contain(iterableType interface{}, value interface{}) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Contain([]string{"a", "b", "c"}, "a") - fmt.Println(res) //true -} -``` - - -### ContainSubSlice -

Check if the slice contain subslice or not.

- -Signature: - -```go -func ContainSubSlice(slice interface{}, subslice interface{}) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"}) - fmt.Println(res) //true -} -``` - - - - -### Chunk -

Creates an slice of elements split into groups the length of `size`.

- -Signature: - -```go -func Chunk(slice []interface{}, size int) [][]interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - arr := []string{"a", "b", "c", "d", "e"} - res := slice.Chunk(InterfaceSlice(arr), 3) - fmt.Println(res) //[][]interface{}{{"a", "b", "c"}, {"d", "e"}} -} -``` - - - -### Compact -

Creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.

- -Signature: - -```go -func Compact(slice interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Compact([]int{0, 1, 2, 3}) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - -### Concat -

Creates a new slice concatenating slice with any additional slices and/or values.

- -Signature: - -```go -func Concat(slice interface{}, values ...interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.Concat([]int{1, 2, 3}, 4, 5) - fmt.Println(res1) //[]int{1, 2, 3, 4, 5} - - res2 := slice.Concat([]int{1, 2, 3}, []int{4, 5}) - fmt.Println(res2) //[]int{1, 2, 3, 4, 5} -} -``` - - - -### Count -

Count iterates over elements of slice, returns a count of all matched elements. The function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func Count(slice, function interface{}) int -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5, 6} - evenFunc := func(i, num int) bool { - return (num % 2) == 0 - } - - res := slice.Count(nums, evenFunc) - fmt.Println(res) //3 -} -``` - - - - -### Difference -

Creates an slice of whose element not included in the other given slice.

- -Signature: - -```go -func Difference(slice1, slice2 interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 3, 4, 5} - s2 := []int{4, 5, 6} - - res := slice.Difference(s1, s2) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### DifferenceBy -

DifferenceBy accepts iteratee func which is invoked for each element of slice and values to generate the criterion by which they're compared.

- -Signature: - -```go -func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 3, 4, 5} - s2 := []int{4, 5, 6} - addOne := func(i int, v int) int { - return v + 1 - } - - res := slice.DifferenceBy(s1, s2, addOne) - fmt.Println(res) //[]int{1, 2} -} -``` - - - - -### DeleteByIndex -

Delete the element of slice from start index to end index - 1.

- -Signature: - -```go -func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.DeleteByIndex([]string{"a", "b", "c", "d", "e"}, 3) - fmt.Println(res1) //[]string{"a", "b", "c", "e"} - - res2 := slice.DeleteByIndex([]string{"a", "b", "c", "d", "e"}, 0, 2) - fmt.Println(res2) //[]string{"c", "d", "e"} - -} -``` - - - - -### Drop -

Creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0.

- -Signature: - -```go -func Drop(slice interface{}, n int) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.Drop([]int{}, 0) - fmt.Println(res1) //[]int{} - - res2 := slice.Drop([]int{1, 2, 3, 4, 5}, 1) - fmt.Println(res2) //[]int{2, 3, 4, 5} - - res3 := slice.Drop([]int{1, 2, 3, 4, 5}, -1) - fmt.Println(res3) //[]int{1, 2, 3, 4} -} -``` - - - - -### Every -

Return true if all of the values in the slice pass the predicate function. The function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func Every(slice, function interface{}) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Every(nums, isEven) - fmt.Println(res) //false -} -``` - - - - -### Filter -

Return all elements which match the function. Function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func Filter(slice, function interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Filter(nums, isEven) - fmt.Println(res) //[]int{2, 4} -} -``` - - - -### Find -

Iterates over elements of slice, returning the first one that passes a truth test on function.function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func Find(slice, function interface{}) (interface{}, bool) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res, ok := slice.Find(nums, even) - fmt.Println(res) //2 - fmt.Println(ok) //true -} -``` - - - - -### FindLast -

iterates over elements of slice from end to begin, returning the last one that passes a truth test on function. The function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func FindLast(slice, function interface{}) (interface{}, bool) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res, ok := slice.FindLast(nums, even) - fmt.Println(res) //4 - fmt.Println(ok) //true -} -``` - - - -### FlattenDeep -

flattens slice recursive.

- -Signature: - -```go -func FlattenDeep(slice interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - arr := [][][]string{{{"a", "b"}}, {{"c", "d"}}} - res := slice.FlattenDeep(arr) - fmt.Println(res) //[]string{"a", "b", "c", "d"} -} -``` - - - - - -### ForEach -

Iterates over elements of slice and invokes function for each element, function signature should be func(index int, value interface{}).

- -Signature: - -```go -func ForEach(slice, function interface{}) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - numbers := []int{1, 2, 3, 4, 5} - var numbersAddTwo []int - slice.ForEach(numbers, func(index int, value int) { - numbersAddTwo = append(numbersAddTwo, value+2) - }) - fmt.Println(numbersAddTwo) //[]int{3, 4, 5, 6, 7} -} -``` - - - - -### GroupBy -

Iterates over elements of the slice, each element will be group by criteria, returns two slices. The function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func GroupBy(slice, function interface{}) (interface{}, interface{}) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5, 6} - evenFunc := func(i, num int) bool { - return (num % 2) == 0 - } - even, odd := slice.GroupBy(nums, evenFunc) - - fmt.Println(even) //[]int{2, 4, 6} - fmt.Println(odd) //]int{1, 3, 5} -} -``` - - - - -### IntSlice -

Convert interface slice to int slice.

- -Signature: - -```go -func IntSlice(slice interface{}) []int -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var nums = []interface{}{1, 2, 3} - res := slice.IntSlice(nums) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### InterfaceSlice -

Convert value to interface slice.

- -Signature: - -```go -func InterfaceSlice(slice interface{}) []interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var nums = []int{}{1, 2, 3} - res := slice.InterfaceSlice(nums) - fmt.Println(res) //[]interface{}{1, 2, 3} -} -``` - - - - -### Intersection -

Creates a slice of unique values that included by all slices.

- -Signature: - -```go -func Intersection(slices ...interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 2, 3} - s2 := []int{1, 2, 3, 4} - res := slice.Intersection(s1, s2), - - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### InsertByIndex -

insert the element into slice at index.

- -Signature: - -```go -func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s := []string{"a", "b", "c"} - - res1, _ := slice.InsertByIndex(s, 0, "1") - fmt.Println(res1) //[]string{"1", "a", "b", "c"} - - res2, _ := slice.InsertByIndex(s, 3, []string{"1", "2", "3"}) - fmt.Println(res2) //[]string{"a", "b", "c", "1", "2", "3"} -} -``` - - - - -### Map -

Creates an slice of values by running each element in slice thru function, function signature should be func(index int, value interface{}) interface{}.

- -Signature: - -```go -func Map(slice, function interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - multiplyTwo := func(i, num int) int { - return num * 2 - } - res := slice.Map(nums, multiplyTwo) - fmt.Println(res) //[]int{2, 4, 6, 8} -} -``` - - - - -### ReverseSlice -

Reverse the elements order in slice.

- -Signature: - -```go -func ReverseSlice(slice interface{}) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - slice.ReverseSlice(nums) - fmt.Println(res) //[]int{4, 3, 2, 1} -} -``` - - - -### Reduce -

Reduce slice, function signature should be func(index int, value1, value2 interface{}) interface{}.

- -Signature: - -```go -func Reduce(slice, function, zero interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - reduceFunc := func(i, v1, v2 int) int { - return v1 + v2 - } - res := slice.Reduce(nums, reduceFunc, 0) - fmt.Println(res) //10 -} -``` - - - - -### Shuffle -

Creates an slice of shuffled values.

- -Signature: - -```go -func Shuffle(slice interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5} - res := slice.Shuffle(nums) - fmt.Println(res) //3,1,5,4,2 -} -``` - - - -### SortByField -

Sort struct slice by field. Slice element should be struct, field type should be int, uint, string, or bool. Default sort type is ascending (asc), if descending order, set sortType to desc

- -Signature: - -```go -func SortByField(slice interface{}, field string, sortType ...string) error -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - type student struct { - name string - age int - } - students := []student{ - {"a", 10}, - {"b", 15}, - {"c", 5}, - {"d", 6}, - } - err := slice.SortByField(students, "age", "desc") - if err != nil { - fmt.Println(err) - } - fmt.Println(students) - // []students{ - // {"b", 15}, - // {"a", 10}, - // {"d", 6}, - // {"c", 5}, - // } -} -``` - - - -### Some -

Return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool.

- -Signature: - -```go -func Some(slice, function interface{}) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Some(nums, isEven) - fmt.Println(res) //true -} -``` - - - -### StringSlice -

Convert interface slice to string slice.

- -Signature: - -```go -func StringSlice(slice interface{}) []string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var s = []interface{}{"a", "b", "c"} - res := slice.StringSlice(s) - fmt.Println(res) //[]string{"a", "b", "c"} -} -``` - - - - -### Unique -

Remove duplicate elements in slice.

- -Signature: - -```go -func Unique(slice interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Unique([]int{1, 2, 2, 3}) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - -### Unique -

Creates a slice of unique values, in order, from all given slices. using == for equality comparisons.

- -Signature: - -```go -func Union(slices ...interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 3, 4, 6} - s2 := []int{1, 2, 5, 6} - res := slice.Union(s1, s2) - - fmt.Println(res) //[]int{1, 3, 4, 6, 2, 5} -} -``` - - - -### UpdateByIndex -

Update the slice element at index. if param index < 0 or index >= len(slice), will return error.

- -Signature: - -```go -func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s := []string{"a", "b", "c"} - - res1, _ := slice.UpdateByIndex(s, 0, "1") - fmt.Println(res1) //[]string{"1", "b", "c"} -} -``` - - - - -### Without -

Creates a slice excluding all given values.

- -Signature: - -```go -func Without(slice interface{}, values ...interface{}) interface{} -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Without([]int{1, 2, 3, 4, 5}, 1, 2) - fmt.Println(res) //[]int{3, 4, 5} -} -``` - - - - - - - - - - - diff --git a/docs/slice_zh-CN.md b/docs/slice_zh-CN.md deleted file mode 100644 index 18bab8d1..00000000 --- a/docs/slice_zh-CN.md +++ /dev/null @@ -1,965 +0,0 @@ -# Slice -slice包包含操作切片的方法集合。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/slice" -) -``` - -
- -## 目录 -- [Contain](#Contain) -- [ContainSubSlice](#ContainSubSlice) -- [Chunk](#Chunk) -- [Compact](#Compact) -- [Concat](#Concat) -- [Count](#Count) -- [Difference](#Difference) -- [DifferenceBy](#DifferenceBy) -- [DeleteByIndex](#DeleteByIndex) -- [Drop](#Drop) -- [Every](#Every) -- [Filter](#Filter) -- [Find](#Find) -- [FindLast](#FindLast) -- [FlattenDeep](#FlattenDeep) -- [ForEach](#ForEach) - -- [GroupBy](#GroupBy) -- [IntSlice](#IntSlice) -- [InterfaceSlice](#InterfaceSlice) -- [Intersection](#Intersection) -- [InsertByIndex](#InsertByIndex) -- [Map](#Map) -- [ReverseSlice](#ReverseSlice) -- [Reduce](#Reduce) -- [Shuffle](#Shuffle) -- [SortByField](#SortByField) -- [Some](#Some) -- [StringSlice](#StringSlice) -- [Unique](#Unique) -- [Union](#Union) -- [UpdateByIndex](#UpdateByIndex) -- [Without](#Without) - -
- -## 文档 - -### Contain -

判断slice是否包含value

- -函数签名: - -```go -func Contain(iterableType interface{}, value interface{}) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Contain([]string{"a", "b", "c"}, "a") - fmt.Println(res) //true -} -``` - - -### ContainSubSlice -

判断slice是否包含subslice

- -函数签名: - -```go -func ContainSubSlice(slice interface{}, subslice interface{}) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"}) - fmt.Println(res) //true -} -``` - - - - -### Chunk -

按照size参数均分slice

- -函数签名: - -```go -func Chunk(slice []interface{}, size int) [][]interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - arr := []string{"a", "b", "c", "d", "e"} - res := slice.Chunk(InterfaceSlice(arr), 3) - fmt.Println(res) //[][]interface{}{{"a", "b", "c"}, {"d", "e"}} -} -``` - - - -### Compact -

去除slice中的假值(false values are false, nil, 0, "")

- -函数签名: - -```go -func Compact(slice interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Compact([]int{0, 1, 2, 3}) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - -### Concat -

连接values到slice中,values类型可以是切片或多个值

- -函数签名: - -```go -func Concat(slice interface{}, values ...interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.Concat([]int{1, 2, 3}, 4, 5) - fmt.Println(res1) //[]int{1, 2, 3, 4, 5} - - res2 := slice.Concat([]int{1, 2, 3}, []int{4, 5}) - fmt.Println(res2) //[]int{1, 2, 3, 4, 5} -} -``` - - - -### Count -

遍历切片,对每个元素执行函数function. 返回符合函数返回值为true的元素的个数,函数签名必须是func(index int, value interface{}) bool

- -函数签名: - -```go -func Count(slice, function interface{}) int -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5, 6} - evenFunc := func(i, num int) bool { - return (num % 2) == 0 - } - - res := slice.Count(nums, evenFunc) - fmt.Println(res) //3 -} -``` - - - - -### Difference -

创建一个切片,其元素不包含在另一个给定切片中

- -函数签名: - -```go -func Difference(slice1, slice2 interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 3, 4, 5} - s2 := []int{4, 5, 6} - - res := slice.Difference(s1, s2) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### DifferenceBy -

在slice和comparedSlice中的每个元素调用iteratee函数,并比较它们的返回值,如果不想等返回在slice中对应的值

- -函数签名: - -```go -func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 3, 4, 5} - s2 := []int{4, 5, 6} - addOne := func(i int, v int) int { - return v + 1 - } - - res := slice.DifferenceBy(s1, s2, addOne) - fmt.Println(res) //[]int{1, 2} -} -``` - - - - -### DeleteByIndex -

删除切片中从开始索引到结束索引-1的元素

- -函数签名: - -```go -func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.DeleteByIndex([]string{"a", "b", "c", "d", "e"}, 3) - fmt.Println(res1) //[]string{"a", "b", "c", "e"} - - res2 := slice.DeleteByIndex([]string{"a", "b", "c", "d", "e"}, 0, 2) - fmt.Println(res2) //[]string{"c", "d", "e"} - -} -``` - - - - -### Drop -

创建一个切片,当 n > 0 时从开头删除 n 个元素,或者当 n < 0 时从结尾删除 n 个元素

- -函数签名: - -```go -func Drop(slice interface{}, n int) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res1 := slice.Drop([]int{}, 0) - fmt.Println(res1) //[]int{} - - res2 := slice.Drop([]int{1, 2, 3, 4, 5}, 1) - fmt.Println(res2) //[]int{2, 3, 4, 5} - - res3 := slice.Drop([]int{1, 2, 3, 4, 5}, -1) - fmt.Println(res3) //[]int{1, 2, 3, 4} -} -``` - - - - -### Every -

如果切片中的所有值都通过谓词函数,则返回true。 函数签名应该是func(index int, value interface{}) bool

- -函数签名: - -```go -func Every(slice, function interface{}) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Every(nums, isEven) - fmt.Println(res) //false -} -``` - - - - -### Filter -

返回与函数匹配的所有元素。 函数签名应该是 func(index int, value interface{}) bool

- -函数签名: - -```go -func Filter(slice, function interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Filter(nums, isEven) - fmt.Println(res) //[]int{2, 4} -} -``` - - - -### Find -

遍历slice的元素,返回第一个通过function真值测试的元素。函数签名应该是 func(index int, value interface{}) bool

- -函数签名: - -```go -func Find(slice, function interface{}) (interface{}, bool) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res, ok := slice.Find(nums, even) - fmt.Println(res) //2 - fmt.Println(ok) //true -} -``` - - - - -### FindLast -

从头到尾遍历 slice 的元素,返回最后一个通过函数真值测试的元素。 函数签名应该是 func(index int, value interface{}) bool。

- -函数签名: - -```go -func FindLast(slice, function interface{}) (interface{}, bool) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res, ok := slice.FindLast(nums, even) - fmt.Println(res) //4 - fmt.Println(ok) //true -} -``` - - - -### FlattenDeep -

flattens slice recursive.

- -函数签名: - -```go -func FlattenDeep(slice interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - arr := [][][]string{{{"a", "b"}}, {{"c", "d"}}} - res := slice.FlattenDeep(arr) - fmt.Println(res) //[]string{"a", "b", "c", "d"} -} -``` - - - - - -### ForEach -

遍历slice的元素并为每个元素调用函数,函数签名应该是func(index int, value interface{})

- -函数签名: - -```go -func ForEach(slice, function interface{}) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - numbers := []int{1, 2, 3, 4, 5} - var numbersAddTwo []int - slice.ForEach(numbers, func(index int, value int) { - numbersAddTwo = append(numbersAddTwo, value+2) - }) - fmt.Println(numbersAddTwo) //[]int{3, 4, 5, 6, 7} -} -``` - - - - -### GroupBy -

迭代切片的元素,每个元素将按条件分组,返回两个切片。 函数签名应该是func(index int, value interface{}) bool

- -函数签名: - -```go -func GroupBy(slice, function interface{}) (interface{}, interface{}) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5, 6} - evenFunc := func(i, num int) bool { - return (num % 2) == 0 - } - even, odd := slice.GroupBy(nums, evenFunc) - - fmt.Println(even) //[]int{2, 4, 6} - fmt.Println(odd) //]int{1, 3, 5} -} -``` - - - - -### IntSlice -

将接口切片转换为int切片

- -函数签名: - -```go -func IntSlice(slice interface{}) []int -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var nums = []interface{}{1, 2, 3} - res := slice.IntSlice(nums) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### InterfaceSlice -

将值转换为接口切片

- -函数签名: - -```go -func InterfaceSlice(slice interface{}) []interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var nums = []int{}{1, 2, 3} - res := slice.InterfaceSlice(nums) - fmt.Println(res) //[]interface{}{1, 2, 3} -} -``` - - - - -### Intersection -

多个切片的交集

- -函数签名: - -```go -func Intersection(slices ...interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 2, 2, 3} - s2 := []int{1, 2, 3, 4} - res := slice.Intersection(s1, s2), - - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - - -### InsertByIndex -

将元素插入到索引处的切片中

- -函数签名: - -```go -func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s := []string{"a", "b", "c"} - - res1, _ := slice.InsertByIndex(s, 0, "1") - fmt.Println(res1) //[]string{"1", "a", "b", "c"} - - res2, _ := slice.InsertByIndex(s, 3, []string{"1", "2", "3"}) - fmt.Println(res2) //[]string{"a", "b", "c", "1", "2", "3"} -} -``` - - - - -### Map -

通过运行函数slice中的每个元素来创建一个值切片,函数签名应该是func(index int, value interface{}) interface{}。

- -函数签名: - -```go -func Map(slice, function interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - multiplyTwo := func(i, num int) int { - return num * 2 - } - res := slice.Map(nums, multiplyTwo) - fmt.Println(res) //[]int{2, 4, 6, 8} -} -``` - - - - -### ReverseSlice -

反转切片中的元素顺序

- -函数签名: - -```go -func ReverseSlice(slice interface{}) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - slice.ReverseSlice(nums) - fmt.Println(res) //[]int{4, 3, 2, 1} -} -``` - - - -### Reduce -

将slice中的元素运行函数,返回运行结果。函数签名应该是func(index int, value1, value2 interface{}) interface{}。

- -函数签名: - -```go -func Reduce(slice, function, zero interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4} - reduceFunc := func(i, v1, v2 int) int { - return v1 + v2 - } - res := slice.Reduce(nums, reduceFunc, 0) - fmt.Println(res) //10 -} -``` - - - - -### Shuffle -

随机打乱切片中的元素顺序

- -函数签名: - -```go -func Shuffle(slice interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 4, 5} - res := slice.Shuffle(nums) - fmt.Println(res) //3,1,5,4,2 -} -``` - - - -### SortByField -

按字段对结构切片进行排序。slice元素应为struct,字段类型应为int、uint、string或bool。 默认排序类型是升序(asc),如果是降序,设置 sortType 为 desc

- -函数签名: - -```go -func SortByField(slice interface{}, field string, sortType ...string) error -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - type student struct { - name string - age int - } - students := []student{ - {"a", 10}, - {"b", 15}, - {"c", 5}, - {"d", 6}, - } - err := slice.SortByField(students, "age", "desc") - if err != nil { - fmt.Println(err) - } - fmt.Println(students) - // []students{ - // {"b", 15}, - // {"a", 10}, - // {"d", 6}, - // {"c", 5}, - // } -} -``` - - - -### Some -

如果列表中的任何值通过谓词函数,则返回true,函数签名应该是func(index int, value interface{}) bool .

- -函数签名: - -```go -func Some(slice, function interface{}) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - nums := []int{1, 2, 3, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } - - res := slice.Some(nums, isEven) - fmt.Println(res) //true -} -``` - - - -### StringSlice -

将接口切片转换为字符串切片

- -函数签名: - -```go -func StringSlice(slice interface{}) []string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - var s = []interface{}{"a", "b", "c"} - res := slice.StringSlice(s) - fmt.Println(res) //[]string{"a", "b", "c"} -} -``` - - - - -### Unique -

删除切片中的重复元素

- -函数签名: - -```go -func Unique(slice interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Unique([]int{1, 2, 2, 3}) - fmt.Println(res) //[]int{1, 2, 3} -} -``` - - - -### Unique -

从所有给定的切片按顺序创建一个唯一值切片。 使用 == 进行相等比较。

- -函数签名: - -```go -func Union(slices ...interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s1 := []int{1, 3, 4, 6} - s2 := []int{1, 2, 5, 6} - res := slice.Union(s1, s2) - - fmt.Println(res) //[]int{1, 3, 4, 6, 2, 5} -} -``` - - - -### UpdateByIndex -

更新索引处的切片元素。 如果 param index < 0 或 index >= len(slice),将返回错误

- -函数签名: - -```go -func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - s := []string{"a", "b", "c"} - - res1, _ := slice.UpdateByIndex(s, 0, "1") - fmt.Println(res1) //[]string{"1", "b", "c"} -} -``` - - - - -### Without -

创建一个不包括所有给定值的切片

- -函数签名: - -```go -func Without(slice interface{}, values ...interface{}) interface{} -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/slice" -) - -func main() { - res := slice.Without([]int{1, 2, 3, 4, 5}, 1, 2) - fmt.Println(res) //[]int{3, 4, 5} -} -``` - - - - - - - - - - - diff --git a/docs/sponsor/sponsor.md b/docs/sponsor/sponsor.md new file mode 100644 index 00000000..b13d4342 --- /dev/null +++ b/docs/sponsor/sponsor.md @@ -0,0 +1,38 @@ +### 赞助 + +您好,我是一名软件开发者,从事开发工作15年。热爱软件开源。并愿意为此付出精力。是开源项目[lancet](https://github.com/duke-git/lancet)的作者。Lancet自2021年前开源发布以来,已有超过1700个内外部项目使用。lancet一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。 + + + + +*捐赠的资金将用于 lancet官网的维护和云服务器的费用支付。或者当我写代码困倦时,给我买杯 ☕️。* diff --git a/docs/strutil.md b/docs/strutil.md deleted file mode 100644 index 7b83263d..00000000 --- a/docs/strutil.md +++ /dev/null @@ -1,574 +0,0 @@ -# Strutil -Package strutil contains some functions to manipulate string. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/strutil/string.go](https://github.com/duke-git/lancet/blob/main/strutil/string.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/strutil" -) -``` - -
- -## Index -- [After](#After) -- [AfterLast](#AfterLast) -- [Before](#Before) -- [BeforeLast](#BeforeLast) -- [CamelCase](#CamelCase) -- [Capitalize](#Capitalize) -- [IsString](#IsString) -- [KebabCase](#KebabCase) -- [LowerFirst](#LowerFirst) -- [UpperFirst](#UpperFirst) -- [PadEnd](#PadEnd) -- [PadStart](#PadStart) -- [ReverseStr](#ReverseStr) -- [SnakeCase](#SnakeCase) -- [Wrap](#Wrap) - -- [Unwrap](#Unwrap) - - -
- -## Documentation - - -### After -

Creates substring in source string after position when char first appear.

- -Signature: - -```go -func After(s, char string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.After("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.After("github.com/test/lancet", "/") - fmt.Println(s2) //test/lancet - - s3 := strutil.After("github.com/test/lancet", "test") - fmt.Println(s3) // /lancet -} -``` - - - -### AfterLast -

Creates substring in source string after position when char last appear.

- -Signature: - -```go -func AfterLast(s, char string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.AfterLast("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.AfterLast("github.com/test/lancet", "/") - fmt.Println(s2) //lancet - - s3 := strutil.AfterLast("github.com/test/test/lancet", "test") - fmt.Println(s3) // /test/lancet -} -``` - - - - -### Before -

Creates substring in source string before position when char first appear.

- -Signature: - -```go -func Before(s, char string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Before("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.Before("github.com/test/lancet", "/") - fmt.Println(s2) //github.com - - s3 := strutil.Before("github.com/test/lancet", "test") - fmt.Println(s3) // github.com/ -} -``` - - - - -### BeforeLast -

Creates substring in source string before position when char first appear.

- -Signature: - -```go -func BeforeLast(s, char string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.BeforeLast("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.BeforeLast("github.com/test/lancet", "/") - fmt.Println(s2) //github.com/test - - s3 := strutil.BeforeLast("github.com/test/test/lancet", "test") - fmt.Println(s3) //github.com/test/ -} -``` - - - - -### CamelCase -

Covert string to camelCase string.

- -Signature: - -```go -func CamelCase(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.CamelCase("foo_bar") - fmt.Println(s1) //fooBar - - s2 := strutil.CamelCase("Foo-Bar") - fmt.Println(s2) //fooBar - - s3 := strutil.CamelCase("Foo&bar") - fmt.Println(s3) //fooBar - - s4 := strutil.CamelCase("foo bar") - fmt.Println(s4) //fooBar -} -``` - - - - -### Capitalize -

Convert the first character of a string to upper case.

- -Signature: - -```go -func Capitalize(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Capitalize("foo") - fmt.Println(s1) //foo - - s2 := strutil.Capitalize("Foo") - fmt.Println(s2) //foo - - s3 := strutil.Capitalize("FOo" - fmt.Println(s3) //fOo -} -``` - - - -### IsString -

Check if the value's data type is string.

- -Signature: - -```go -func IsString(v interface{}) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - fmt.Println(strutil.IsString("lancet")) //true - fmt.Println(strutil.IsString("")) //true - - fmt.Println(strutil.IsString(1)) //false - fmt.Println(strutil.IsString("")) //false - fmt.Println(strutil.IsString([]string{})) //false -} -``` - - - -### KebabCase -

Covert string to kebab-case.

- -Signature: - -```go -func KebabCase(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.KebabCase("Foo Bar-") - fmt.Println(s1) //foo-bar - - s2 := strutil.KebabCase("foo_Bar") - fmt.Println(s2) //foo-bar - - s3 := strutil.KebabCase("fooBar") - fmt.Println(s3) //foo-bar - - s4 := strutil.KebabCase("__FOO_BAR__") - fmt.Println(s4) //f-o-o-b-a-r -} -``` - - - - -### LowerFirst -

Convert the first character of string to lower case.

- -Signature: - -```go -func LowerFirst(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.LowerFirst("foo") - fmt.Println(s1) //foo - - s2 := strutil.LowerFirst("BAR") - fmt.Println(s2) //bAR - - s3 := strutil.LowerFirst("FOo") - fmt.Println(s3) //fOo - - s4 := strutil.LowerFirst("fOo大") - fmt.Println(s4) //fOo大 -} -``` - - - - -### UpperFirst -

Convert the first character of string to upper case.

- -Signature: - -```go -func UpperFirst(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.UpperFirst("foo") - fmt.Println(s1) //Foo - - s2 := strutil.UpperFirst("bAR") - fmt.Println(s2) //BAR - - s3 := strutil.UpperFirst("FOo") - fmt.Println(s3) //FOo - - s4 := strutil.UpperFirst("fOo大") - fmt.Println(s4) //FOo大 -} -``` - - - - -### PadEnd -

Pads string on the right side if it's shorter than size.

- -Signature: - -```go -func PadEnd(source string, size int, padStr string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.PadEnd("a", 1, "b") - fmt.Println(s1) //a - - s2 := strutil.PadEnd("a", 2, "b") - fmt.Println(s2) //ab - - s3 := strutil.PadEnd("abcd", 6, "mno") - fmt.Println(s3) //abcdmn - - s4 := strutil.PadEnd("abc", 6, "ab") - fmt.Println(s4) //abcaba -} -``` - - - - -### PadStart -

Pads string on the left side if it's shorter than size.

- -Signature: - -```go -func PadStart(source string, size int, padStr string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.PadStart("a", 1, "b") - fmt.Println(s1) //a - - s2 := strutil.PadStart("a", 2, "b") - fmt.Println(s2) //ba - - s3 := strutil.PadStart("abcd", 6, "mno") - fmt.Println(s3) //mnabcd - - s4 := strutil.PadStart("abc", 6, "ab") - fmt.Println(s4) //abaabc -} -``` - - - - -### ReverseStr -

Return string whose char order is reversed to the given string.

- -Signature: - -```go -func ReverseStr(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.ReverseStr("abc") - fmt.Println(s1) //cba - - s2 := strutil.ReverseStr("12345") - fmt.Println(s2) //54321 -} -``` - - - -### SnakeCase -

Covert string to snake_case.

- -Signature: - -```go -func SnakeCase(s string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.SnakeCase("Foo Bar-") - fmt.Println(s1) //foo_bar - - s2 := strutil.SnakeCase("foo_Bar") - fmt.Println(s2) //foo_bar - - s3 := strutil.SnakeCase("fooBar") - fmt.Println(s3) //foo_bar - - s4 := strutil.SnakeCase("__FOO_BAR__") - fmt.Println(s4) //f_o_o_b_a_r - - s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") - fmt.Println(s5) //a_bbc_s_a_b_b_c -} -``` - - - - -### Wrap -

Wrap a string with another string.

- -Signature: - -```go -func Wrap(str string, wrapWith string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Wrap("ab", "") - fmt.Println(s1) //ab - - s2 := strutil.Wrap("", "*") - fmt.Println(s2) //"" - - s3 := strutil.Wrap("ab", "*") - fmt.Println(s3) //*ab* - - s4 := strutil.Wrap("ab", "\"") - fmt.Println(s4) //\"ab\" - - s5 := strutil.Wrap("ab", "'") - fmt.Println(s5) //'ab' -} -``` - - - - -### Wrap -

Unwrap a given string from anther string. will change str value.

- -Signature: - -```go -func Unwrap(str string, wrapToken string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Unwrap("ab", "") - fmt.Println(s1) //ab - - s2 := strutil.Unwrap("ab", "*") - fmt.Println(s2) //ab - - s3 := strutil.Unwrap("**ab**", "*") - fmt.Println(s3) //*ab* - - s4 := strutil.Unwrap("*ab", "*") - fmt.Println(s4) //*ab - - s5 := strutil.Unwrap("***", "**") - fmt.Println(s5) //*** -} -``` - - - - - - - - - diff --git a/docs/strutil_zh-CN.md b/docs/strutil_zh-CN.md deleted file mode 100644 index 35d3ef32..00000000 --- a/docs/strutil_zh-CN.md +++ /dev/null @@ -1,575 +0,0 @@ -# Strutil -strutil包含处理字符串的相关函数。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/strutil/string.go](https://github.com/duke-git/lancet/blob/main/strutil/string.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/strutil" -) -``` - -
- -## 目录 -- [After](#After) -- [AfterLast](#AfterLast) -- [Before](#Before) -- [BeforeLast](#BeforeLast) -- [CamelCase](#CamelCase) -- [Capitalize](#Capitalize) -- [IsString](#IsString) -- [KebabCase](#KebabCase) -- [LowerFirst](#LowerFirst) -- [UpperFirst](#UpperFirst) -- [PadEnd](#PadEnd) -- [PadStart](#PadStart) -- [ReverseStr](#ReverseStr) -- [SnakeCase](#SnakeCase) -- [Wrap](#Wrap) - -- [Unwrap](#Unwrap) - - -
- - -## Documentation文档 - - -### After -

截取源字符串中char首次出现时的位置之后的子字符串

- -函数签名: - -```go -func After(s, char string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.After("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.After("github.com/test/lancet", "/") - fmt.Println(s2) //test/lancet - - s3 := strutil.After("github.com/test/lancet", "test") - fmt.Println(s3) // /lancet -} -``` - - - -### AfterLast -

截取源字符串中char最后一次出现时的位置之后的子字符串

- -函数签名: - -```go -func AfterLast(s, char string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.AfterLast("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.AfterLast("github.com/test/lancet", "/") - fmt.Println(s2) //lancet - - s3 := strutil.AfterLast("github.com/test/test/lancet", "test") - fmt.Println(s3) // /lancet -} -``` - - - - -### Before -

截取源字符串中char首次出现时的位置之前的子字符串

- -函数签名: - -```go -func Before(s, char string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Before("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.Before("github.com/test/lancet", "/") - fmt.Println(s2) //github.com - - s3 := strutil.Before("github.com/test/lancet", "test") - fmt.Println(s3) // github.com/ -} -``` - - - - -### BeforeLast -

截取源字符串中char最后一次出现时的位置之前的子字符串

- -函数签名: - -```go -func BeforeLast(s, char string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.BeforeLast("lancet", "") - fmt.Println(s1) //lancet - - s2 := strutil.BeforeLast("github.com/test/lancet", "/") - fmt.Println(s2) //github.com/test - - s3 := strutil.BeforeLast("github.com/test/test/lancet", "test") - fmt.Println(s3) //github.com/test/ -} -``` - - - - -### CamelCase -

将字符串转换为驼峰式字符串

- -函数签名: - -```go -func CamelCase(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.CamelCase("foo_bar") - fmt.Println(s1) //fooBar - - s2 := strutil.CamelCase("Foo-Bar") - fmt.Println(s2) //fooBar - - s3 := strutil.CamelCase("Foo&bar") - fmt.Println(s3) //fooBar - - s4 := strutil.CamelCase("foo bar") - fmt.Println(s4) //fooBar -} -``` - - - - -### Capitalize -

将字符串的第一个字符转换为大写

- -函数签名: - -```go -func Capitalize(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Capitalize("foo") - fmt.Println(s1) //foo - - s2 := strutil.Capitalize("Foo") - fmt.Println(s2) //foo - - s3 := strutil.Capitalize("FOo" - fmt.Println(s3) //fOo -} -``` - - - -### IsString -

检查值的数据类型是否为字符串

- -函数签名: - -```go -func IsString(v interface{}) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - fmt.Println(strutil.IsString("lancet")) //true - fmt.Println(strutil.IsString("")) //true - - fmt.Println(strutil.IsString(1)) //false - fmt.Println(strutil.IsString("")) //false - fmt.Println(strutil.IsString([]string{})) //false -} -``` - - - -### KebabCase -

将字符串转换为kebab-case

- -函数签名: - -```go -func KebabCase(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.KebabCase("Foo Bar-") - fmt.Println(s1) //foo-bar - - s2 := strutil.KebabCase("foo_Bar") - fmt.Println(s2) //foo-bar - - s3 := strutil.KebabCase("fooBar") - fmt.Println(s3) //foo-bar - - s4 := strutil.KebabCase("__FOO_BAR__") - fmt.Println(s4) //f-o-o-b-a-r -} -``` - - - - -### LowerFirst -

将字符串的第一个字符转换为小写

- -函数签名: - -```go -func LowerFirst(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.LowerFirst("foo") - fmt.Println(s1) //foo - - s2 := strutil.LowerFirst("BAR") - fmt.Println(s2) //bAR - - s3 := strutil.LowerFirst("FOo") - fmt.Println(s3) //fOo - - s4 := strutil.LowerFirst("fOo大") - fmt.Println(s4) //fOo大 -} -``` - - - - -### UpperFirst -

将字符串的第一个字符转换为大写

- -函数签名: - -```go -func UpperFirst(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.UpperFirst("foo") - fmt.Println(s1) //Foo - - s2 := strutil.UpperFirst("bAR") - fmt.Println(s2) //BAR - - s3 := strutil.UpperFirst("FOo") - fmt.Println(s3) //FOo - - s4 := strutil.UpperFirst("fOo大") - fmt.Println(s4) //FOo大 -} -``` - - - - -### PadEnd -

如果字符串长度短于size,则在右侧填充字符串

- -函数签名: - -```go -func PadEnd(source string, size int, padStr string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.PadEnd("a", 1, "b") - fmt.Println(s1) //a - - s2 := strutil.PadEnd("a", 2, "b") - fmt.Println(s2) //ab - - s3 := strutil.PadEnd("abcd", 6, "mno") - fmt.Println(s3) //abcdmn - - s4 := strutil.PadEnd("abc", 6, "ab") - fmt.Println(s4) //abcaba -} -``` - - - - -### PadStart -

如果字符串长度短于size,则在左侧填充字符串

- -函数签名: - -```go -func PadStart(source string, size int, padStr string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.PadStart("a", 1, "b") - fmt.Println(s1) //a - - s2 := strutil.PadStart("a", 2, "b") - fmt.Println(s2) //ba - - s3 := strutil.PadStart("abcd", 6, "mno") - fmt.Println(s3) //mnabcd - - s4 := strutil.PadStart("abc", 6, "ab") - fmt.Println(s4) //abaabc -} -``` - - - - -### ReverseStr -

返回字符顺序与给定字符串相反的字符串

- -函数签名: - -```go -func ReverseStr(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.ReverseStr("abc") - fmt.Println(s1) //cba - - s2 := strutil.ReverseStr("12345") - fmt.Println(s2) //54321 -} -``` - - - -### SnakeCase -

将字符串转换为snake_case形式

- -函数签名: - -```go -func SnakeCase(s string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.SnakeCase("Foo Bar-") - fmt.Println(s1) //foo_bar - - s2 := strutil.SnakeCase("foo_Bar") - fmt.Println(s2) //foo_bar - - s3 := strutil.SnakeCase("fooBar") - fmt.Println(s3) //foo_bar - - s4 := strutil.SnakeCase("__FOO_BAR__") - fmt.Println(s4) //f_o_o_b_a_r - - s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") - fmt.Println(s5) //a_bbc_s_a_b_b_c -} -``` - - - - -### Wrap -

用另一个字符串包裹一个字符串

- -函数签名: - -```go -func Wrap(str string, wrapWith string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Wrap("ab", "") - fmt.Println(s1) //ab - - s2 := strutil.Wrap("", "*") - fmt.Println(s2) //"" - - s3 := strutil.Wrap("ab", "*") - fmt.Println(s3) //*ab* - - s4 := strutil.Wrap("ab", "\"") - fmt.Println(s4) //\"ab\" - - s5 := strutil.Wrap("ab", "'") - fmt.Println(s5) //'ab' -} -``` - - - - -### Unwrap -

用另一个字符串解开包裹一个字符串

- -函数签名: - -```go -func Unwrap(str string, wrapToken string) string -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/strutil" -) - -func main() { - s1 := strutil.Unwrap("ab", "") - fmt.Println(s1) //ab - - s2 := strutil.Unwrap("ab", "*") - fmt.Println(s2) //ab - - s3 := strutil.Unwrap("**ab**", "*") - fmt.Println(s3) //*ab* - - s4 := strutil.Unwrap("*ab", "*") - fmt.Println(s4) //*ab - - s5 := strutil.Unwrap("***", "**") - fmt.Println(s5) //*** -} -``` - - - - - - - - - diff --git a/docs/system.md b/docs/system.md deleted file mode 100644 index 6e2adaee..00000000 --- a/docs/system.md +++ /dev/null @@ -1,242 +0,0 @@ -# System -Package system contains some functions about os, runtime, shell command. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/system/os.go](https://github.com/duke-git/lancet/blob/main/system/os.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/system" -) -``` - -
- -## Index -- [IsWindows](#IsWindows) -- [IsLinux](#IsLinux) -- [IsMac](#IsMac) -- [GetOsEnv](#GetOsEnv) -- [SetOsEnv](#SetOsEnv) -- [RemoveOsEnv](#RemoveOsEnv) -- [CompareOsEnv](#CompareOsEnv) -- [ExecCommand](#ExecCommand) - - -
- -## Documentation - - -### IsWindows -

Check if current os is windows.

- -Signature: - -```go -func IsWindows() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsWindows := system.IsWindows() - fmt.Println(isOsWindows) -} -``` - - - - -### IsLinux -

Check if current os is linux.

- -Signature: - -```go -func IsLinux() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsLinux := system.IsLinux() - fmt.Println(isOsLinux) -} -``` - - - -### IsMac -

Check if current os is macos.

- -Signature: - -```go -func IsMac() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsMac := system.IsMac - fmt.Println(isOsMac) -} -``` - - - -### GetOsEnv -

Gets the value of the environment variable named by the key.

- -Signature: - -```go -func GetOsEnv(key string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - fooEnv := system.GetOsEnv("foo") - fmt.Println(fooEnv) -} -``` - - - -### SetOsEnv -

Sets the value of the environment variable named by the key.

- -Signature: - -```go -func SetOsEnv(key, value string) error -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - err := system.SetOsEnv("foo", "foo_value") - fmt.Println(err) -} -``` - - - - -### RemoveOsEnv -

Remove a single environment variable.

- -Signature: - -```go -func RemoveOsEnv(key string) error -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - err := system.RemoveOsEnv("foo") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### CompareOsEnv -

Get env named by the key and compare it with comparedEnv.

- -Signature: - -```go -func CompareOsEnv(key, comparedEnv string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - system.SetOsEnv("foo", "foo_value") - res := system.CompareOsEnv("foo", "foo_value") - fmt.Println(res) //true -} -``` - - - - -### CompareOsEnv -

use shell /bin/bash -c(linux) or cmd (windows) to execute command.

- -Signature: - -```go -func ExecCommand(command string) (stdout, stderr string, err error) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - out, errout, err := system.ExecCommand("ls") - fmt.Println(out) - fmt.Println(errout) - fmt.Println(err) -} -``` - - - - - - - - diff --git a/docs/system_zh-CN.md b/docs/system_zh-CN.md deleted file mode 100644 index 166d892b..00000000 --- a/docs/system_zh-CN.md +++ /dev/null @@ -1,242 +0,0 @@ -# System -system包含os, runtime, shell command相关函数。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/system/os.go](https://github.com/duke-git/lancet/blob/main/system/os.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/system" -) -``` - -
- -## 目录 -- [IsWindows](#IsWindows) -- [IsLinux](#IsLinux) -- [IsMac](#IsMac) -- [GetOsEnv](#GetOsEnv) -- [SetOsEnv](#SetOsEnv) -- [RemoveOsEnv](#RemoveOsEnv) -- [CompareOsEnv](#CompareOsEnv) -- [ExecCommand](#ExecCommand) - - -
- -## Documentation文档 - - -### IsWindows -

检查当前操作系统是否是windows

- -Signature: - -```go -func IsWindows() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsWindows := system.IsWindows() - fmt.Println(isOsWindows) -} -``` - - - - -### IsLinux -

检查当前操作系统是否是linux

- -Signature: - -```go -func IsLinux() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsLinux := system.IsLinux() - fmt.Println(isOsLinux) -} -``` - - - -### IsMac -

检查当前操作系统是否是macos

- -Signature: - -```go -func IsMac() bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - isOsMac := system.IsMac - fmt.Println(isOsMac) -} -``` - - - -### GetOsEnv -

获取key命名的环境变量的值

- -Signature: - -```go -func GetOsEnv(key string) string -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - fooEnv := system.GetOsEnv("foo") - fmt.Println(fooEnv) -} -``` - - - -### SetOsEnv -

设置由key命名的环境变量的值

- -Signature: - -```go -func SetOsEnv(key, value string) error -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - err := system.SetOsEnv("foo", "foo_value") - fmt.Println(err) -} -``` - - - - -### RemoveOsEnv -

删除单个环境变量

- -Signature: - -```go -func RemoveOsEnv(key string) error -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - err := system.RemoveOsEnv("foo") - if err != nil { - fmt.Println(err) - } -} -``` - - - -### CompareOsEnv -

获取key命名的环境变量值并与compareEnv进行比较

- -Signature: - -```go -func CompareOsEnv(key, comparedEnv string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - system.SetOsEnv("foo", "foo_value") - res := system.CompareOsEnv("foo", "foo_value") - fmt.Println(res) //true -} -``` - - - - -### CompareOsEnv -

使用shell /bin/bash -c(linux) 或 cmd (windows) 执行shell命令

- -Signature: - -```go -func ExecCommand(command string) (stdout, stderr string, err error) -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/system" -) - -func main() { - out, errout, err := system.ExecCommand("ls") - fmt.Println(out) - fmt.Println(errout) - fmt.Println(err) -} -``` - - - - - - - - diff --git a/docs/validator.md b/docs/validator.md deleted file mode 100644 index f4275f10..00000000 --- a/docs/validator.md +++ /dev/null @@ -1,799 +0,0 @@ -# Validator -Package validator contains some functions for data validation. - -
- -## Source: - -[https://github.com/duke-git/lancet/blob/main/validator/validator.go](https://github.com/duke-git/lancet/blob/main/validator/validator.go) - - -
- -## Usage: -```go -import ( - "github.com/duke-git/lancet/validator" -) -``` - -
- -## Index -- [ContainChinese](#ContainChinese) -- [ContainLetter](#ContainLetter) -- [ContainLower](#ContainLower) -- [ContainUpper](#ContainUpper) -- [IsAlpha](#IsAlpha) -- [IsAllUpper](#IsAllUpper) -- [IsAllLower](#IsAllLower) -- [IsBase64](#IsBase64) -- [IsChineseMobile](#IsChineseMobile) -- [IsChineseIdNum](#IsChineseIdNum) -- [IsChinesePhone](#IsChinesePhone) -- [IsCreditCard](#IsCreditCard) -- [IsDns](#IsDns) -- [IsEmail](#IsEmail) - -- [IsEmptyString](#IsEmptyString) -- [IsFloatStr](#IsFloatStr) -- [IsNumberStr](#IsNumberStr) -- [IsJSON](#IsJSON) -- [IsRegexMatch](#IsRegexMatch) -- [IsIntStr](#IsIntStr) -- [IsIp](#IsIp) -- [IsIpV4](#IsIpV4) -- [IsIpV6](#IsIpV6) -- [IsStrongPassword](#IsStrongPassword) -- [IsUrl](#IsUrl) -- [IsWeakPassword](#IsWeakPassword) - - -
- -## Documentation - - -### ContainChinese -

Check if the string contain mandarin chinese.

- -Signature: - -```go -func ContainChinese(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainChinese("你好") - fmt.Println(res1) //true - - res2 := validator.ContainChinese("你好hello") - fmt.Println(res2) //true - - res3 := validator.ContainChinese("hello") - fmt.Println(res3) //false -} -``` - - - -### ContainLetter -

Check if the string contain at least one letter.

- -Signature: - -```go -func ContainLetter(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainLetter("1bc") - fmt.Println(res1) //true - - res2 := validator.ContainLetter("123") - fmt.Println(res2) //false - - res3 := validator.ContainLetter("&@#$%^&*") - fmt.Println(res3) //false -} -``` - - - - -### ContainLower -

Check if the string contain at least one lower case letter a-z.

- -Signature: - -```go -func ContainLower(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainLower("1bc") - fmt.Println(res1) //true - - res2 := validator.ContainLower("123") - fmt.Println(res2) //false - - res3 := validator.ContainLower("1BC") - fmt.Println(res3) //false -} -``` - - - - -### ContainUpper -

Check if the string contain at least one upper case letter A-Z.

- -Signature: - -```go -func ContainUpper(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainUpper("1bc") - fmt.Println(res1) //false - - res2 := validator.ContainUpper("123") - fmt.Println(res2) //false - - res3 := validator.ContainUpper("1BC") - fmt.Println(res3) //true -} -``` - - - - -### IsAlpha -

Check if the string contains only letters (a-zA-Z).

- -Signature: - -```go -func IsAlpha(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAlpha("abc") - fmt.Println(res1) //true - - res2 := validator.IsAlpha("1bc") - fmt.Println(res2) //false - - res3 := validator.IsAlpha("") - fmt.Println(res3) //false -} -``` - -### IsAllUpper -

Check if string is all upper case letters A-Z.

- -Signature: - -```go -func IsAllUpper(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAllUpper("ABC") - fmt.Println(res1) //true - - res2 := validator.IsAllUpper("aBC") - fmt.Println(res2) //false -} -``` - - - - -### IsAllLower -

Check if string is all lower case letters a-z.

- -Signature: - -```go -func IsAllLower(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAllLower("abc") - fmt.Println(res1) //true - - res2 := validator.IsAllLower("abC") - fmt.Println(res2) //false -} -``` - - - - -### IsBase64 -

Check if the string is base64 string.

- -Signature: - -```go -func IsBase64(base64 string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsBase64("aGVsbG8=") - fmt.Println(res1) //true - - res2 := validator.IsBase64("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsChineseMobile -

Check if the string is valid chinese mobile number.

- -Signature: - -```go -func IsChineseMobile(mobileNum string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChineseMobile("13263527980") - fmt.Println(res1) //true - - res2 := validator.IsChineseMobile("434324324") - fmt.Println(res2) //false -} -``` - - - -### IsChineseIdNum -

Check if the string is chinese id number.

- -Signature: - -```go -func IsChineseIdNum(id string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChineseIdNum("210911192105130715") - fmt.Println(res1) //true - - res2 := validator.IsChineseIdNum("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsChinesePhone -

Check if the string is chinese phone number.

- -Signature: - -```go -func IsChinesePhone(phone string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChinesePhone("010-32116675") - fmt.Println(res1) //true - - res2 := validator.IsChinesePhone("123-87562") - fmt.Println(res2) //false -} -``` - - - - -### IsCreditCard -

Check if the string is credit card.

- -Signature: - -```go -func IsCreditCard(creditCart string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsCreditCard("4111111111111111") - fmt.Println(res1) //true - - res2 := validator.IsCreditCard("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsDns -

Check if the string is valid dns.

- -Signature: - -```go -func IsDns(dns string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsDns("abc.com") - fmt.Println(res1) //true - - res2 := validator.IsDns("a.b.com") - fmt.Println(res2) //false - - res3 := validator.IsDns("http://abc.com") - fmt.Println(res3) //false -} -``` - - - - -### IsEmail -

Check if the string is email address.

- -Signature: - -```go -func IsEmail(email string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsEmail("abc@xyz.com") - fmt.Println(res1) //true - - res2 := validator.IsEmail("a.b@@com") - fmt.Println(res2) //false -} -``` - - - - - -### IsEmptyString -

Check if the string is empty or not.

- -Signature: - -```go -func IsEmptyString(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsEmptyString("") - fmt.Println(res1) //true - - res2 := validator.IsEmptyString("abc") - fmt.Println(res2) //false -} -``` - - - - -### IsFloatStr -

Check if the string can convert to a float.

- -Signature: - -```go -func IsFloatStr(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsFloatStr("")) //false - fmt.Println(validator.IsFloatStr("12a")) //false - fmt.Println(validator.IsFloatStr("3.")) //true - fmt.Println(validator.IsFloatStr("+3.")) //true - fmt.Println(validator.IsFloatStr("-3.")) //true - fmt.Println(validator.IsFloatStr("12")) //true -} -``` - - - - -### IsNumberStr -

Check if the string can convert to a number.

- -Signature: - -```go -func IsNumberStr(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsNumberStr("")) //false - fmt.Println(validator.IsNumberStr("12a")) //false - fmt.Println(validator.IsNumberStr("3.")) //true - fmt.Println(validator.IsNumberStr("+3.")) //true - fmt.Println(validator.IsNumberStr("-3.")) //true - fmt.Println(validator.IsNumberStr("+3e2")) //true -} -``` - - - - -### IsJSON -

Check if the string is valid JSON.

- -Signature: - -```go -func IsJSON(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsJSON("")) //false - fmt.Println(validator.IsJSON("abc")) //false - fmt.Println(validator.IsJSON("{}")) //true - fmt.Println(validator.IsJSON("[]")) //true - fmt.Println(validator.IsJSON("123")) //true - fmt.Println(validator.IsJSON("{\"name\": \"test\"}")) //true -} -``` - - - - -### IsRegexMatch -

Check if the string match the regexp.

- -Signature: - -```go -func IsRegexMatch(s, regex string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsRegexMatch("abc", `^[a-zA-Z]+$`)) //true - fmt.Println(validator.IsRegexMatch("1ab", `^[a-zA-Z]+$`)) //false - fmt.Println(validator.IsRegexMatch("", `^[a-zA-Z]+$`)) //false -} -``` - - - - -### IsIntStr -

Check if the string can convert to a integer.

- -Signature: - -```go -func IsIntStr(s string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIntStr("+3")) //true - fmt.Println(validator.IsIntStr("-3")) //true - fmt.Println(validator.IsIntStr("3.")) //false - fmt.Println(validator.IsIntStr("abc")) //false -} -``` - - - - -### IsIp -

Check if the string is a ip address.

- -Signature: - -```go -func IsIp(ipstr string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIp("127.0.0.1")) //true - fmt.Println(validator.IsIp("::0:0:0:0:0:0:1")) //true - fmt.Println(validator.IsIp("127.0.0")) //false - fmt.Println(validator.IsIp("127")) //false -} -``` - - - - -### IsIpV4 -

Check if the string is a ipv4 address.

- -Signature: - -```go -func IsIpV4(ipstr string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIpV4("127.0.0.1")) //true - fmt.Println(validator.IsIpV4("::0:0:0:0:0:0:1")) //false - fmt.Println(validator.IsIpV4("127.0.0")) //false - fmt.Println(validator.IsIpV4("127")) //false -} -``` - - - - -### IsIpV6 -

Check if the string is a ipv6 address.

- -Signature: - -```go -func IsIpV6(ipstr string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIpV6("127.0.0.1")) //false - fmt.Println(validator.IsIpV6("::0:0:0:0:0:0:1")) //true - fmt.Println(validator.IsIpV6("127.0.0")) //false - fmt.Println(validator.IsIpV6("127")) //false -} -``` - - - - -### IsStrongPassword -

Check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?><)).

- -Signature: - -```go -func IsStrongPassword(password string, length int) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsStrongPassword("abc", 3)) //false - fmt.Println(validator.IsStrongPassword("abc123", 6)) //false - fmt.Println(validator.IsStrongPassword("abcABC", 6)) //false - fmt.Println(validator.IsStrongPassword("abcABC123@#$", 16)) //false - fmt.Println(validator.IsStrongPassword("abcABC123@#$", 12)) //true -} -``` - - - - -### IsUrl -

Check if the string is url.

- -Signature: - -```go -func IsUrl(str string) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsUrl("http://abc.com")) //true - fmt.Println(validator.IsUrl("abc.com")) //true - fmt.Println(validator.IsUrl("a.b.com")) //true - fmt.Println(validator.IsUrl("abc")) //false -} -``` - - - - -### IsWeakPassword -

Check if the string is weak password(only letter or only number or letter + number) -.

- -Signature: - -```go -func IsWeakPassword(password string, length int) bool -``` -Example: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsWeakPassword("abc")) //true - fmt.Println(validator.IsWeakPassword("123")) //true - fmt.Println(validator.IsWeakPassword("abc123")) //true - fmt.Println(validator.IsWeakPassword("abc123@#$")) //false -} -``` - - - - - - - - - diff --git a/docs/validator_zh-CN.md b/docs/validator_zh-CN.md deleted file mode 100644 index 00c5e103..00000000 --- a/docs/validator_zh-CN.md +++ /dev/null @@ -1,799 +0,0 @@ -# Validator -validator验证器包,包含常用字符串格式验证函数。 - -
- -## 源码: - -[https://github.com/duke-git/lancet/blob/main/validator/validator.go](https://github.com/duke-git/lancet/blob/main/validator/validator.go) - - -
- -## 用法: -```go -import ( - "github.com/duke-git/lancet/validator" -) -``` - -
- -## 目录: -- [ContainChinese](#ContainChinese) -- [ContainLetter](#ContainLetter) -- [ContainLower](#ContainLower) -- [ContainUpper](#ContainUpper) -- [IsAlpha](#IsAlpha) -- [IsAllUpper](#IsAllUpper) -- [IsAllLower](#IsAllLower) -- [IsBase64](#IsBase64) -- [IsChineseMobile](#IsChineseMobile) -- [IsChineseIdNum](#IsChineseIdNum) -- [IsChinesePhone](#IsChinesePhone) -- [IsCreditCard](#IsCreditCard) -- [IsDns](#IsDns) -- [IsEmail](#IsEmail) - -- [IsEmptyString](#IsEmptyString) -- [IsFloatStr](#IsFloatStr) -- [IsNumberStr](#IsNumberStr) -- [IsJSON](#IsJSON) -- [IsRegexMatch](#IsRegexMatch) -- [IsIntStr](#IsIntStr) -- [IsIp](#IsIp) -- [IsIpV4](#IsIpV4) -- [IsIpV6](#IsIpV6) -- [IsStrongPassword](#IsStrongPassword) -- [IsUrl](#IsUrl) -- [IsWeakPassword](#IsWeakPassword) - - -
- -## 文档 - - -### ContainChinese -

验证字符串是否包含中文字符

- -函数签名: - -```go -func ContainChinese(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainChinese("你好") - fmt.Println(res1) //true - - res2 := validator.ContainChinese("你好hello") - fmt.Println(res2) //true - - res3 := validator.ContainChinese("hello") - fmt.Println(res3) //false -} -``` - - - -### ContainLetter -

验证字符串是否包含至少一个英文字母

- -函数签名: - -```go -func ContainLetter(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainLetter("1bc") - fmt.Println(res1) //true - - res2 := validator.ContainLetter("123") - fmt.Println(res2) //false - - res3 := validator.ContainLetter("&@#$%^&*") - fmt.Println(res3) //false -} -``` - - - - -### ContainLower -

验证字符串是否包含至少一个英文小写字母

- -函数签名: - -```go -func ContainLower(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainLower("1bc") - fmt.Println(res1) //true - - res2 := validator.ContainLower("123") - fmt.Println(res2) //false - - res3 := validator.ContainLower("1BC") - fmt.Println(res3) //false -} -``` - - - - -### ContainUpper -

验证字符串是否包含至少一个英文大写字母.

- -函数签名: - -```go -func ContainUpper(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.ContainUpper("1bc") - fmt.Println(res1) //false - - res2 := validator.ContainUpper("123") - fmt.Println(res2) //false - - res3 := validator.ContainUpper("1BC") - fmt.Println(res3) //true -} -``` - - - - -### IsAlpha -

验证字符串是否只包含英文字母

- -函数签名: - -```go -func IsAlpha(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAlpha("abc") - fmt.Println(res1) //true - - res2 := validator.IsAlpha("1bc") - fmt.Println(res2) //false - - res3 := validator.IsAlpha("") - fmt.Println(res3) //false -} -``` - -### IsAllUpper -

验证字符串是否全是大写英文字母

- -函数签名: - -```go -func IsAllUpper(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAllUpper("ABC") - fmt.Println(res1) //true - - res2 := validator.IsAllUpper("aBC") - fmt.Println(res2) //false -} -``` - - - - -### IsAllLower -

验证字符串是否全是小写英文字母

- -函数签名: - -```go -func IsAllLower(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsAllLower("abc") - fmt.Println(res1) //true - - res2 := validator.IsAllLower("abC") - fmt.Println(res2) //false -} -``` - - - - -### IsBase64 -

验证字符串是否是base64编码

- -函数签名: - -```go -func IsBase64(base64 string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsBase64("aGVsbG8=") - fmt.Println(res1) //true - - res2 := validator.IsBase64("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsChineseMobile -

验证字符串是否是中国手机号码

- -函数签名: - -```go -func IsChineseMobile(mobileNum string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChineseMobile("13263527980") - fmt.Println(res1) //true - - res2 := validator.IsChineseMobile("434324324") - fmt.Println(res2) //false -} -``` - - - -### IsChineseIdNum -

验证字符串是否是中国身份证号码

- -函数签名: - -```go -func IsChineseIdNum(id string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChineseIdNum("210911192105130715") - fmt.Println(res1) //true - - res2 := validator.IsChineseIdNum("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsChinesePhone -

验证字符串是否是中国电话座机号码

- -函数签名: - -```go -func IsChinesePhone(phone string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsChinesePhone("010-32116675") - fmt.Println(res1) //true - - res2 := validator.IsChinesePhone("123-87562") - fmt.Println(res2) //false -} -``` - - - - -### IsCreditCard -

验证字符串是否是信用卡号码

- -函数签名: - -```go -func IsCreditCard(creditCart string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsCreditCard("4111111111111111") - fmt.Println(res1) //true - - res2 := validator.IsCreditCard("123456") - fmt.Println(res2) //false -} -``` - - - - -### IsDns -

验证字符串是否是有效dns

- -函数签名: - -```go -func IsDns(dns string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsDns("abc.com") - fmt.Println(res1) //true - - res2 := validator.IsDns("a.b.com") - fmt.Println(res2) //false - - res3 := validator.IsDns("http://abc.com") - fmt.Println(res3) //false -} -``` - - - - -### IsEmail -

验证字符串是否是有效电子邮件地址

- -函数签名: - -```go -func IsEmail(email string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsEmail("abc@xyz.com") - fmt.Println(res1) //true - - res2 := validator.IsEmail("a.b@@com") - fmt.Println(res2) //false -} -``` - - - - - -### IsEmptyString -

验证字符串是否是空字符串

- -函数签名: - -```go -func IsEmptyString(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - res1 := validator.IsEmptyString("") - fmt.Println(res1) //true - - res2 := validator.IsEmptyString("abc") - fmt.Println(res2) //false -} -``` - - - - -### IsFloatStr -

验证字符串是否是可以转换为浮点数

- -函数签名: - -```go -func IsFloatStr(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsFloatStr("")) //false - fmt.Println(validator.IsFloatStr("12a")) //false - fmt.Println(validator.IsFloatStr("3.")) //true - fmt.Println(validator.IsFloatStr("+3.")) //true - fmt.Println(validator.IsFloatStr("-3.")) //true - fmt.Println(validator.IsFloatStr("12")) //true -} -``` - - - - -### IsNumberStr -

验证字符串是否是可以转换为数字

- -函数签名: - -```go -func IsNumberStr(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsNumberStr("")) //false - fmt.Println(validator.IsNumberStr("12a")) //false - fmt.Println(validator.IsNumberStr("3.")) //true - fmt.Println(validator.IsNumberStr("+3.")) //true - fmt.Println(validator.IsNumberStr("-3.")) //true - fmt.Println(validator.IsNumberStr("+3e2")) //true -} -``` - - - - -### IsJSON -

验证字符串是否是有效json

- -函数签名: - -```go -func IsJSON(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsJSON("")) //false - fmt.Println(validator.IsJSON("abc")) //false - fmt.Println(validator.IsJSON("{}")) //true - fmt.Println(validator.IsJSON("[]")) //true - fmt.Println(validator.IsJSON("123")) //true - fmt.Println(validator.IsJSON("{\"name\": \"test\"}")) //true -} -``` - - - - -### IsRegexMatch -

验证字符串是否可以匹配正则表达式

- -函数签名: - -```go -func IsRegexMatch(s, regex string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsRegexMatch("abc", `^[a-zA-Z]+$`)) //true - fmt.Println(validator.IsRegexMatch("1ab", `^[a-zA-Z]+$`)) //false - fmt.Println(validator.IsRegexMatch("", `^[a-zA-Z]+$`)) //false -} -``` - - - - -### IsIntStr -

验证字符串是否是可以转换为整数

- -函数签名: - -```go -func IsIntStr(s string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIntStr("+3")) //true - fmt.Println(validator.IsIntStr("-3")) //true - fmt.Println(validator.IsIntStr("3.")) //false - fmt.Println(validator.IsIntStr("abc")) //false -} -``` - - - - -### IsIp -

验证字符串是否是ip地址

- -函数签名: - -```go -func IsIp(ipstr string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIp("127.0.0.1")) //true - fmt.Println(validator.IsIp("::0:0:0:0:0:0:1")) //true - fmt.Println(validator.IsIp("127.0.0")) //false - fmt.Println(validator.IsIp("127")) //false -} -``` - - - - -### IsIpV4 -

验证字符串是否是ipv4地址

- -函数签名: - -```go -func IsIpV4(ipstr string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIpV4("127.0.0.1")) //true - fmt.Println(validator.IsIpV4("::0:0:0:0:0:0:1")) //false - fmt.Println(validator.IsIpV4("127.0.0")) //false - fmt.Println(validator.IsIpV4("127")) //false -} -``` - - - - -### IsIpV6 -

验证字符串是否是ipv6地址

- -函数签名: - -```go -func IsIpV6(ipstr string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsIpV6("127.0.0.1")) //false - fmt.Println(validator.IsIpV6("::0:0:0:0:0:0:1")) //true - fmt.Println(validator.IsIpV6("127.0.0")) //false - fmt.Println(validator.IsIpV6("127")) //false -} -``` - - - - -### IsStrongPassword -

验证字符串是否是强密码:(alpha(lower+upper) + number + special chars(!@#$%^&*()?><))

- -函数签名: - -```go -func IsStrongPassword(password string, length int) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsStrongPassword("abc", 3)) //false - fmt.Println(validator.IsStrongPassword("abc123", 6)) //false - fmt.Println(validator.IsStrongPassword("abcABC", 6)) //false - fmt.Println(validator.IsStrongPassword("abcABC123@#$", 16)) //false - fmt.Println(validator.IsStrongPassword("abcABC123@#$", 12)) //true -} -``` - - - - -### IsUrl -

验证字符串是否是url

- -函数签名: - -```go -func IsUrl(str string) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsUrl("http://abc.com")) //true - fmt.Println(validator.IsUrl("abc.com")) //true - fmt.Println(validator.IsUrl("a.b.com")) //true - fmt.Println(validator.IsUrl("abc")) //false -} -``` - - - - -### IsWeakPassword -

验证字符串是否是弱密码:(only letter or only number or letter + number) -.

- -函数签名: - -```go -func IsWeakPassword(password string, length int) bool -``` -例子: - -```go -import ( - "fmt" - "github.com/duke-git/lancet/validator" -) - -func main() { - fmt.Println(validator.IsWeakPassword("abc")) //true - fmt.Println(validator.IsWeakPassword("123")) //true - fmt.Println(validator.IsWeakPassword("abc123")) //true - fmt.Println(validator.IsWeakPassword("abc123@#$")) //false -} -``` - - - - - - - - - diff --git a/eventbus/eventbus.go b/eventbus/eventbus.go new file mode 100644 index 00000000..8fe9b25d --- /dev/null +++ b/eventbus/eventbus.go @@ -0,0 +1,195 @@ +// Copyright 2025 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package eventbus implements a simple event bus. +package eventbus + +import ( + "fmt" + "sort" + "sync" +) + +// Event is the struct that is passed to the event listener, now it directly uses the generic Payload type. +type Event[T any] struct { + Topic string + Payload T +} + +// EventBus is the struct that holds the listeners and the error handler. +type EventBus[T any] struct { + // listeners map[string][]*EventListener[T] + listeners sync.Map + mu sync.RWMutex + errorHandler func(err error) +} + +// EventListener is the struct that holds the listener function and its priority. +type EventListener[T any] struct { + priority int + listener func(eventData T) + async bool + filter func(eventData T) bool +} + +// NewEventBus creates a new EventBus. +// Play: https://go.dev/play/p/gHbOPV_NUOJ +func NewEventBus[T any]() *EventBus[T] { + return &EventBus[T]{ + listeners: sync.Map{}, + } +} + +// Subscribe subscribes to an event with a specific event topic and listener function. +// Play: https://go.dev/play/p/EYGf_8cHei- +func (eb *EventBus[T]) Subscribe(topic string, listener func(eventData T), async bool, priority int, filter func(eventData T) bool) { + eb.mu.Lock() + defer eb.mu.Unlock() + + el := &EventListener[T]{ + priority: priority, + listener: listener, + async: async, + filter: filter, + } + + listenersInterface, _ := eb.listeners.LoadOrStore(topic, []*EventListener[T]{}) + listeners := listenersInterface.([]*EventListener[T]) + + listeners = append(listeners, el) + sort.Slice(listeners, func(i, j int) bool { + return listeners[i].priority > listeners[j].priority + }) + + eb.listeners.Store(topic, listeners) +} + +// Unsubscribe unsubscribes from an event with a specific event topic and listener function. +// Play: https://go.dev/play/p/Tmh7Ttfvprf +func (eb *EventBus[T]) Unsubscribe(topic string, listener func(eventData T)) { + eb.mu.Lock() + defer eb.mu.Unlock() + + listenersInterface, ok := eb.listeners.Load(topic) + if !ok { + return + } + + listeners := listenersInterface.([]*EventListener[T]) + listenerPtr := fmt.Sprintf("%p", listener) + + var updatedListeners []*EventListener[T] + for _, l := range listeners { + if fmt.Sprintf("%p", l.listener) != listenerPtr { + updatedListeners = append(updatedListeners, l) + } + } + + eb.listeners.Store(topic, updatedListeners) +} + +// Publish publishes an event with a specific event topic and data payload. +// Play: https://go.dev/play/p/gHTtVexFSH9 +func (eb *EventBus[T]) Publish(event Event[T]) { + eb.mu.RLock() + defer eb.mu.RUnlock() + + listenersInterface, exists := eb.listeners.Load(event.Topic) + if !exists { + return + } + + listeners := listenersInterface.([]*EventListener[T]) + + for _, listener := range listeners { + if listener.filter != nil && !listener.filter(event.Payload) { + continue + } + + if listener.async { + go eb.publishToListener(listener, event) + } else { + eb.publishToListener(listener, event) + } + } +} + +func (eb *EventBus[T]) publishToListener(listener *EventListener[T], event Event[T]) { + defer func() { + if r := recover(); r != nil && eb.errorHandler != nil { + eb.errorHandler(fmt.Errorf("%v", r)) + } + }() + + listener.listener(event.Payload) +} + +// SetErrorHandler sets the error handler function. +// Play: https://go.dev/play/p/gmB0gnFe5mc +func (eb *EventBus[T]) SetErrorHandler(handler func(err error)) { + eb.errorHandler = handler +} + +// ClearListeners clears all the listeners. +// Play: https://go.dev/play/p/KBfBYlKPgqD +func (eb *EventBus[T]) ClearListeners() { + eb.mu.Lock() + defer eb.mu.Unlock() + + eb.listeners = sync.Map{} +} + +// ClearListenersByTopic clears all the listeners by topic. +// Play: https://go.dev/play/p/gvMljmJOZmU +func (eb *EventBus[T]) ClearListenersByTopic(topic string) { + eb.mu.Lock() + defer eb.mu.Unlock() + + eb.listeners.Delete(topic) +} + +// GetListenersCount returns the number of listeners for a specific event topic. +// Play: https://go.dev/play/p/8VPJsMQgStM +func (eb *EventBus[T]) GetListenersCount(topic string) int { + eb.mu.RLock() + defer eb.mu.RUnlock() + + listenersInterface, ok := eb.listeners.Load(topic) + if !ok { + return 0 + } + + listeners := listenersInterface.([]*EventListener[T]) + return len(listeners) +} + +// GetAllListenersCount returns the total number of listeners. +// Play: https://go.dev/play/p/PUlr0xcpEOz +func (eb *EventBus[T]) GetAllListenersCount() int { + eb.mu.RLock() + defer eb.mu.RUnlock() + + count := 0 + eb.listeners.Range(func(key, value interface{}) bool { + count += len(value.([]*EventListener[T])) + return true + }) + + return count +} + +// GetEvents returns all the events topics. +// Play: https://go.dev/play/p/etgjjcOtAjX +func (eb *EventBus[T]) GetEvents() []string { + eb.mu.RLock() + defer eb.mu.RUnlock() + + var events []string + + eb.listeners.Range(func(key, value interface{}) bool { + events = append(events, key.(string)) + return true + }) + + return events +} diff --git a/eventbus/eventbus_example_test.go b/eventbus/eventbus_example_test.go new file mode 100644 index 00000000..cac6d8c2 --- /dev/null +++ b/eventbus/eventbus_example_test.go @@ -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(err error) { + fmt.Println(err) + }) + + eb.Subscribe("event1", func(eventData int) { + panic("error") + }, false, 0, nil) + + eb.Publish(Event[int]{Topic: "event1", Payload: 1}) + + // Output: + // error +} + +func ExampleEventBus_GetAllListenersCount() { + + eb := NewEventBus[int]() + + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event2", func(eventData int) {}, false, 0, nil) + + count := eb.GetAllListenersCount() + + fmt.Println(count) + + // Output: + // 2 +} + +func ExampleEventBus_GetEvents() { + eb := NewEventBus[int]() + + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event2", func(eventData int) {}, false, 0, nil) + + events := eb.GetEvents() + sort.Strings(events) + + for _, event := range events { + fmt.Println(event) + } + + // Output: + // event1 + // event2 +} diff --git a/eventbus/eventbus_test.go b/eventbus/eventbus_test.go new file mode 100644 index 00000000..dc90ca4f --- /dev/null +++ b/eventbus/eventbus_test.go @@ -0,0 +1,221 @@ +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(err error) { + assert.Equal("error", err.Error()) + }) + + eb.Subscribe("event1", func(eventData string) { + panic("error") + }, false, 0, nil) + + eb.Publish(Event[string]{Topic: "event1", Payload: "hello"}) +} + +func TestEventBus_ClearListeners(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEventBus_ClearListeners") + + eb := NewEventBus[int]() + + receivedData1 := 0 + receivedData2 := 0 + + eb.Subscribe("event1", func(eventData int) { + receivedData1 = eventData + }, false, 0, nil) + + eb.Subscribe("event2", func(eventData int) { + receivedData2 = eventData + }, false, 0, nil) + + eb.ClearListeners() + + eb.Publish(Event[int]{Topic: "event1", Payload: 1}) + eb.Publish(Event[int]{Topic: "event1", Payload: 2}) + + assert.Equal(0, receivedData1) + assert.Equal(0, receivedData2) +} + +func TestEventBus_ClearListenersByTopic(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEventBus_ClearListenersByTopic") + + eb := NewEventBus[int]() + + receivedData1 := 0 + receivedData2 := 0 + + eb.Subscribe("event1", func(eventData int) { + receivedData1 = eventData + }, false, 0, nil) + + eb.Subscribe("event2", func(eventData int) { + receivedData2 = eventData + }, false, 0, nil) + + eb.ClearListenersByTopic("event1") + + eb.Publish(Event[int]{Topic: "event1", Payload: 1}) + eb.Publish(Event[int]{Topic: "event2", Payload: 2}) + + assert.Equal(0, receivedData1) + assert.Equal(2, receivedData2) +} + +func TestEventBus_GetListenersCount(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEventBus_GetListenersCount") + + eb := NewEventBus[int]() + + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event2", func(eventData int) {}, false, 0, nil) + + assert.Equal(2, eb.GetListenersCount("event1")) + assert.Equal(1, eb.GetListenersCount("event2")) +} + +func TestEventBus_GetAllListenersCount(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEventBus_GetAllListenersCount") + + eb := NewEventBus[int]() + + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event2", func(eventData int) {}, false, 0, nil) + + assert.Equal(3, eb.GetAllListenersCount()) +} + +func TestEventBus_GetEvents(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEventBus_GetEvents") + + eb := NewEventBus[int]() + + eb.Subscribe("event1", func(eventData int) {}, false, 0, nil) + eb.Subscribe("event2", func(eventData int) {}, false, 0, nil) + + events := eb.GetEvents() + sort.Strings(events) + + assert.Equal(2, len(events)) + assert.Equal([]string{"event1", "event2"}, events) +} diff --git a/fileutil/file.go b/fileutil/file.go index 11812a4b..a050fa72 100644 --- a/fileutil/file.go +++ b/fileutil/file.go @@ -7,17 +7,85 @@ package fileutil import ( "archive/zip" "bufio" + "bytes" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/csv" "errors" + "fmt" "io" "io/fs" - "io/ioutil" "net/http" "os" "path/filepath" + "runtime" + "sort" "strings" + "sync" + + "github.com/duke-git/lancet/v2/validator" + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/transform" ) -// IsExist checks if a file or directory exists +// FileReader is a reader supporting offset seeking and reading one +// line at a time, this is especially useful for large files +type FileReader struct { + *bufio.Reader + file *os.File + offset int64 +} + +// NewFileReader creates the FileReader struct for reading +func NewFileReader(path string) (*FileReader, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + return &FileReader{ + file: f, + Reader: bufio.NewReader(f), + offset: 0, + }, nil +} + +// ReadLine reads and returns one line at a time excluding the trailing '\r' and '\n' +func (f *FileReader) ReadLine() (string, error) { + data, err := f.Reader.ReadBytes('\n') + f.offset += int64(len(data)) + if err == nil || err == io.EOF { + for len(data) > 0 && (data[len(data)-1] == '\r' || data[len(data)-1] == '\n') { + data = data[:len(data)-1] + } + return string(data), err + } + return "", err +} + +// Offset returns the current offset of the file +func (f *FileReader) Offset() int64 { + return f.offset +} + +// SeekOffset sets the current offset of the reading +func (f *FileReader) SeekOffset(offset int64) error { + _, err := f.file.Seek(offset, 0) + if err != nil { + return err + } + f.Reader = bufio.NewReader(f.file) + f.offset = offset + return nil +} + +// Close takes care of the opened file +func (f *FileReader) Close() error { + return f.file.Close() +} + +// IsExist checks if a file or directory exists. +// Play: https://go.dev/play/p/nKKXt8ZQbmh func IsExist(path string) bool { _, err := os.Stat(path) if err == nil { @@ -29,7 +97,8 @@ func IsExist(path string) bool { return false } -// CreateFile create a file in path +// CreateFile create a file in path. +// Play: https://go.dev/play/p/lDt8PEsTNKI func CreateFile(path string) bool { file, err := os.Create(path) if err != nil { @@ -40,7 +109,59 @@ func CreateFile(path string) bool { return true } -// IsDir checks if the path is directory or not +// CreateDir create directory in absolute path. param `absPath` like /a/, /a/b/. +// Play: https://go.dev/play/p/qUuCe1OGQnM +func CreateDir(absPath string) error { + // return os.MkdirAll(path.Dir(absPath), os.ModePerm) + return os.MkdirAll(absPath, os.ModePerm) +} + +// CopyDir copy src directory to dst directory, it will copy all files and directories recursively. +// the access permission will be the same as the source directory. +// if dstPath exists, it will return an error. +// Play: https://go.dev/play/p/YAyFTA_UuPb +func CopyDir(srcPath string, dstPath string) error { + srcInfo, err := os.Stat(srcPath) + if err != nil { + return fmt.Errorf("failed to get source directory info: %w", err) + } + + if !srcInfo.IsDir() { + return fmt.Errorf("source path is not a directory: %s", srcPath) + } + + err = os.MkdirAll(dstPath, 0755) + if err != nil { + return fmt.Errorf("failed to create destination directory: %w", err) + } + + entries, err := os.ReadDir(srcPath) + if err != nil { + return fmt.Errorf("failed to read source directory: %w", err) + } + + for _, entry := range entries { + srcDir := filepath.Join(srcPath, entry.Name()) + dstDir := filepath.Join(dstPath, entry.Name()) + + if entry.IsDir() { + err := CopyDir(srcDir, dstDir) + if err != nil { + return err + } + } else { + err := CopyFile(srcDir, dstDir) + if err != nil { + return err + } + } + } + + return nil +} + +// IsDir checks if the path is directory or not. +// Play: https://go.dev/play/p/WkVwEKqtOWk func IsDir(path string) bool { file, err := os.Stat(path) if err != nil { @@ -49,20 +170,66 @@ func IsDir(path string) bool { return file.IsDir() } -// RemoveFile remove the path file -func RemoveFile(path string) error { +// RemoveFile remove the path file. +// Play: https://go.dev/play/p/P2y0XW8a1SH +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) } -// CopyFile copy src file to dest file -func CopyFile(srcFilePath string, dstFilePath string) error { - srcFile, err := os.Open(srcFilePath) +// 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 { + srcFile, err := os.Open(srcPath) if err != nil { return err } defer srcFile.Close() - distFile, err := os.Create(dstFilePath) + distFile, err := os.Create(dstPath) if err != nil { return err } @@ -71,17 +238,21 @@ func CopyFile(srcFilePath string, dstFilePath string) error { var tmp = make([]byte, 1024*4) for { n, err := srcFile.Read(tmp) - distFile.Write(tmp[:n]) if err != nil { if err == io.EOF { return nil } return err } + _, err = distFile.Write(tmp[:n]) + if err != nil { + return err + } } } -//ClearFile write empty string to path file +// ClearFile write empty string to path file. +// Play: https://go.dev/play/p/NRZ0ZT-G94H func ClearFile(path string) error { f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) if err != nil { @@ -93,16 +264,18 @@ func ClearFile(path string) error { return err } -//ReadFileToString return string of file content +// ReadFileToString return string of file content. +// Play: https://go.dev/play/p/cmfwp_5SQTp func ReadFileToString(path string) (string, error) { - bytes, err := ioutil.ReadFile(path) + bytes, err := os.ReadFile(path) if err != nil { return "", err } return string(bytes), nil } -// ReadFileByLine read file line by line +// ReadFileByLine read file line by line. +// Play: https://go.dev/play/p/svJP_7ZrBrD func ReadFileByLine(path string) ([]string, error) { f, err := os.Open(path) if err != nil { @@ -110,7 +283,7 @@ func ReadFileByLine(path string) ([]string, error) { } defer f.Close() - res := make([]string, 0) + result := make([]string, 0) buf := bufio.NewReader(f) for { @@ -122,19 +295,20 @@ func ReadFileByLine(path string) ([]string, error) { if err != nil { continue } - res = append(res, l) + result = append(result, l) } - return res, nil + return result, nil } -// ListFileNames return all file names in the path +// ListFileNames return all file names in the path. +// Play: https://go.dev/play/p/Tjd7Y07rejl func ListFileNames(path string) ([]string, error) { if !IsExist(path) { return []string{}, nil } - fs, err := ioutil.ReadDir(path) + fs, err := os.ReadDir(path) if err != nil { return []string{}, err } @@ -144,18 +318,44 @@ func ListFileNames(path string) ([]string, error) { return []string{}, nil } - res := []string{} + result := []string{} for i := 0; i < sz; i++ { if !fs[i].IsDir() { - res = append(res, fs[i].Name()) + result = append(result, fs[i].Name()) } } - return res, nil + return result, nil } -// Zip create zip file, fpath could be a single file or a directory -func Zip(fpath string, destPath string) error { +// IsZipFile checks if file is zip or not. +// Play: https://go.dev/play/p/9M0g2j_uF_e +func IsZipFile(filepath string) bool { + f, err := os.Open(filepath) + if err != nil { + return false + } + defer f.Close() + + buf := make([]byte, 4) + if n, err := f.Read(buf); err != nil || n < 4 { + return false + } + + return bytes.Equal(buf, []byte("PK\x03\x04")) +} + +// Zip create zip file, fpath could be a single file or a directory. +// Play: https://go.dev/play/p/j-3sWBp8ik_P +func Zip(path string, destPath string) error { + if IsDir(path) { + return zipFolder(path, destPath) + } + + return zipFile(path, destPath) +} + +func zipFile(filePath string, destPath string) error { zipFile, err := os.Create(destPath) if err != nil { return err @@ -165,7 +365,33 @@ func Zip(fpath string, destPath string) error { archive := zip.NewWriter(zipFile) defer archive.Close() - filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { + return addFileToArchive1(filePath, archive) +} + +func zipFolder(folderPath string, destPath string) error { + outFile, err := os.Create(destPath) + if err != nil { + return err + } + defer outFile.Close() + + w := zip.NewWriter(outFile) + + err = addFileToArchive2(w, folderPath, "") + if err != nil { + return err + } + + err = w.Close() + if err != nil { + return err + } + + return nil +} + +func addFileToArchive1(fpath string, archive *zip.Writer) error { + err := filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -181,31 +407,59 @@ func Zip(fpath string, destPath string) error { header.Name += "/" } else { header.Method = zip.Deflate - } - - writer, err := archive.CreateHeader(header) - if err != nil { - return err - } - - if !info.IsDir() { + writer, err := archive.CreateHeader(header) + if err != nil { + return err + } file, err := os.Open(path) if err != nil { return err } defer file.Close() - _, err = io.Copy(writer, file) - if err != nil { + if _, err := io.Copy(writer, file); err != nil { return err } } return nil }) + return err +} + +func addFileToArchive2(w *zip.Writer, basePath, baseInZip string) error { + files, err := os.ReadDir(basePath) + if err != nil { + return err + } + if !strings.HasSuffix(basePath, "/") { + basePath = basePath + "/" + } + + for _, file := range files { + if !file.IsDir() { + dat, err := os.ReadFile(basePath + file.Name()) + if err != nil { + return err + } + + f, err := w.Create(baseInZip + file.Name()) + if err != nil { + return err + } + _, err = f.Write(dat) + if err != nil { + return err + } + } else if file.IsDir() { + newBase := basePath + file.Name() + "/" + addFileToArchive2(w, newBase, baseInZip+file.Name()+"/") + } + } return nil } -// UnZip unzip the file and save it to destPath +// UnZip unzip the file and save it to destPath. +// Play: https://go.dev/play/p/g0w34kS7B8m func UnZip(zipFile string, destPath string) error { zipReader, err := zip.OpenReader(zipFile) if err != nil { @@ -214,11 +468,27 @@ func UnZip(zipFile string, destPath string) error { defer zipReader.Close() for _, f := range zipReader.File { - path := filepath.Join(destPath, f.Name) + 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, decodeName) + if err != nil { + return err + } + if f.FileInfo().IsDir() { - os.MkdirAll(path, os.ModePerm) + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return err + } } else { - if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { + err = os.MkdirAll(filepath.Dir(path), os.ModePerm) + if err != nil { return err } @@ -240,10 +510,81 @@ func UnZip(zipFile string, destPath string) error { } } } + return nil } -// IsLink checks if a file is symbol link or not +// ZipAppendEntry append a single file or directory by fpath to an existing zip file. +// Play: https://go.dev/play/p/cxvaT8TRNQp +func ZipAppendEntry(fpath string, destPath string) error { + tempFile, err := os.CreateTemp("", "temp.zip") + if err != nil { + return err + } + defer os.Remove(tempFile.Name()) + + zipReader, err := zip.OpenReader(destPath) + if err != nil { + return err + } + + archive := zip.NewWriter(tempFile) + + for _, zipItem := range zipReader.File { + zipItemReader, err := zipItem.Open() + if err != nil { + return err + } + header, err := zip.FileInfoHeader(zipItem.FileInfo()) + if err != nil { + return err + } + header.Name = zipItem.Name + targetItem, err := archive.CreateHeader(header) + if err != nil { + return err + } + _, err = io.Copy(targetItem, zipItemReader) + if err != nil { + return err + } + } + + err = addFileToArchive1(fpath, archive) + + if err != nil { + return err + } + + err = zipReader.Close() + if err != nil { + return err + } + err = archive.Close() + if err != nil { + return err + } + err = tempFile.Close() + if err != nil { + return err + } + + return CopyFile(tempFile.Name(), destPath) +} + +func safeFilepathJoin(path1, path2 string) (string, error) { + relPath, err := filepath.Rel(".", path2) + if err != nil || strings.HasPrefix(relPath, "..") { + return "", fmt.Errorf("(zipslip) filepath is unsafe %q: %v", path2, err) + } + if path1 == "" { + path1 = "." + } + return filepath.Join(path1, filepath.Join("/", relPath)), nil +} + +// IsLink checks if a file is symbol link or not. +// Play: https://go.dev/play/p/TL-b-Kzvf44 func IsLink(path string) bool { fi, err := os.Lstat(path) if err != nil { @@ -252,7 +593,8 @@ func IsLink(path string) bool { return fi.Mode()&os.ModeSymlink != 0 } -// FileMode return file's mode and permission +// FileMode return file's mode and permission. +// Play: https://go.dev/play/p/2l2hI42fA3p func FileMode(path string) (fs.FileMode, error) { fi, err := os.Lstat(path) if err != nil { @@ -262,8 +604,9 @@ func FileMode(path string) (fs.FileMode, error) { } // MiMeType return file mime type -// param `file` should be string(file path) or *os.File -func MiMeType(file interface{}) string { +// param `file` should be string(file path) or *os.File. +// Play: https://go.dev/play/p/bd5sevSUZNu +func MiMeType(file any) string { var mediatype string readBuffer := func(f *os.File) ([]byte, error) { @@ -296,3 +639,365 @@ func MiMeType(file interface{}) string { } return mediatype } + +// CurrentPath return current absolute path. +// Play: https://go.dev/play/p/s74a9iBGcSw +func CurrentPath() string { + var absPath string + _, filename, _, ok := runtime.Caller(1) + if ok { + absPath = filepath.Dir(filename) + } + + return absPath +} + +// FileSize returns file size in bytes. +// Play: https://go.dev/play/p/H9Z05uD-Jjc +func FileSize(path string) (int64, error) { + f, err := os.Stat(path) + if err != nil { + return 0, err + } + return f.Size(), nil +} + +// DirSize walks the folder recursively and returns folder size in bytes. +func DirSize(path string) (int64, error) { + var size int64 + err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + info, err := d.Info() + if err != nil { + return err + } + size += info.Size() + } + return err + }) + return size, err +} + +// MTime returns file modified time. +// Play: https://go.dev/play/p/s_Tl7lZoAaY +func MTime(filepath string) (int64, error) { + f, err := os.Stat(filepath) + if err != nil { + return 0, err + } + return f.ModTime().Unix(), nil +} + +// Sha returns file sha value, param `shaType` should be 1, 256 or 512. +// Play: https://go.dev/play/p/VfEEcO2MJYf +func Sha(filepath string, shaType ...int) (string, error) { + file, err := os.Open(filepath) + if err != nil { + return "", err + } + defer file.Close() + + h := sha1.New() + if len(shaType) > 0 { + if shaType[0] == 1 { + h = sha1.New() + } else if shaType[0] == 256 { + h = sha256.New() + } else if shaType[0] == 512 { + h = sha512.New() + } else { + return "", errors.New("param `shaType` should be 1, 256 or 512") + } + } + + _, err = io.Copy(h, file) + + if err != nil { + return "", err + } + + sha := fmt.Sprintf("%x", h.Sum(nil)) + + return sha, nil + +} + +// ReadCsvFile read file content into slice. +// Play: https://go.dev/play/p/OExTkhGEd3_u +func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) { + f, err := os.Open(filepath) + if err != nil { + return nil, err + } + defer f.Close() + + reader := csv.NewReader(f) + if len(delimiter) > 0 { + reader.Comma = delimiter[0] + } + + records, err := reader.ReadAll() + if err != nil { + return nil, err + } + + return records, nil +} + +// WriteCsvFile write content to target csv file. +// append: append to existing csv file +// delimiter: specifies csv delimiter +// Play: https://go.dev/play/p/dAXm58Q5U1o +func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error { + flag := os.O_RDWR | os.O_CREATE + + if append { + flag = flag | os.O_APPEND + } + + f, err := os.OpenFile(filepath, flag, 0644) + if err != nil { + return err + } + + defer f.Close() + + writer := csv.NewWriter(f) + // 设置默认分隔符为逗号,除非另外指定 + if len(delimiter) > 0 { + writer.Comma = delimiter[0] + } else { + writer.Comma = ',' + } + + // 遍历所有记录并处理包含分隔符或双引号的单元格 + for i := range records { + for j := range records[i] { + records[i][j] = escapeCSVField(records[i][j], writer.Comma) + } + } + + return writer.WriteAll(records) +} + +// WriteStringToFile write string to target file. +// Play: https://go.dev/play/p/GhLS6d8lH_g +func WriteStringToFile(filepath string, content string, append bool) error { + var flag int + if append { + flag = os.O_RDWR | os.O_CREATE | os.O_APPEND + } else { + flag = os.O_RDWR | os.O_CREATE | os.O_TRUNC + } + + f, err := os.OpenFile(filepath, flag, 0644) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(content) + return err +} + +// WriteBytesToFile write bytes to target file. +// Play: https://go.dev/play/p/s7QlDxMj3P8 +func WriteBytesToFile(filepath string, content []byte) error { + f, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + + defer f.Close() + + _, err = f.Write(content) + return err +} + +// ReadFile get file reader by a url or a local file +// Play: https://go.dev/play/p/uNep3Tr8fqF +func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) { + switch { + case validator.IsUrl(path): + resp, err := http.Get(path) + if err != nil { + return nil, func() {}, err + } + return resp.Body, func() { resp.Body.Close() }, nil + case IsExist(path): + reader, err := os.Open(path) + if err != nil { + return nil, func() {}, err + } + return reader, func() { reader.Close() }, nil + default: + return nil, func() {}, errors.New("unknown file type") + } +} + +// escapeCSVField 处理单元格内容,如果包含分隔符,则用双引号包裹 +func escapeCSVField(field string, delimiter rune) string { + // 替换所有的双引号为两个双引号 + escapedField := strings.ReplaceAll(field, "\"", "\"\"") + + // 如果字段包含分隔符、双引号或换行符,用双引号包裹整个字段 + if strings.ContainsAny(escapedField, string(delimiter)+"\"\n") { + escapedField = fmt.Sprintf("\"%s\"", escapedField) + } + + return escapedField +} + +// WriteMapsToCsv write slice of map to csv file. +// Play: https://go.dev/play/p/umAIomZFV1c +// filepath: Path to the CSV file. +// records: Slice of maps to be written. the value of map should be basic type. +// the maps will be sorted by key in alphabeta order, then be written into csv file. +// appendToExistingFile: If true, data will be appended to the file if it exists. +// delimiter: Delimiter to use in the CSV file. +// headers: order of the csv column headers, needs to be consistent with the key of the map. +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, + headers ...[]string) error { + for _, record := range records { + for _, value := range record { + if !isCsvSupportedType(value) { + return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr") + } + } + } + + var columnHeaders []string + if len(headers) > 0 { + columnHeaders = headers[0] + } else { + columnHeaders = make([]string, 0, len(records[0])) + for key := range records[0] { + columnHeaders = append(columnHeaders, key) + } + // sort keys in alphabeta order + sort.Strings(columnHeaders) + } + + var datasToWrite [][]string + if !appendToExistingFile { + datasToWrite = append(datasToWrite, columnHeaders) + } + + for _, record := range records { + row := make([]string, 0, len(columnHeaders)) + for _, h := range columnHeaders { + row = append(row, fmt.Sprintf("%v", record[h])) + } + datasToWrite = append(datasToWrite, row) + } + + return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter) +} + +// check if the value of map which to be written into csv is basic type. +func isCsvSupportedType(v interface{}) bool { + switch v.(type) { + case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr: + return true + default: + return false + } +} + +// ChunkRead reads a block from the file at the specified offset and returns all lines within the block +// Play: https://go.dev/play/p/r0hPmKWhsgf +func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) { + buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小 + n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区 + if err != nil && err != io.EOF { + return nil, err + } + buf = buf[:n] // 调整切片以匹配实际读取的字节数 + + var lines []string + var lineStart int + for i, b := range buf { + if b == '\n' { + line := string(buf[lineStart:i]) // 不包括换行符 + lines = append(lines, line) + lineStart = i + 1 // 设置下一行的开始 + } + } + + if lineStart < len(buf) { // 处理块末尾的行 + line := string(buf[lineStart:]) + lines = append(lines, line) + } + bufPool.Put(buf) // 读取完成后,将缓冲区放回Pool + return lines, nil +} + +// ParallelChunkRead reads the file in parallel and send each chunk of lines to the specified channel. +// filePath 文件路径 +// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整 +// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数 +// linesCh用于接收返回结果的通道。 +// Play: https://go.dev/play/p/teMXnCsdSEw +func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error { + if chunkSizeMB == 0 { + chunkSizeMB = 100 + } + chunkSize := chunkSizeMB * 1024 * 1024 + // 内存复用 + bufPool := sync.Pool{ + New: func() interface{} { + return make([]byte, 0, chunkSize) + }, + } + + if maxGoroutine == 0 { + maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数 + } + + f, err := os.Open(filePath) + if err != nil { + return err + } + + defer f.Close() + + info, err := f.Stat() + if err != nil { + return err + } + + wg := sync.WaitGroup{} + chunkOffsetCh := make(chan int64, maxGoroutine) + + // 分配工作 + go func() { + for i := int64(0); i < info.Size(); i += int64(chunkSize) { + chunkOffsetCh <- i + } + close(chunkOffsetCh) + }() + + // 启动工作协程 + for i := 0; i < maxGoroutine; i++ { + wg.Add(1) + go func() { + for chunkOffset := range chunkOffsetCh { + chunk, err := ChunkRead(f, chunkOffset, chunkSize, &bufPool) + if err == nil { + linesCh <- chunk + } + + } + wg.Done() + }() + } + + // 等待所有解析完成后关闭行通道 + wg.Wait() + close(linesCh) + + return nil +} diff --git a/fileutil/file_example_test.go b/fileutil/file_example_test.go new file mode 100644 index 00000000..1d53a4c3 --- /dev/null +++ b/fileutil/file_example_test.go @@ -0,0 +1,508 @@ +package fileutil + +import ( + "fmt" + "io" + "log" + "os" + "runtime" + "sync" +) + +func ExampleIsExist() { + + result1 := IsExist("./") + result2 := IsExist("./xxx.go") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleCreateFile() { + fname := "./a.txt" + + result1 := IsExist(fname) + + CreateFile(fname) + + result2 := IsExist(fname) + + os.Remove(fname) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} + +func ExampleCreateDir() { + pwd, _ := os.Getwd() + dirPath := pwd + "/createdir/a/b" + + result1 := IsExist(dirPath) + + err := CreateDir(dirPath) + if err != nil { + return + } + + result2 := IsExist(pwd + "/createdir/") + result3 := IsExist(pwd + "/createdir/a") + result4 := IsExist(pwd + "/createdir/a/b") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + os.RemoveAll(pwd + "/createdir/") + + // Output: + // false + // true + // true + // true +} + +func ExampleIsDir() { + + result1 := IsDir("./") + result2 := IsDir("./xxx.go") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleRemoveFile() { + srcFile := "./text.txt" + CreateFile(srcFile) + + copyFile := "./text_copy.txt" + err := CopyFile(srcFile, copyFile) + if err != nil { + return + } + file, err := os.Open(copyFile) + if err != nil { + return + } + result1 := IsExist(copyFile) + result2 := file.Name() + + os.Remove(srcFile) + os.Remove(copyFile) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // ./text_copy.txt +} + +func ExampleReadFileToString() { + fname := "./test.txt" + CreateFile(fname) + + f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + _, err := f.WriteString("hello world") + if err != nil { + return + } + + content, _ := ReadFileToString(fname) + + os.Remove(fname) + + fmt.Println(content) + + // Output: + // hello world +} + +func ExampleClearFile() { + fname := "./test.txt" + CreateFile(fname) + + f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + _, err := f.WriteString("hello world") + if err != nil { + return + } + + content1, _ := ReadFileToString(fname) + + err = ClearFile(fname) + if err != nil { + return + } + content2, _ := ReadFileToString(fname) + + os.Remove(fname) + + fmt.Println(content1) + fmt.Println(content2) + + // Output: + // hello world + // +} + +func ExampleReadFileByLine() { + fname := "./test.txt" + CreateFile(fname) + + f, _ := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + _, err := f.WriteString("hello\nworld") + if err != nil { + return + } + + content, _ := ReadFileByLine(fname) + + os.Remove(fname) + + fmt.Println(content) + + // Output: + // [hello world] +} + +func ExampleListFileNames() { + fileList, _ := ListFileNames("../internal") + fmt.Println(fileList) + + // Output: + // [assert.go assert_test.go error_join.go] +} + +func ExampleMiMeType() { + fname := "./test.txt" + file, _ := os.Create(fname) + + _, err := file.WriteString("hello\nworld") + if err != nil { + return + } + + f, _ := os.Open(fname) + defer f.Close() + + mimeType := MiMeType(f) + fmt.Println(mimeType) + + os.Remove(fname) + + // Output: + // application/octet-stream +} + +func ExampleZip() { + srcFile := "./test.txt" + CreateFile(srcFile) + + zipFile := "./test.zip" + err := Zip(srcFile, zipFile) + if err != nil { + return + } + + result := IsExist(zipFile) + + os.Remove(srcFile) + os.Remove(zipFile) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleUnZip() { + zipFile := "./testdata/file.go.zip" + + err := UnZip(zipFile, "./testdata") + if err != nil { + return + } + + exist := IsExist("./testdata/file.go") + fmt.Println(exist) + + os.Remove("./testdata/file.go") + + // Output: + // true +} + +func ExampleZipAppendEntry() { + zipFile := "./test.zip" + CopyFile("./testdata/file.go.zip", zipFile) + + ZipAppendEntry("./testdata", zipFile) + + unZipPath := "./unzip" + UnZip(zipFile, unZipPath) + + fmt.Println(IsExist("./unzip/file.go")) + fmt.Println(IsExist("./unzip/testdata/file.go.zip")) + fmt.Println(IsExist("./unzip/testdata/test.txt")) + + os.Remove(zipFile) + os.RemoveAll(unZipPath) + + // Output: + // true + // true + // true +} + +func ExampleIsZipFile() { + result1 := IsZipFile("./file.go") + result2 := IsZipFile("./testdata/file.go.zip") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} + +func ExampleFileSize() { + size, err := FileSize("./testdata/test.txt") + + fmt.Println(size) + fmt.Println(err) + + // Output: + // 20 + // +} + +// func ExampleMTime() { +// mtime, err := MTime("./testdata/test.txt") + +// fmt.Println(mtime) // 1682478195 (unix timestamp) +// fmt.Println(err) + +// // Output: +// // 1682478195 +// // +// } + +func ExampleSha() { + sha1, err := Sha("./testdata/test.txt", 1) + sha256, _ := Sha("./testdata/test.txt", 256) + sha512, _ := Sha("./testdata/test.txt", 512) + + fmt.Println(sha1) + fmt.Println(sha256) + fmt.Println(sha512) + fmt.Println(err) + + // Output: + // dda3cf10c5a6ff6c6659a497bf7261b287af2bc7 + // aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35 + // d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870 + // +} + +func ExampleReadCsvFile() { + content, err := ReadCsvFile("./testdata/demo.csv") + + fmt.Println(content) + fmt.Println(err) + + // Output: + // [[Bob 12 male] [Duke 14 male] [Lucy 16 female]] + // +} + +func ExampleWriteCsvFile() { + data := [][]string{ + {"Lili", "22", "female"}, + {"Jim", "21", "male"}, + } + err := WriteCsvFile("./testdata/test2.csv", data, false) + fmt.Println(err) + + content, _ := ReadCsvFile("./testdata/test2.csv") + fmt.Println(content) + + // Output: + // + // [[Lili 22 female] [Jim 21 male]] +} + +func ExampleWriteMapsToCsv() { + csvFilePath := "./testdata/test3.csv" + records := []map[string]any{ + {"Name": "Lili", "Age": "22", "Gender": "female"}, + {"Name": "Jim", "Age": "21", "Gender": "male"}, + } + + headers := []string{"Name", "Age", "Gender"} + err := WriteMapsToCsv(csvFilePath, records, false, ';', headers) + + if err != nil { + log.Fatal(err) + } + + content, err := ReadCsvFile(csvFilePath, ';') + + fmt.Println(content) + + // Output: + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] +} + +func ExampleWriteStringToFile() { + filepath := "./test.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = WriteStringToFile(filepath, "hello", true) + if err != nil { + return + } + + content, err := ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} + +func ExampleWriteBytesToFile() { + filepath := "./bytes.txt" + + file, err := os.Create(filepath) + if err != nil { + return + } + + defer file.Close() + + err = WriteBytesToFile(filepath, []byte("hello")) + if err != nil { + return + } + + content, err := ReadFileToString(filepath) + if err != nil { + return + } + + os.Remove(filepath) + + fmt.Println(content) + + // Output: + // hello +} + +func ExampleReadFile() { + reader, fn, err := ReadFile("https://httpbin.org/robots.txt") + if err != nil { + return + } + defer fn() + + dat, err := io.ReadAll(reader) + if err != nil { + return + } + + fmt.Println(string(dat)) + + // Output: + // User-agent: * + // Disallow: /deny +} + +func ExampleChunkRead() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 + + filePath := "./testdata/test1.csv" + f, err := os.Open(filePath) + if err != nil { + return + } + + defer f.Close() + + var bufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, defaultChunkSizeMB*mb) + }, + } + + lines, err := ChunkRead(f, 0, 100, &bufPool) + if err != nil { + return + } + + fmt.Println(lines[0]) + fmt.Println(lines[1]) + + // Output: + // Lili,22,female + // Jim,21,male +} + +func ExampleParallelChunkRead() { + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 // 默认值 + + numParsers := runtime.NumCPU() + + linesCh := make(chan []string, numParsers) + filePath := "./testdata/test1.csv" + + go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers) + + var totalLines int + for lines := range linesCh { + totalLines += len(lines) + + for _, line := range lines { + fmt.Println(line) + } + } + + fmt.Println(totalLines) + + // Output: + // Lili,22,female + // Jim,21,male + // 2 +} diff --git a/fileutil/file_test.go b/fileutil/file_test.go index 6ee59566..13d658b8 100644 --- a/fileutil/file_test.go +++ b/fileutil/file_test.go @@ -1,13 +1,20 @@ package fileutil import ( + "io" "os" + "path/filepath" + "runtime" + "strings" + "sync" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestIsExist(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsExist") cases := []string{"./", "./file.go", "./a.txt"} @@ -20,20 +27,51 @@ func TestIsExist(t *testing.T) { } func TestCreateFile(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCreateFile") - f := "./text.txt" + f := "./testdata/text.txt" if CreateFile(f) { file, err := os.Open(f) assert.IsNil(err) assert.Equal(f, file.Name()) + + defer file.Close() } else { t.FailNow() } os.Remove(f) } +func TestCreateDir(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCreateDir") + + pwd, err := os.Getwd() + if err != nil { + t.Error(err) + t.FailNow() + } + + dirPath := pwd + "/a/b" + err = CreateDir(dirPath) + if err != nil { + t.Error(err) + t.FailNow() + } + + assert.Equal(true, IsExist(dirPath)) + + os.RemoveAll(pwd + "/a") + + assert.Equal(false, IsExist(dirPath)) +} + func TestIsDir(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsDir") cases := []string{"./", "./a.txt"} @@ -46,13 +84,38 @@ 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) { @@ -73,16 +136,6 @@ func TestCopyFile(t *testing.T) { os.Remove(destFile) } -func TestListFileNames(t *testing.T) { - assert := internal.NewAssert(t, "TestListFileNames") - - filesInPath, err := ListFileNames("../datetime/") - assert.IsNil(err) - - expected := []string{"datetime.go", "datetime_test.go"} - assert.Equal(expected, filesInPath) -} - func TestReadFileToString(t *testing.T) { assert := internal.NewAssert(t, "TestReadFileToString") @@ -90,7 +143,12 @@ func TestReadFileToString(t *testing.T) { CreateFile(path) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) - f.WriteString("hello world") + defer f.Close() + + _, err := f.WriteString("hello world") + if err != nil { + t.Error(err) + } content, _ := ReadFileToString(path) assert.Equal("hello world", content) @@ -107,9 +165,12 @@ func TestClearFile(t *testing.T) { f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) defer f.Close() - f.WriteString("hello world") + _, err := f.WriteString("hello world") + if err != nil { + t.Error(err) + } - err := ClearFile(path) + err = ClearFile(path) assert.IsNil(err) content, _ := ReadFileToString(path) @@ -125,8 +186,13 @@ func TestReadFileByLine(t *testing.T) { CreateFile(path) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() - f.WriteString("hello\nworld") + + _, err := f.WriteString("hello\nworld") + if err != nil { + t.Error(err) + } expected := []string{"hello", "world"} actual, _ := ReadFileByLine(path) @@ -143,10 +209,14 @@ func TestZipAndUnZip(t *testing.T) { file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777) defer file.Close() - file.WriteString("hello\nworld") + + _, err := file.WriteString("hello\nworld") + if err != nil { + t.Fail() + } zipFile := "./text.zip" - err := Zip(srcFile, zipFile) + err = Zip(srcFile, zipFile) assert.IsNil(err) unZipPath := "./unzip" @@ -161,6 +231,57 @@ func TestZipAndUnZip(t *testing.T) { os.RemoveAll(unZipPath) } +func TestZipAppendEntry(t *testing.T) { + assert := internal.NewAssert(t, "TestZipAppendEntry") + + zipFile := "./text.zip" + err := CopyFile("./testdata/file.go.zip", zipFile) + assert.IsNil(err) + + srcFile := "./text.txt" + CreateFile(srcFile) + + file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, os.ModePerm) + + _, err = file.WriteString("hello\nworld") + if err != nil { + t.Fail() + } + file.Close() + + err = ZipAppendEntry(srcFile, zipFile) + assert.IsNil(err) + + err = ZipAppendEntry("./testdata", zipFile) + assert.IsNil(err) + + unZipPath := "./unzip" + err = UnZip(zipFile, unZipPath) + assert.IsNil(err) + + assert.Equal(true, IsExist("./unzip/text.txt")) + assert.Equal(true, IsExist("./unzip/file.go")) + assert.Equal(true, IsExist("./unzip/testdata/file.go.zip")) + assert.Equal(true, IsExist("./unzip/testdata/test.txt")) + + os.Remove(srcFile) + os.Remove(zipFile) + os.RemoveAll(unZipPath) +} + +func TestZipFolder(t *testing.T) { + // assert := internal.NewAssert(t, "TestZipFolder") + + // toZipFolder := "./tempdir/a/b" + // zipFolder := "./tempdir/a/b.zip" + + // err := Zip(toZipFolder, zipFolder) + // assert.IsNil(err) + // assert.Equal(true, IsExist(zipFolder)) + + // os.Remove(zipFolder) +} + func TestFileMode(t *testing.T) { assert := internal.NewAssert(t, "TestFileMode") @@ -168,9 +289,9 @@ func TestFileMode(t *testing.T) { CreateFile(srcFile) mode, err := FileMode(srcFile) - assert.IsNil(err) - t.Log(mode) + assert.IsNotNil(mode) + assert.IsNil(err) os.Remove(srcFile) } @@ -197,6 +318,327 @@ func TestMiMeType(t *testing.T) { assert := internal.NewAssert(t, "TestMiMeType") f, _ := os.Open("./file.go") + defer f.Close() assert.Equal("text/plain; charset=utf-8", MiMeType(f)) assert.Equal("text/plain; charset=utf-8", MiMeType("./file.go")) } + +func TestListFileNames(t *testing.T) { + assert := internal.NewAssert(t, "TestListFileNames") + + filesInPath, err := ListFileNames("../internal") + assert.IsNil(err) + + expected := []string{"assert.go", "assert_test.go", "error_join.go"} + assert.Equal(expected, filesInPath) +} + +func TestCurrentPath(t *testing.T) { + absPath := CurrentPath() + t.Log(absPath) +} + +func TestIsZipFile(t *testing.T) { + assert := internal.NewAssert(t, "TestIsZipFile") + + assert.Equal(false, IsZipFile("./file.go")) + assert.Equal(true, IsZipFile("./testdata/file.go.zip")) +} + +func TestFileSize(t *testing.T) { + assert := internal.NewAssert(t, "TestFileSize") + + size, err := FileSize("./testdata/test.txt") + + assert.IsNil(err) + assert.Equal(int64(20), size) +} + +func TestMTime(t *testing.T) { + assert := internal.NewAssert(t, "TestMTime") + + mtime, err := MTime("./testdata/test.txt") + t.Log("TestMTime", mtime) + assert.IsNil(err) + // assert.Equal(int64(1682478195), mtime) +} + +func TestSha(t *testing.T) { + assert := internal.NewAssert(t, "TestSha") + + sha1, err := Sha("./testdata/test.txt", 1) + assert.IsNil(err) + + sha256, err := Sha("./testdata/test.txt", 256) + assert.IsNil(err) + + sha512, err := Sha("./testdata/test.txt", 512) + assert.IsNil(err) + + assert.Equal("dda3cf10c5a6ff6c6659a497bf7261b287af2bc7", sha1) + assert.Equal("aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35", sha256) + assert.Equal("d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870", sha512) +} + +func TestReadCsvFile(t *testing.T) { + assert := internal.NewAssert(t, "TestReadCsvFile") + + content, err := ReadCsvFile("./testdata/demo.csv") + + assert.IsNil(err) + + assert.Equal(3, len(content)) + assert.Equal(3, len(content[0])) + assert.Equal("Bob", content[0][0]) +} + +func TestWriteCsvFile(t *testing.T) { + assert := internal.NewAssert(t, "TestWriteCsvFile") + + csvFilePath := "./testdata/test1.csv" + content := [][]string{ + {"Lili", "22", "female"}, + {"Jim", "21", "male"}, + } + + err := WriteCsvFile(csvFilePath, content, false) + assert.IsNil(err) + + readContent, err := ReadCsvFile(csvFilePath) + + assert.IsNil(err) + + assert.Equal(2, len(readContent)) + assert.Equal(3, len(readContent[0])) + assert.Equal("Lili", readContent[0][0]) + + // RemoveFile(csvFilePath) +} + +func TestWriteMapsToCsv(t *testing.T) { + assert := internal.NewAssert(t, "TestWriteMapsToCSV") + + csvFilePath := "./testdata/test4.csv" + records := []map[string]any{ + {"Name": "Lili", "Age": "22", "Gender": "female"}, + {"Name": "Jim", "Age": "21", "Gender": "male"}, + } + + headers := []string{"Name", "Age", "Gender"} + err := WriteMapsToCsv(csvFilePath, records, false, ';', headers) + + assert.IsNil(err) + + content, err := ReadCsvFile(csvFilePath, ';') + + assert.IsNil(err) + + assert.Equal(3, len(content)) + assert.Equal(3, len(content[0])) + assert.Equal("Lili", content[1][0]) + assert.Equal("22", content[1][1]) + assert.Equal("female", content[1][2]) +} + +func TestWriteStringToFile(t *testing.T) { + assert := internal.NewAssert(t, "TestWriteStringToFile") + + filepath := "./test.txt" + + file, err := os.Create(filepath) + if err != nil { + t.Fail() + } + + err = WriteStringToFile(filepath, "hello world", false) + if err != nil { + t.Fail() + } + + content1, err := ReadFileToString(filepath) + if err != nil { + t.Fail() + } + + err = WriteStringToFile(filepath, "hello", false) + if err != nil { + t.Fail() + } + + content2, err := ReadFileToString(filepath) + if err != nil { + t.Fail() + } + + err = WriteStringToFile(filepath, " world", true) + if err != nil { + t.Fail() + } + + content3, err := os.ReadFile(filepath) + if err != nil { + t.Fail() + } + + assert.Equal("hello world", content1) + assert.Equal("hello", content2) + assert.Equal("hello world", string(content3)) + + _ = file.Close() + _ = os.Remove(filepath) +} + +func TestWriteBytesToFile(t *testing.T) { + assert := internal.NewAssert(t, "TestWriteBytesToFile") + + filepath := "./bytes.txt" + + file, err := os.Create(filepath) + if err != nil { + t.Fail() + } + + defer file.Close() + + err = WriteBytesToFile(filepath, []byte("hello")) + if err != nil { + t.Fail() + } + + content, err := os.ReadFile(filepath) + if err != nil { + t.Fail() + } + + assert.Equal("hello", string(content)) + + os.Remove(filepath) +} + +func TestReadFile(t *testing.T) { + reader, close, err := ReadFile("https://httpbin.org/robots.txt") + if err != nil { + t.Fail() + } + defer close() + + dat, err := io.ReadAll(reader) + if err != nil { + t.Fail() + } + + want := `User-agent: * +Disallow: /deny +` + internal.NewAssert(t, "TestReadFile").Equal(want, string(dat)) +} + +func TestReadlineFile(t *testing.T) { + path := "./testdata/demo.csv" + reader, err := NewFileReader(path) + if err != nil { + t.Fail() + } + defer reader.Close() + + indexMap := make(map[string]int64) + defer reader.Close() + for { + offset := reader.Offset() + line, err := reader.ReadLine() + if err == io.EOF { + break + } + indexMap[line] = offset + } + + lines, err := ReadFileByLine(path) + if err != nil { + t.Fail() + } + for _, line := range lines { + offset, ok := indexMap[line] + if !ok { + t.Fail() + } + if err = reader.SeekOffset(offset); err != nil { + t.Fail() + } + lineRead, err := reader.ReadLine() + if err == io.EOF { + break + } + internal.NewAssert(t, "TestReadlineFile").Equal(line, lineRead) + } +} + +func TestCopyDir(t *testing.T) { + assert := internal.NewAssert(t, "TestCopyDir") + + src := "./testdata" + dest := "./testdata_copy" + + err := CopyDir(src, dest) + assert.IsNil(err) + + assert.Equal(true, IsExist(dest)) + + filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + destPath := strings.Replace(path, src, dest, 1) + assert.Equal(true, IsExist(destPath)) + return nil + }) + + os.RemoveAll(dest) +} + +func TestParallelChunkRead(t *testing.T) { + assert := internal.NewAssert(t, "TestParallelChunkRead") + + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 // 默认值 + + numParsers := runtime.NumCPU() + + linesCh := make(chan []string, numParsers) + filePath := "./testdata/test1.csv" // 替换为你的文件路径 + + go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers) + + var totalLines int + for lines := range linesCh { + totalLines += len(lines) + + assert.Equal("Lili,22,female", lines[0]) + assert.Equal("Jim,21,male", lines[1]) + } + + assert.Equal(2, totalLines) +} + +func TestChunkRead(t *testing.T) { + assert := internal.NewAssert(t, "TestChunkRead") + + const mb = 1024 * 1024 + const defaultChunkSizeMB = 100 // 默认值 + + filePath := "./testdata/test1.csv" // 替换为你的文件路径 + f, err := os.Open(filePath) + if err != nil { + return + } + + defer f.Close() + + var bufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, defaultChunkSizeMB*mb) + }, + } + + lines, err := ChunkRead(f, 0, 100, &bufPool) + + assert.Equal("Lili,22,female", lines[0]) + assert.Equal("Jim,21,male", lines[1]) + +} diff --git a/fileutil/file_windows.go b/fileutil/file_windows.go new file mode 100644 index 00000000..cad0c989 --- /dev/null +++ b/fileutil/file_windows.go @@ -0,0 +1,83 @@ +//go:build windows + +package fileutil + +import ( + "fmt" + "syscall" + "unsafe" +) + +// tagVS_FIXEDFILEINFO 参考结构体https://learn.microsoft.com/zh-cn/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo +type tagVS_FIXEDFILEINFO struct { + Signature uint32 + StructVersion uint32 + FileVersionMS uint32 + FileVersionLS uint32 + ProductVersionMS uint32 + ProductVersionLS uint32 + FileFlagsMask uint32 + FileFlags uint32 + FileOS uint32 + FileType uint32 + FileSubtype uint32 + FileDateMS uint32 + FileDateLS uint32 +} + +// GetExeOrDllVersion get the version of exe or dll file on windows. +// Play: https://go.dev/play/p/iLRrDBhE38E +func GetExeOrDllVersion(filePath string) (string, error) { + // 加载系统dll + versionDLL := syscall.NewLazyDLL("version.dll") + getFileVersionInfoSize := versionDLL.NewProc("GetFileVersionInfoSizeW") + getFileVersionInfo := versionDLL.NewProc("GetFileVersionInfoW") + verQueryValue := versionDLL.NewProc("VerQueryValueW") + + // 转换路径为UTF-16 + filePathPtr, err := syscall.UTF16PtrFromString(filePath) + if err != nil { + return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err) + } + + // 获取version信息大小 + size, _, err := getFileVersionInfoSize.Call( + uintptr(unsafe.Pointer(filePathPtr)), + 0, + ) + if size == 0 { + return "", fmt.Errorf("unable to obtain version information size: %v", err) + } + + // 加载version信息 + data := make([]byte, size) + ret, _, err := getFileVersionInfo.Call(uintptr(unsafe.Pointer(filePathPtr)), 0, size, uintptr(unsafe.Pointer(&data[0]))) + if ret == 0 { + return "", fmt.Errorf("unable to obtain version information: %v", err) + } + + // 查询version信息 + var fixedInfo *tagVS_FIXEDFILEINFO + var fixedInfoLen uint32 + u16, err := syscall.UTF16PtrFromString(`\`) + if err != nil { + return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err) + } + ret, _, err = verQueryValue.Call( + uintptr(unsafe.Pointer(&data[0])), + uintptr(unsafe.Pointer(u16)), + uintptr(unsafe.Pointer(&fixedInfo)), + uintptr(unsafe.Pointer(&fixedInfoLen)), + ) + if ret == 0 { + return "", fmt.Errorf("unable to query version information: %v", err) + } + + // 转换结构体 + major := fixedInfo.FileVersionMS >> 16 + minor := fixedInfo.FileVersionMS & 0xFFFF + build := fixedInfo.FileVersionLS >> 16 + revision := fixedInfo.FileVersionLS & 0xFFFF + + return fmt.Sprintf("%d.%d.%d.%d", major, minor, build, revision), nil +} diff --git a/fileutil/file_windows_test.go b/fileutil/file_windows_test.go new file mode 100644 index 00000000..244eeb2d --- /dev/null +++ b/fileutil/file_windows_test.go @@ -0,0 +1,13 @@ +//go:build windows + +package fileutil + +import "testing" + +func TestGetExeOrDllVersion(t *testing.T) { + v, err := GetExeOrDllVersion(`C:\Windows\System32\cmd.exe`) + if err != nil { + t.Error(err) + } + t.Log(v) +} diff --git a/fileutil/testdata/demo.csv b/fileutil/testdata/demo.csv new file mode 100644 index 00000000..8523fab8 --- /dev/null +++ b/fileutil/testdata/demo.csv @@ -0,0 +1,3 @@ +Bob, 12, male +Duke, 14, male +Lucy, 16, female diff --git a/fileutil/testdata/file.go.zip b/fileutil/testdata/file.go.zip new file mode 100644 index 00000000..2383a1ee Binary files /dev/null and b/fileutil/testdata/file.go.zip differ diff --git a/fileutil/testdata/test.txt b/fileutil/testdata/test.txt new file mode 100644 index 00000000..c1cac2d8 --- /dev/null +++ b/fileutil/testdata/test.txt @@ -0,0 +1 @@ +this is a test file. \ No newline at end of file diff --git a/fileutil/testdata/test01/demo2.csv b/fileutil/testdata/test01/demo2.csv new file mode 100644 index 00000000..28defaf2 --- /dev/null +++ b/fileutil/testdata/test01/demo2.csv @@ -0,0 +1 @@ +makj1 \ No newline at end of file diff --git a/fileutil/testdata/test1.csv b/fileutil/testdata/test1.csv new file mode 100644 index 00000000..c8961e72 --- /dev/null +++ b/fileutil/testdata/test1.csv @@ -0,0 +1,2 @@ +Lili,22,female +Jim,21,male diff --git a/fileutil/testdata/test2.csv b/fileutil/testdata/test2.csv new file mode 100644 index 00000000..6bbd640e --- /dev/null +++ b/fileutil/testdata/test2.csv @@ -0,0 +1,5 @@ +Lili,22,female +Jim,21,male + + + diff --git a/fileutil/testdata/test3.csv b/fileutil/testdata/test3.csv new file mode 100644 index 00000000..d5e2f124 --- /dev/null +++ b/fileutil/testdata/test3.csv @@ -0,0 +1,3 @@ +Name;Age;Gender +Lili;22;female +Jim;21;male diff --git a/fileutil/testdata/test4.csv b/fileutil/testdata/test4.csv new file mode 100644 index 00000000..d5e2f124 --- /dev/null +++ b/fileutil/testdata/test4.csv @@ -0,0 +1,3 @@ +Name;Age;Gender +Lili;22;female +Jim;21;male diff --git a/formatter/byte.go b/formatter/byte.go new file mode 100644 index 00000000..2f02ccb8 --- /dev/null +++ b/formatter/byte.go @@ -0,0 +1,206 @@ +package formatter + +import ( + "fmt" + "math" + "strconv" + "strings" + "unicode" + + "github.com/duke-git/lancet/v2/mathutil" + "github.com/duke-git/lancet/v2/strutil" +) + +// +// code logic come from: +// https://github.com/docker/go-units/blob/master/size.go + +// http://en.wikipedia.org/wiki/Binary_prefix +const ( + // Decimal + unitB = 1 + unitKB = 1000 + unitMB = 1000 * unitKB + unitGB = 1000 * unitMB + unitTB = 1000 * unitGB + unitPB = 1000 * unitTB + unitEB = 1000 * unitPB + + // Binary + unitBiB = 1 + unitKiB = 1024 + unitMiB = 1024 * unitKiB + unitGiB = 1024 * unitMiB + unitTiB = 1024 * unitGiB + unitPiB = 1024 * unitTiB + unitEiB = 1024 * unitPiB +) + +// type byteUnitMap map[byte]int64 + +var ( + decimalByteMap = map[string]uint64{ + "b": unitB, + "kb": unitKB, + "mb": unitMB, + "gb": unitGB, + "tb": unitTB, + "pb": unitPB, + "eb": unitEB, + + // Without suffix + "": unitB, + "k": unitKB, + "m": unitMB, + "g": unitGB, + "t": unitTB, + "p": unitPB, + "e": unitEB, + } + + binaryByteMap = map[string]uint64{ + "bi": unitBiB, + "kib": unitKiB, + "mib": unitMiB, + "gib": unitGiB, + "tib": unitTiB, + "pib": unitPiB, + "eib": unitEiB, + + // Without suffix + "": unitBiB, + "ki": unitKiB, + "mi": unitMiB, + "gi": unitGiB, + "ti": unitTiB, + "pi": unitPiB, + "ei": unitEiB, + } +) + +var ( + decimalByteUnits = []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + binaryByteUnits = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} +) + +// DecimalBytes returns a human readable byte size under decimal standard (base 1000) +// The precision parameter specifies the number of digits after the decimal point, which defaults to 4. +// Play: https://go.dev/play/p/FPXs1suwRcs +func DecimalBytes(size float64, precision ...int) string { + pointPosition := 4 + if len(precision) > 0 { + pointPosition = precision[0] + } + + size, unit := calculateByteSize(size, 1000.0, decimalByteUnits) + + return roundToToString(size, pointPosition) + unit +} + +// BinaryBytes returns a human-readable byte size under binary standard (base 1024) +// The precision parameter specifies the number of digits after the decimal point, which defaults to 4. +// Play: https://go.dev/play/p/G9oHHMCAZxP +func BinaryBytes(size float64, precision ...int) string { + pointPosition := 4 + if len(precision) > 0 { + pointPosition = precision[0] + } + + size, unit := calculateByteSize(size, 1024.0, binaryByteUnits) + + return roundToToString(size, pointPosition) + unit +} + +func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) { + i := 0 + unitsLimit := len(byteUnits) - 1 + for size >= base && i < unitsLimit { + size = size / base + i++ + } + return size, byteUnits[i] +} + +func roundToToString(x float64, max ...int) string { + pointPosition := 4 + if len(max) > 0 { + pointPosition = max[0] + } + result := mathutil.RoundToString(x, pointPosition) + + // 删除小数位结尾的0 + decimal := strings.TrimRight(strutil.After(result, "."), "0") + if decimal == "" || pointPosition == 0 { + // 没有小数位直接返回整数 + return strutil.Before(result, ".") + } + + // 小数位大于想要设置的位数,按需要截断 + if len(decimal) > pointPosition { + return strutil.Before(result, ".") + "." + decimal[:pointPosition] + } + + // 小数位小于等于想要的位数,直接拼接返回 + return strutil.Before(result, ".") + "." + decimal +} + +// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000). +// ParseDecimalBytes("42 MB") -> 42000000, nil +// Play: https://go.dev/play/p/Am98ybWjvjj +func ParseDecimalBytes(size string) (uint64, error) { + return parseBytes(size, "decimal") +} + +// ParseBinaryBytes return the human readable bytes size string into the amount it represents(base 1024). +// ParseBinaryBytes("42 mib") -> 44040192, nil +// Play: https://go.dev/play/p/69v1tTT62x8 +func ParseBinaryBytes(size string) (uint64, error) { + return parseBytes(size, "binary") +} + +// see https://github.com/dustin/go-humanize/blob/master/bytes.go +func parseBytes(s string, kind string) (uint64, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + f, err := strconv.ParseFloat(num, 64) + if err != nil { + return 0, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + + if kind == "decimal" { + if m, ok := decimalByteMap[extra]; ok { + f *= float64(m) + if f >= math.MaxUint64 { + return 0, fmt.Errorf("too large: %v", s) + } + return uint64(f), nil + } + } else { + if m, ok := binaryByteMap[extra]; ok { + f *= float64(m) + if f >= math.MaxUint64 { + return 0, fmt.Errorf("too large: %v", s) + } + return uint64(f), nil + } + } + + return 0, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/formatter/byte_test.go b/formatter/byte_test.go new file mode 100644 index 00000000..897dec29 --- /dev/null +++ b/formatter/byte_test.go @@ -0,0 +1,107 @@ +package formatter + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestDecimalBytes(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDecimalBytes") + + assert.Equal("1KB", DecimalBytes(1000)) + assert.Equal("1.024KB", DecimalBytes(1024)) + assert.Equal("1.2346MB", DecimalBytes(1234567)) + assert.Equal("1.235MB", DecimalBytes(1234567, 3)) + assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB))) + assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB))) + assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB))) + assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB))) + assert.Equal("1EB", DecimalBytes(float64(1000*unitPB))) + assert.Equal("62MB", DecimalBytes(61812496, 0)) + assert.Equal("61.8MB", DecimalBytes(61812496, 1)) + assert.Equal("401MB", DecimalBytes(401000000)) + assert.Equal("401MB", DecimalBytes(401000000, 0)) + assert.Equal("4MB", DecimalBytes(4010000, 0)) + + assert.Equal("4MB", DecimalBytes(4000000, 0)) + assert.Equal("4MB", DecimalBytes(4000000, 1)) +} + +func TestBinaryBytes(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBinaryBytes") + + assert.Equal("1KiB", BinaryBytes(1024)) + assert.Equal("1MiB", BinaryBytes(1024*1024)) + assert.Equal("1.1774MiB", BinaryBytes(1234567)) + assert.Equal("1.18MiB", BinaryBytes(1234567, 2)) + + assert.Equal("10KiB", BinaryBytes(10240, 0)) + assert.Equal("10KiB", BinaryBytes(10240, 1)) + assert.Equal("10KiB", BinaryBytes(10240, 2)) +} + +func TestParseDecimalBytes(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestParseDecimalBytes") + + cases := map[string]uint64{ + "12": uint64(12), + "12 k": uint64(12000), + "12 kb": uint64(12000), + "12kb": uint64(12000), + "12k": uint64(12000), + "12K": uint64(12000), + "12KB": uint64(12000), + "12 K": uint64(12000), + "12 KB": uint64(12000), + "12 Kb": uint64(12000), + "12 kB": uint64(12000), + "12.2 KB": uint64(12200), + } + + for k, v := range cases { + result, err := ParseDecimalBytes(k) + assert.Equal(v, result) + assert.IsNil(err) + } + + _, err := ParseDecimalBytes("12 AB") + assert.IsNotNil(err) +} + +func TestParseBinaryBytes(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestParseBinaryBytes") + + cases := map[string]uint64{ + "12": uint64(12), + "12 ki": uint64(12288), + "12 kib": uint64(12288), + "12kib": uint64(12288), + "12ki": uint64(12288), + "12KI": uint64(12288), + "12KIB": uint64(12288), + "12KiB": uint64(12288), + "12 Ki": uint64(12288), + "12 KiB": uint64(12288), + "12 Kib": uint64(12288), + "12 kiB": uint64(12288), + "12.2 KiB": uint64(12492), + } + + for k, v := range cases { + result, err := ParseBinaryBytes(k) + assert.Equal(v, result) + assert.IsNil(err) + } + + _, err := ParseDecimalBytes("12 AB") + assert.IsNotNil(err) +} diff --git a/formatter/formatter.go b/formatter/formatter.go index 9d5687bb..9b086553 100644 --- a/formatter/formatter.go +++ b/formatter/formatter.go @@ -4,14 +4,66 @@ // Package formatter implements some functions to format string, struct. package formatter -import "strings" - -// Comma add comma to number by every 3 numbers from right. ahead by symbol char -func Comma(v interface{}, symbol string) string { - s := numString(v) - dotIndex := strings.Index(s, ".") - if dotIndex != -1 { - return symbol + commaString(s[:dotIndex]) + s[dotIndex:] +import ( + "encoding/json" + "io" + "strconv" + "strings" + + "github.com/duke-git/lancet/v2/convertor" + "golang.org/x/exp/constraints" +) + +// Comma add comma to a number value by every 3 numbers from right. ahead by prefix symbol char. +// if value is invalid number string eg "aa", return empty string +// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345" +// Play: https://go.dev/play/p/eRD5k2vzUVX +func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string { + numString := convertor.ToString(value) + + _, err := strconv.ParseFloat(numString, 64) + if err != nil { + return "" + } + + isNegative := strings.HasPrefix(numString, "-") + if isNegative { + numString = numString[1:] } - return symbol + commaString(s) + + index := strings.Index(numString, ".") + if index == -1 { + index = len(numString) + } + + for index > 3 { + index -= 3 + numString = numString[:index] + "," + numString[index:] + } + + if isNegative { + numString = "-" + numString + } + + return prefixSymbol + numString +} + +// Pretty data to JSON string. +// Play: https://go.dev/play/p/YsciGj3FH2x +func Pretty(v any) (string, error) { + out, err := json.MarshalIndent(v, "", " ") + return string(out), err +} + +// PrettyToWriter pretty encode data to writer. +// Play: https://go.dev/play/p/LPLZ3lDi5ma +func PrettyToWriter(v any, out io.Writer) error { + enc := json.NewEncoder(out) + enc.SetIndent("", " ") + + if err := enc.Encode(v); err != nil { + return err + } + + return nil } diff --git a/formatter/formatter_example_test.go b/formatter/formatter_example_test.go new file mode 100644 index 00000000..5f18e4e5 --- /dev/null +++ b/formatter/formatter_example_test.go @@ -0,0 +1,133 @@ +package formatter + +import ( + "bytes" + "fmt" +) + +func ExampleComma() { + result1 := Comma("123", "") + result2 := Comma("12345", "$") + result3 := Comma(1234567, "¥") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 123 + // $12,345 + // ¥1,234,567 +} + +func ExamplePretty() { + result1, _ := Pretty([]string{"a", "b", "c"}) + result2, _ := Pretty(map[string]int{"a": 1}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [ + // "a", + // "b", + // "c" + // ] + // { + // "a": 1 + // } +} + +func ExamplePrettyToWriter() { + type User struct { + Name string `json:"name"` + Aage uint `json:"age"` + } + user := User{Name: "King", Aage: 10000} + + buf := &bytes.Buffer{} + err := PrettyToWriter(user, buf) + + fmt.Println(buf) + fmt.Println(err) + + // Output: + // { + // "name": "King", + // "age": 10000 + // } + // + // +} + +func ExampleDecimalBytes() { + result1 := DecimalBytes(1000) + result2 := DecimalBytes(1024) + result3 := DecimalBytes(1234567) + result4 := DecimalBytes(1234567, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KB + // 1.024KB + // 1.2346MB + // 1.235MB +} + +func ExampleBinaryBytes() { + result1 := BinaryBytes(1024) + result2 := BinaryBytes(1024 * 1024) + result3 := BinaryBytes(1234567) + result4 := BinaryBytes(1234567, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 1KiB + // 1MiB + // 1.1774MiB + // 1.18MiB +} + +func ExampleParseDecimalBytes() { + result1, _ := ParseDecimalBytes("12") + result2, _ := ParseDecimalBytes("12k") + result3, _ := ParseDecimalBytes("12 Kb") + result4, _ := ParseDecimalBytes("12.2 kb") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12000 + // 12000 + // 12200 +} + +func ExampleParseBinaryBytes() { + result1, _ := ParseBinaryBytes("12") + result2, _ := ParseBinaryBytes("12ki") + result3, _ := ParseBinaryBytes("12 KiB") + result4, _ := ParseBinaryBytes("12.2 kib") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 12 + // 12288 + // 12288 + // 12492 +} diff --git a/formatter/formatter_internal.go b/formatter/formatter_internal.go deleted file mode 100644 index 4928147f..00000000 --- a/formatter/formatter_internal.go +++ /dev/null @@ -1,40 +0,0 @@ -package formatter - -import ( - "fmt" - "reflect" - "strconv" - "strings" -) - -func commaString(s string) string { - if len(s) <= 3 { - return s - } - return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:]) -} - -func numString(value interface{}) string { - switch reflect.TypeOf(value).Kind() { - case reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64: - return fmt.Sprintf("%v", value) - case reflect.String: - { - sv := fmt.Sprintf("%v", value) - if strings.Contains(sv, ".") { - _, err := strconv.ParseFloat(sv, 64) - if err == nil { - return sv - } - } else { - _, err := strconv.ParseInt(sv, 10, 64) - if err == nil { - return sv - } - } - } - default: - return "" - } - return "" -} diff --git a/formatter/formatter_test.go b/formatter/formatter_test.go index d2d4f87f..7620934f 100644 --- a/formatter/formatter_test.go +++ b/formatter/formatter_test.go @@ -1,23 +1,88 @@ package formatter import ( + "bytes" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestComma(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestComma") assert.Equal("", Comma("", "")) assert.Equal("", Comma("aa", "")) assert.Equal("", Comma("aa.a", "")) - assert.Equal("", Comma([]int{1}, "")) assert.Equal("123", Comma("123", "")) assert.Equal("12,345", Comma("12345", "")) + assert.Equal("12,345.6789", Comma("12345.6789", "")) + assert.Equal("123,456,789,000", Comma("123456789000", "")) + assert.Equal("12,345,678.9", Comma("12345678.9", "")) assert.Equal("12,345", Comma(12345, "")) assert.Equal("$12,345", Comma(12345, "$")) assert.Equal("¥12,345", Comma(12345, "¥")) assert.Equal("12,345.6789", Comma(12345.6789, "")) + assert.Equal("12,345.6789", Comma(+12345.6789, "")) + assert.Equal("12,345,678.9", Comma(12345678.9, "")) + assert.Equal("123,456,789,000", Comma(123456789000, "")) + + assert.Equal("-999", Comma(-999, "")) + assert.Equal("-1,000", Comma(-1000, "")) + assert.Equal("-1,234,567", Comma(-1234567, "")) +} + +func TestPretty(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPretty") + + cases := []any{ + "", + "abc", + 123, + []string{"a", "b", "c"}, + map[string]int{"a": 1}, + struct { + Abc int `json:"abc"` + }{Abc: 123}, + } + + expects := []string{ + "\"\"", + `"abc"`, + "123", + "[\n \"a\",\n \"b\",\n \"c\"\n]", + "{\n \"a\": 1\n}", + "{\n \"abc\": 123\n}", + } + + for i, v := range cases { + result, err := Pretty(v) + + assert.IsNil(err) + assert.Equal(expects[i], result) + } +} + +func TestPrettyToWriter(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPrettyToWriter") + + type User struct { + Name string `json:"name"` + Aage uint `json:"age"` + } + user := User{Name: "King", Aage: 10000} + + expects := "{\n \"name\": \"King\",\n \"age\": 10000\n}\n" + + buf := &bytes.Buffer{} + err := PrettyToWriter(user, buf) + + assert.IsNil(err) + assert.Equal(expects, buf.String()) } diff --git a/function/function.go b/function/function.go index 6e2cddba..e063d6df 100644 --- a/function/function.go +++ b/function/function.go @@ -5,16 +5,19 @@ package function import ( + "fmt" "reflect" + "sync" "time" ) -// After creates a function that invokes func once it's called n or more times -func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { +// After creates a function that invokes func once it's called n or more times. +// Play: https://go.dev/play/p/8mQhkFmsgqs +func After(n int, fn any) func(args ...any) []reflect.Value { // Catch programming error while constructing the closure mustBeFunction(fn) - return func(args ...interface{}) []reflect.Value { + return func(args ...any) []reflect.Value { n-- if n < 1 { return unsafeInvokeFunc(fn, args...) @@ -23,88 +26,156 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { } } -// Before creates a function that invokes func once it's called less than n times -func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value { +// Before creates a function that invokes func once it's called less than n times. +// Play: https://go.dev/play/p/0HqUDIFZ3IL +func Before(n int, fn any) func(args ...any) []reflect.Value { // Catch programming error while constructing the closure mustBeFunction(fn) - var res []reflect.Value - return func(args ...interface{}) []reflect.Value { + var result []reflect.Value + return func(args ...any) []reflect.Value { if n > 0 { - res = unsafeInvokeFunc(fn, args...) + result = unsafeInvokeFunc(fn, args...) } if n <= 0 { fn = nil } n-- - return res + return result } } -// Fn is for curry function which is func(...interface{}) interface{} -type Fn func(...interface{}) interface{} +// CurryFn is for make curry function +type CurryFn[T any] func(...T) T -// Curry make a curry function -func (f Fn) Curry(i interface{}) func(...interface{}) interface{} { - return func(values ...interface{}) interface{} { - v := append([]interface{}{i}, values...) - return f(v...) +// New make a curry function for specific value. +// Play: https://go.dev/play/p/5HopfDwANKX +func (cf CurryFn[T]) New(val T) func(...T) T { + return func(vals ...T) T { + args := append([]T{val}, vals...) + return cf(args...) } } -// Compose compose the functions from right to left -func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} { - return func(s ...interface{}) interface{} { - f := fnList[0] - restFn := fnList[1:] +// Compose compose the functions from right to left. +// Play: https://go.dev/play/p/KKfugD4PKYF +func Compose[T any](fnList ...func(...T) T) func(...T) T { + return func(args ...T) T { + firstFn := fnList[0] + restFns := fnList[1:] if len(fnList) == 1 { - return f(s...) + return firstFn(args...) } - return f(Compose(restFn...)(s...)) + fn := Compose(restFns...) + arg := fn(args...) + + return firstFn(arg) } } -// Delay make the function execution after delayed time -func Delay(delay time.Duration, fn interface{}, args ...interface{}) { +// Delay make the function execution after delayed time. +// Play: https://go.dev/play/p/Ivtc2ZE-Tye +func Delay(delay time.Duration, fn any, args ...any) { // Catch programming error while constructing the closure mustBeFunction(fn) time.Sleep(delay) - invokeFunc(fn, args...) + unsafeInvokeFunc(fn, args...) } // Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked. -func Debounced(fn func(), duration time.Duration) func() { - // Catch programming error while constructing the closure - mustBeFunction(fn) +// Deprecated: Use Debounce function instead. +// Play: https://go.dev/play/p/absuEGB_GN7 +func Debounced(fn func(), delay time.Duration) func() { + debouncedFn, _ := Debounce(fn, delay) + return debouncedFn +} - timer := time.NewTimer(duration) - timer.Stop() +// Debounce creates a debounced version of the provided function. +// Play: https://go.dev/play/p/-dGFrYn_1Zi +func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) { + var ( + timer *time.Timer + mu sync.Mutex + ) - go func() { - for { - select { - case <-timer.C: - go fn() - } + debouncedFn = func() { + mu.Lock() + defer mu.Unlock() + + if timer != nil { + timer.Stop() } - }() - return func() { timer.Reset(duration) } + timer = time.AfterFunc(delay, func() { + fn() + }) + } + + cancelFn = func() { + mu.Lock() + defer mu.Unlock() + + if timer != nil { + timer.Stop() + } + } + + return debouncedFn, cancelFn } -// Schedule invoke function every duration time, util close the returned bool chan -func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { +// Throttle creates a throttled version of the provided function. +// The returned function guarantees that it will only be invoked at most once per interval. +// Play: https://go.dev/play/p/HpoMov-tJSN +func Throttle(fn func(), interval time.Duration) func() { + var ( + timer *time.Timer + lastRun time.Time + mu sync.Mutex + ) + + return func() { + mu.Lock() + defer mu.Unlock() + + now := time.Now() + if now.Sub(lastRun) >= interval { + fn() + lastRun = now + if timer != nil { + timer.Stop() + timer = nil + } + } else if timer == nil { + delay := interval - now.Sub(lastRun) + + timer = time.AfterFunc(delay, func() { + mu.Lock() + defer mu.Unlock() + + fn() + lastRun = time.Now() + timer = nil + }) + } + } +} + +// Schedule invoke function every duration time, util close the returned bool channel. +// Play: https://go.dev/play/p/hbON-Xeyn5N +func Schedule(duration time.Duration, fn any, args ...any) chan bool { // Catch programming error while constructing the closure mustBeFunction(fn) quit := make(chan bool) + go func() { for { unsafeInvokeFunc(fn, args...) + select { - case <-time.After(d): + case <-time.After(duration): case <-quit: return } @@ -113,3 +184,54 @@ func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { return quit } + +// Pipeline takes a list of functions and returns a function whose param will be passed into +// the functions one by one. +// Play: https://go.dev/play/p/mPdUVvj6HD6 +func Pipeline[T any](funcs ...func(T) T) func(T) T { + return func(arg T) (result T) { + result = arg + for _, fn := range funcs { + result = fn(result) + } + return + } +} + +// AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. +// A predicate function that takes an argument of type T and returns a bool. +// An apply function that also takes an argument of type T and returns a modified value of the same type. +// Play: https://go.dev/play/p/XlXHHtzCf7d +func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) { + if predicate == nil { + panic("programming error: predicate must be not nil") + } + + if apply == nil { + panic("programming error: apply must be not nil") + } + + return func(t T) (T, bool) { + if !predicate(t) { + var defaultValue T + return defaultValue, false + } + return apply(t), true + } +} + +func unsafeInvokeFunc(fn any, args ...any) []reflect.Value { + fv := reflect.ValueOf(fn) + params := make([]reflect.Value, len(args)) + for i, item := range args { + params[i] = reflect.ValueOf(item) + } + return fv.Call(params) +} + +func mustBeFunction(function any) { + v := reflect.ValueOf(function) + if v.Kind() != reflect.Func { + panic(fmt.Sprintf("Invalid function type, value of type %T", function)) + } +} diff --git a/function/function_example_test.go b/function/function_example_test.go new file mode 100644 index 00000000..42059495 --- /dev/null +++ b/function/function_example_test.go @@ -0,0 +1,223 @@ +package function + +import ( + "fmt" + "strings" + "time" +) + +func ExampleAfter() { + fn := After(2, func() { + fmt.Println("test") + }) + + fn() + fn() + + // Output: + // test +} + +func ExampleBefore() { + fn := Before(2, func() { + fmt.Println("test") + }) + + fn() + fn() + fn() + fn() + + // Output: + // test + // test +} + +func ExampleCurryFn_New() { + add := func(a, b int) int { + return a + b + } + + var addCurry CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) + } + add1 := addCurry.New(1) + + result := add1(2) + + fmt.Println(result) + + // Output: + // 3 +} + +func ExampleCompose() { + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) + } + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) + } + transform := Compose(toUpper, toLower) + + result := transform("aBCde") + + fmt.Println(result) + + // Output: + // ABCDE +} + +func ExampleDelay() { + var print = func(s string) { + fmt.Println(s) + } + + Delay(2*time.Second, print, "hello") + + // Output: + // hello +} + +func ExampleDebounce() { + callCount := 0 + fn := func() { + callCount++ + } + + debouncedFn, _ := Debounce(fn, 500*time.Millisecond) + + for i := 0; i < 10; i++ { + debouncedFn() + time.Sleep(50 * time.Millisecond) + } + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + debouncedFn() + + time.Sleep(1 * time.Second) + fmt.Println(callCount) + + // Output: + // 1 + // 2 +} + +func ExampleDebounced() { + count := 0 + add := func() { + count++ + } + + debouncedAdd := Debounced(add, 50*time.Microsecond) + + debouncedAdd() + debouncedAdd() + debouncedAdd() + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + debouncedAdd() + + time.Sleep(100 * time.Millisecond) + + fmt.Println(count) + + // Output: + // 1 + // 2 +} + +func ExampleSchedule() { + count := 0 + + increase := func() { + count++ + } + + stop := Schedule(2*time.Second, increase) + + time.Sleep(2 * time.Second) + close(stop) + + fmt.Println(count) + + // Output: + // 2 +} + +func ExamplePipeline() { + addOne := func(x int) int { + return x + 1 + } + double := func(x int) int { + return 2 * x + } + square := func(x int) int { + return x * x + } + + fn := Pipeline(addOne, double, square) + + result := fn(2) + + fmt.Println(result) + + // Output: + // 36 +} + +func ExampleAcceptIf() { + + adder := AcceptIf( + And( + func(x int) bool { + return x > 10 + }, func(x int) bool { + return x%2 == 0 + }), + func(x int) int { + return x + 1 + }, + ) + + result, ok := adder(20) + fmt.Println(result) + fmt.Println(ok) + + result, ok = adder(21) + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 21 + // true + // 0 + // false +} + +func ExampleThrottle() { + callCount := 0 + + fn := func() { + callCount++ + } + + throttledFn := Throttle(fn, 1*time.Second) + + for i := 0; i < 5; i++ { + throttledFn() + } + + time.Sleep(1 * time.Second) + + fmt.Println(callCount) + + // Output: + // 1 +} diff --git a/function/function_internal.go b/function/function_internal.go deleted file mode 100644 index b819d95f..00000000 --- a/function/function_internal.go +++ /dev/null @@ -1,39 +0,0 @@ -package function - -import ( - "fmt" - "reflect" -) - -func invokeFunc(fn interface{}, args ...interface{}) []reflect.Value { - fv := functionValue(fn) - params := make([]reflect.Value, len(args)) - for i, item := range args { - params[i] = reflect.ValueOf(item) - } - return fv.Call(params) -} - -func unsafeInvokeFunc(fn interface{}, args ...interface{}) []reflect.Value { - fv := reflect.ValueOf(fn) - params := make([]reflect.Value, len(args)) - for i, item := range args { - params[i] = reflect.ValueOf(item) - } - return fv.Call(params) -} - -func functionValue(function interface{}) reflect.Value { - v := reflect.ValueOf(function) - if v.Kind() != reflect.Func { - panic(fmt.Sprintf("Invalid function type, value of type %T", function)) - } - return v -} - -func mustBeFunction(function interface{}) { - v := reflect.ValueOf(function) - if v.Kind() != reflect.Func { - panic(fmt.Sprintf("Invalid function type, value of type %T", function)) - } -} diff --git a/function/function_test.go b/function/function_test.go index 98f53f56..4ff33a59 100644 --- a/function/function_test.go +++ b/function/function_test.go @@ -7,16 +7,18 @@ import ( "testing" "time" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestAfter(t *testing.T) { + t.Parallel() + arr := []string{"a", "b"} f := After(len(arr), func(i int) int { fmt.Println("print done") return i }) - type cb func(args ...interface{}) []reflect.Value + type cb func(args ...any) []reflect.Value print := func(i int, s string, fn cb) { fmt.Printf("print: arr[%d] is %s \n", i, s) v := fn(i) @@ -34,6 +36,8 @@ func TestAfter(t *testing.T) { } func TestBefore(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBefore") arr := []string{"a", "b", "c", "d", "e"} @@ -42,8 +46,8 @@ func TestBefore(t *testing.T) { }) var res []int64 - type cb func(args ...interface{}) []reflect.Value - appendStr := func(i int, s string, fn cb) { + type cb func(args ...any) []reflect.Value + appendStr := func(i int, _ string, fn cb) { v := fn(i) res = append(res, v[0].Int()) } @@ -57,29 +61,35 @@ func TestBefore(t *testing.T) { } func TestCurry(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCurry") add := func(a, b int) int { return a + b } - var addCurry Fn = func(values ...interface{}) interface{} { - return add(values[0].(int), values[1].(int)) + var addCurry CurryFn[int] = func(values ...int) int { + return add(values[0], values[1]) } - add1 := addCurry.Curry(1) + add1 := addCurry.New(1) + assert.Equal(3, add1(2)) } func TestCompose(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCompose") - toUpper := func(a ...interface{}) interface{} { - return strings.ToUpper(a[0].(string)) + toUpper := func(strs ...string) string { + return strings.ToUpper(strs[0]) } - toLower := func(a ...interface{}) interface{} { - return strings.ToLower(a[0].(string)) + toLower := func(strs ...string) string { + return strings.ToLower(strs[0]) } expected := toUpper(toLower("aBCde")) + cf := Compose(toUpper, toLower) res := cf("aBCde") @@ -115,18 +125,254 @@ func TestDebounced(t *testing.T) { assert.Equal(2, count) } +func TestDebounce(t *testing.T) { + assert := internal.NewAssert(t, "TestDebounce") + + t.Run("Single call", func(t *testing.T) { + callCount := 0 + + debouncedFn, _ := Debounce(func() { + callCount++ + }, 500*time.Millisecond) + + debouncedFn() + + time.Sleep(1 * time.Second) + + assert.Equal(1, callCount) + }) + + t.Run("Multiple calls within debounce interval", func(t *testing.T) { + callCount := 0 + + debouncedFn, _ := Debounce(func() { + callCount++ + }, 1*time.Second) + + for i := 0; i < 5; i++ { + go func(index int) { + time.Sleep(time.Duration(index) * 200 * time.Millisecond) + debouncedFn() + }(i) + } + + time.Sleep(2 * time.Second) + + assert.Equal(1, callCount) + }) + + t.Run("Immediate consecutive calls", func(t *testing.T) { + callCount := 0 + + debouncedFn, _ := Debounce(func() { + callCount++ + }, 500*time.Millisecond) + + for i := 0; i < 10; i++ { + debouncedFn() + time.Sleep(50 * time.Millisecond) + } + + time.Sleep(1 * time.Second) + + assert.Equal(1, callCount) + }) + + t.Run("Cancel calls", func(t *testing.T) { + callCount := 0 + + debouncedFn, cancelFn := Debounce(func() { + callCount++ + }, 500*time.Millisecond) + + debouncedFn() + + cancelFn() + + time.Sleep(1 * time.Second) + + assert.Equal(0, callCount) + }) + +} + +func TestThrottle(t *testing.T) { + assert := internal.NewAssert(t, "TestThrottle") + + t.Run("Single call", func(t *testing.T) { + callCount := 0 + + throttledFn := Throttle(func() { + callCount++ + }, 1*time.Second) + + throttledFn() + + time.Sleep(100 * time.Millisecond) + + assert.Equal(1, callCount) + }) + + t.Run("Multiple calls within throttle interval", func(t *testing.T) { + callCount := 0 + + throttledFn := Throttle(func() { + callCount++ + }, 1*time.Second) + + for i := 0; i < 5; i++ { + throttledFn() + } + + time.Sleep(1 * time.Second) + + assert.Equal(1, callCount) + }) + + t.Run("Mutiple calls space out throttle interval", func(t *testing.T) { + callCount := 0 + + throttledFn := Throttle(func() { + callCount++ + }, 500*time.Millisecond) + + throttledFn() + time.Sleep(600 * time.Millisecond) + + throttledFn() + time.Sleep(600 * time.Millisecond) + + throttledFn() + + time.Sleep(1 * time.Second) + + assert.Equal(3, callCount) + }) + + t.Run("Call function near the end of the interval", func(t *testing.T) { + callCount := 0 + + throttledFn := Throttle(func() { + callCount++ + }, 1*time.Second) + + throttledFn() + time.Sleep(900 * time.Millisecond) + + throttledFn() + time.Sleep(200 * time.Millisecond) + + assert.Equal(2, callCount) + }) + +} + func TestSchedule(t *testing.T) { assert := internal.NewAssert(t, "TestSchedule") - var res []string - appendStr := func(s string) { - res = append(res, s) + t.Run("Single call", func(t *testing.T) { + res := []string{} + appendStr := func(s string) { + res = append(res, s) + } + stop := Schedule(200*time.Millisecond, appendStr, "*") + close(stop) + + time.Sleep(400 * time.Millisecond) + + assert.Equal([]string{"*"}, res) + }) + + t.Run("Multiple calls", func(t *testing.T) { + res := []string{} + appendStr := func(s string) { + res = append(res, s) + } + stop := Schedule(200*time.Millisecond, appendStr, "*") + + time.Sleep(800 * time.Millisecond) + + close(stop) + + assert.Equal([]string{"*", "*", "*", "*"}, res) + }) + +} + +func TestPipeline(t *testing.T) { + assert := internal.NewAssert(t, "TestPipeline") + + addOne := func(x int) int { + return x + 1 + } + double := func(x int) int { + return 2 * x + } + square := func(x int) int { + return x * x } - stop := Schedule(1*time.Second, appendStr, "*") - time.Sleep(5 * time.Second) - close(stop) + f := Pipeline(addOne, double, square) - expected := []string{"*", "*", "*", "*", "*"} - assert.Equal(expected, res) + assert.Equal(36, f(2)) +} + +func TestAcceptIf(t *testing.T) { + assert := internal.NewAssert(t, "AcceptIf") + + adder := AcceptIf( + And( + func(x int) bool { + return x > 10 + }, func(x int) bool { + return x%2 == 0 + }), + func(x int) int { + return x + 1 + }, + ) + + result, ok := adder(20) + assert.Equal(21, result) + assert.Equal(true, ok) + + result, ok = adder(21) + assert.Equal(0, result) + assert.Equal(false, ok) +} + +func TestAcceptIfPanicMissingPredicate(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAcceptIfPanicMissingPredicate") + + defer func() { + v := recover() + assert.Equal("programming error: predicate must be not nil", v) + }() + + AcceptIf( + nil, + func(x int) int { + return x + }, + ) +} + +func TestAcceptIfPanicMissingApply(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAcceptIfPanicMissingApply") + + defer func() { + v := recover() + assert.Equal("programming error: apply must be not nil", v) + }() + + AcceptIf( + func(i int) bool { + return false + }, + nil, + ) } diff --git a/function/predicate.go b/function/predicate.go new file mode 100644 index 00000000..c31eeb2d --- /dev/null +++ b/function/predicate.go @@ -0,0 +1,97 @@ +package function + +// And returns a composed predicate that represents the logical AND of a list of predicates. +// It evaluates to true only if all predicates evaluate to true for the given value. +// Play: https://go.dev/play/p/dTBHJMQ0zD2 +func And[T any](predicates ...func(T) bool) func(T) bool { + if len(predicates) < 2 { + panic("programming error: predicates count must be at least 2") + } + return func(value T) bool { + for _, predicate := range predicates { + if !predicate(value) { + return false // Short-circuit on the first false predicate + } + } + return true // True if all predicates are true + } +} + +// 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. +// Play: https://go.dev/play/p/Rb-FdNGpgSO +func Nand[T any](predicates ...func(T) bool) func(T) bool { + if len(predicates) < 2 { + panic("programming error: predicates count must be at least 2") + } + return func(value T) bool { + for _, predicate := range predicates { + if predicate(value) { + return false // Short-circuit on the first true predicate + } + } + return true // True if all predicates are false + } +} + +// Negate returns a predicate that represents the logical negation of this predicate. +// Play: https://go.dev/play/p/jbI8BtgFnVE +func Negate[T any](predicate func(T) bool) func(T) bool { + return func(value T) bool { + return !predicate(value) + } +} + +// Or returns a composed predicate that represents the logical OR of a list of predicates. +// It evaluates to true if at least one of the predicates evaluates to true for the given value. +// Play: https://go.dev/play/p/LitCIsDFNDA +func Or[T any](predicates ...func(T) bool) func(T) bool { + if len(predicates) < 2 { + panic("programming error: predicates count must be at least 2") + } + return func(value T) bool { + for _, predicate := range predicates { + if predicate(value) { + return true // Short-circuit on the first true predicate + } + } + return false // False if all predicates are false + } +} + +// Nor returns a composed predicate that represents the logical NOR of a list of predicates. +// It evaluates to true only if all predicates evaluate to false for the given value. +// Play: https://go.dev/play/p/2KdCoBEOq84 +func Nor[T any](predicates ...func(T) bool) func(T) bool { + if len(predicates) < 2 { + panic("programming error: predicates count must be at least 2") + } + return func(value T) bool { + for _, predicate := range predicates { + if predicate(value) { + return false // If any predicate evaluates to true, the NOR result is false + } + } + return true // Only returns true if all predicates evaluate to false + } +} + +// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates. +// It evaluates to true only if all predicates evaluate to true or false for the given value. +// Play: https://go.dev/play/p/FJxko8SFbqc +func Xnor[T any](predicates ...func(T) bool) func(T) bool { + if len(predicates) < 2 { + panic("programming error: predicates count must be at least 2") + } + return func(value T) bool { + trueCount := 0 + for _, predicate := range predicates { + if predicate(value) { + trueCount++ + } + } + // XNOR is true if either all predicates are true or all are false + // This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true) + return trueCount == 0 || trueCount == len(predicates) + } +} diff --git a/function/predicate_example_test.go b/function/predicate_example_test.go new file mode 100644 index 00000000..22bb0973 --- /dev/null +++ b/function/predicate_example_test.go @@ -0,0 +1,128 @@ +package function + +import ( + "fmt" + "strings" +) + +func ExampleNegate() { + // Define some simple predicates for demonstration + isUpperCase := func(s string) bool { + return strings.ToUpper(s) == s + } + isLowerCase := func(s string) bool { + return strings.ToLower(s) == s + } + isMixedCase := Negate(Or(isUpperCase, isLowerCase)) + + fmt.Println(isMixedCase("ABC")) + fmt.Println(isMixedCase("AbC")) + + // Output: + // false + // true +} + +func ExampleOr() { + containsDigitOrSpecialChar := Or( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return strings.ContainsAny(s, "!@#$%") }, + ) + + fmt.Println(containsDigitOrSpecialChar("hello!")) + fmt.Println(containsDigitOrSpecialChar("hello")) + + // Output: + // true + // false +} + +func ExampleAnd() { + isNumericAndLength5 := And( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcde")) + + // Output: + // true + // false + // false +} + +func ExampleNand() { + isNumericAndLength5 := Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(isNumericAndLength5("12345")) + fmt.Println(isNumericAndLength5("1234")) + fmt.Println(isNumericAndLength5("abcdef")) + + // Output: + // false + // false + // true +} + +func ExampleNor() { + match := Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("dbcdckkeee")) + + match = Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + fmt.Println(match("0123456789")) + + // Output: + // true + // false +} + +func ExampleXnor() { + isEven := func(i int) bool { return i%2 == 0 } + isPositive := func(i int) bool { return i > 0 } + + match := Xnor(isEven, isPositive) + + fmt.Println(match(2)) + fmt.Println(match(-3)) + fmt.Println(match(3)) + + // Output: + // true + // true + // false +} + +// func ExamplePredicatesMix() { +// a := Or( +// func(s string) bool { return strings.ContainsAny(s, "0123456789") }, +// func(s string) bool { return strings.ContainsAny(s, "!") }, +// ) + +// b := And( +// func(s string) bool { return strings.ContainsAny(s, "hello") }, +// func(s string) bool { return strings.ContainsAny(s, "!") }, +// ) + +// c := Negate(And(a, b)) +// fmt.Println(c("hello!")) + +// c = Nor(a, b) +// fmt.Println(c("hello!")) + +// // Output: +// // false +// // false +// } diff --git a/function/predicate_test.go b/function/predicate_test.go new file mode 100644 index 00000000..95bde2b8 --- /dev/null +++ b/function/predicate_test.go @@ -0,0 +1,134 @@ +package function + +import ( + "strings" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestPredicatesNegatePure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesNegatePure") + + // Define some simple predicates for demonstration + isUpperCase := func(s string) bool { + return strings.ToUpper(s) == s + } + isLowerCase := func(s string) bool { + return strings.ToLower(s) == s + } + isMixedCase := Negate(Or(isUpperCase, isLowerCase)) + + assert.ShouldBeFalse(isMixedCase("ABC")) + assert.ShouldBeTrue(isMixedCase("AbC")) +} + +func TestPredicatesOrPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesOrPure") + + containsDigitOrSpecialChar := Or( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return strings.ContainsAny(s, "!@#$%") }, + ) + + assert.ShouldBeTrue(containsDigitOrSpecialChar("hello!")) + assert.ShouldBeFalse(containsDigitOrSpecialChar("hello")) +} + +func TestPredicatesAndPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesAndPure") + + isNumericAndLength5 := And( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + assert.ShouldBeTrue(isNumericAndLength5("12345")) + assert.ShouldBeFalse(isNumericAndLength5("1234")) + assert.ShouldBeFalse(isNumericAndLength5("abcde")) +} + +func TestPredicatesNandPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesNandPure") + + isNumericAndLength5 := Nand( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + assert.ShouldBeFalse(isNumericAndLength5("12345")) + assert.ShouldBeFalse(isNumericAndLength5("1234")) + assert.ShouldBeTrue(isNumericAndLength5("abcdef")) +} + +func TestPredicatesNorPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesNorPure") + + match := Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + assert.ShouldBeTrue(match("dbcdckkeee")) + + match = Nor( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return len(s) == 5 }, + ) + + assert.ShouldBeFalse(match("0123456789")) +} + +func TestPredicatesXnorPure(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesXnorPure") + + isEven := func(i int) bool { return i%2 == 0 } + isPositive := func(i int) bool { return i > 0 } + + match := Xnor(isEven, isPositive) + + assert.ShouldBeTrue(match(2)) + assert.ShouldBeTrue(match(-3)) + assert.ShouldBeFalse(match(3)) +} + +func TestPredicatesMix(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPredicatesMix") + + a := Or( + func(s string) bool { return strings.ContainsAny(s, "0123456789") }, + func(s string) bool { return strings.ContainsAny(s, "!") }, + ) + + b := And( + func(s string) bool { return strings.ContainsAny(s, "hello") }, + func(s string) bool { return strings.ContainsAny(s, "!") }, + ) + + c := Negate(And(a, b)) + + assert.ShouldBeFalse(c("hello!")) + + k := Nor(a, b) + assert.ShouldBeFalse(k("hello!")) + + o := Xnor(a, b) + assert.ShouldBeTrue(o("hello!")) + + p := Nand(c, k) + assert.ShouldBeTrue(p("hello!")) +} diff --git a/function/watcher.go b/function/watcher.go index a484acb4..6028189b 100644 --- a/function/watcher.go +++ b/function/watcher.go @@ -3,12 +3,18 @@ package function import "time" // Watcher is used for record code excution time +// Play: https://go.dev/play/p/l2yrOpCLd1I type Watcher struct { startTime int64 stopTime int64 excuting bool } +// Start the watch timer. +func NewWatcher() *Watcher { + return &Watcher{} +} + // Start the watch timer. func (w *Watcher) Start() { w.startTime = time.Now().UnixNano() diff --git a/function/watcher_example_test.go b/function/watcher_example_test.go new file mode 100644 index 00000000..9da542f2 --- /dev/null +++ b/function/watcher_example_test.go @@ -0,0 +1,22 @@ +package function + +import "fmt" + +func ExampleWatcher() { + w := NewWatcher() + + w.Start() + + longRunningTask() + + w.Stop() + + // eapsedTime := w.GetElapsedTime().Milliseconds() + + fmt.Println("foo") + + w.Reset() + + // Output: + // foo +} diff --git a/function/watcher_test.go b/function/watcher_test.go index 2660a29a..3bc44075 100644 --- a/function/watcher_test.go +++ b/function/watcher_test.go @@ -3,13 +3,15 @@ package function import ( "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestWatcher(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestWatcher") - w := &Watcher{} + w := NewWatcher() w.Start() longRunningTask() @@ -29,9 +31,10 @@ func TestWatcher(t *testing.T) { assert.Equal(int64(0), w.stopTime) } -func longRunningTask() { - var slice []int64 +func longRunningTask() []int64 { + var data []int64 for i := 0; i < 10000000; i++ { - slice = append(slice, int64(i)) + data = append(data, int64(i)) } + return data } diff --git a/go.mod b/go.mod index 02610a3a..e5be56c2 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ -module github.com/duke-git/lancet +module github.com/duke-git/lancet/v2 -go 1.16 +go 1.18 + +require ( + golang.org/x/exp v0.0.0-20221208152030-732eee02a75a + golang.org/x/text v0.9.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..4aeb174e --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= +golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= diff --git a/internal/assert.go b/internal/assert.go index 3e817d8b..8ef907bf 100644 --- a/internal/assert.go +++ b/internal/assert.go @@ -5,6 +5,7 @@ package internal import ( + "bytes" "fmt" "reflect" "runtime" @@ -30,22 +31,82 @@ func NewAssert(t *testing.T, caseName string) *Assert { } // Equal check if expected is equal with actual -func (a *Assert) Equal(expected, actual interface{}) { +func (a *Assert) Equal(expected, actual any) { if compare(expected, actual) != compareEqual { makeTestFailed(a.T, a.CaseName, expected, actual) } } +// ShouldBeFalse check if expected is false +func (a *Assert) ShouldBeFalse(actual any) { + if compare(false, actual) != compareEqual { + makeTestFailed(a.T, a.CaseName, false, actual) + } +} + +// ShouldBeTrue check if expected is true +func (a *Assert) ShouldBeTrue(actual any) { + if compare(true, actual) != compareEqual { + makeTestFailed(a.T, a.CaseName, true, actual) + } +} + // NotEqual check if expected is not equal with actual -func (a *Assert) NotEqual(expected, actual interface{}) { +func (a *Assert) NotEqual(expected, actual any) { if compare(expected, actual) == compareEqual { expectedInfo := fmt.Sprintf("not %v", expected) makeTestFailed(a.T, a.CaseName, expectedInfo, actual) } } +// EqualValues asserts that two objects are equal or convertable to the same types and equal. +// https://github.com/stretchr/testify/assert/assertions.go +func (a *Assert) EqualValues(expected, actual any) { + if !objectsAreEqualValues(expected, actual) { + makeTestFailed(a.T, a.CaseName, expected, actual) + } +} + +func objectsAreEqualValues(expected, actual interface{}) bool { + if objectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +func objectsAreEqual(expected, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) + } + + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) +} + // Greater check if expected is greate than actual -func (a *Assert) Greater(expected, actual interface{}) { +func (a *Assert) Greater(expected, actual any) { if compare(expected, actual) != compareGreater { expectedInfo := fmt.Sprintf("> %v", expected) makeTestFailed(a.T, a.CaseName, expectedInfo, actual) @@ -53,7 +114,7 @@ func (a *Assert) Greater(expected, actual interface{}) { } // GreaterOrEqual check if expected is greate than or equal with actual -func (a *Assert) GreaterOrEqual(expected, actual interface{}) { +func (a *Assert) GreaterOrEqual(expected, actual any) { isGreatOrEqual := compare(expected, actual) == compareGreater || compare(expected, actual) == compareEqual if !isGreatOrEqual { expectedInfo := fmt.Sprintf(">= %v", expected) @@ -62,7 +123,7 @@ func (a *Assert) GreaterOrEqual(expected, actual interface{}) { } // Less check if expected is less than actual -func (a *Assert) Less(expected, actual interface{}) { +func (a *Assert) Less(expected, actual any) { if compare(expected, actual) != compareLess { expectedInfo := fmt.Sprintf("< %v", expected) makeTestFailed(a.T, a.CaseName, expectedInfo, actual) @@ -70,7 +131,7 @@ func (a *Assert) Less(expected, actual interface{}) { } // LessOrEqual check if expected is less than or equal with actual -func (a *Assert) LessOrEqual(expected, actual interface{}) { +func (a *Assert) LessOrEqual(expected, actual any) { isLessOrEqual := compare(expected, actual) == compareLess || compare(expected, actual) == compareEqual if !isLessOrEqual { expectedInfo := fmt.Sprintf("<= %v", expected) @@ -79,22 +140,24 @@ func (a *Assert) LessOrEqual(expected, actual interface{}) { } // IsNil check if value is nil -func (a *Assert) IsNil(value interface{}) { - if value != nil { - makeTestFailed(a.T, a.CaseName, nil, value) +func (a *Assert) IsNil(v any) { + if v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil()) { + return } + + makeTestFailed(a.T, a.CaseName, nil, v) } // IsNotNil check if value is not nil -func (a *Assert) IsNotNil(value interface{}) { - if value == nil { - makeTestFailed(a.T, a.CaseName, "not nil", value) +func (a *Assert) IsNotNil(v any) { + if v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil()) { + makeTestFailed(a.T, a.CaseName, "not nil", v) } } // compare x and y return : // x > y -> 1, x < y -> -1, x == y -> 0, x != y -> -2 -func compare(x, y interface{}) int { +func compare(x, y any) int { vx := reflect.ValueOf(x) vy := reflect.ValueOf(y) @@ -162,8 +225,8 @@ func compare(x, y interface{}) int { } -// logFailedInfo make test failed and log error info -func makeTestFailed(t *testing.T, caseName string, expected, actual interface{}) { +// makeTestFailed make test failed and log error info +func makeTestFailed(t *testing.T, caseName string, expected, actual any) { _, file, line, _ := runtime.Caller(2) errInfo := fmt.Sprintf("Case %v failed. file: %v, line: %v, expected: %v, actual: %v.", caseName, file, line, expected, actual) t.Error(errInfo) diff --git a/internal/assert_test.go b/internal/assert_test.go index ae0bcfa2..297872f0 100644 --- a/internal/assert_test.go +++ b/internal/assert_test.go @@ -6,10 +6,11 @@ import ( func TestAssert(t *testing.T) { assert := NewAssert(t, "TestAssert") + assert.Equal(0, 0) assert.NotEqual(1, 0) - assert.NotEqual("1", 1) + var uInt1 uint var uInt2 uint var uInt8 uint8 @@ -47,4 +48,11 @@ func TestAssert(t *testing.T) { assert.IsNil(nil) assert.IsNotNil("abc") + var valA int = 1 + var valB int64 = 1 + assert.NotEqual(valA, valB) + assert.EqualValues(valA, valB) + + assert.ShouldBeFalse(false) + assert.ShouldBeTrue(true) } diff --git a/internal/error_join.go b/internal/error_join.go new file mode 100644 index 00000000..1f62cacc --- /dev/null +++ b/internal/error_join.go @@ -0,0 +1,51 @@ +package internal + +// Note: this file is copyed from the go standart repo (https://github.com/golang/go/blob/master/src/errors/join.go). +// just in order to adapt under go1.9 +// do not use it outside lancet lib. + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if errs contains no non-nil values. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +func JoinError(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/iterator/iterator.go b/iterator/iterator.go new file mode 100644 index 00000000..949405e3 --- /dev/null +++ b/iterator/iterator.go @@ -0,0 +1,223 @@ +// Copyright 2022 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package iterator provides a way to iterate over values stored in containers. +// note: +// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go. +// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs. +// 3. It is currently under development, unstable, and will not be completed for some time in the future. +// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it. +// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413 +package iterator + +import ( + "context" + + "golang.org/x/exp/constraints" +) + +// Iterator supports iterating over a sequence of values of type `E`. +type Iterator[T any] interface { + // Next checks if there is a next value in the iteration or not + HasNext() bool + // Next returns the next value in the iteration if there is one, + // and reports whether the returned value is valid. + // Once Next returns ok==false, the iteration is over, + // and all subsequent calls will return ok==false. + Next() (item T, ok bool) +} + +// ResettableIterator supports to reset the iterator +type ResettableIterator[T any] interface { + Iterator[T] + // Reset allows for the iteration process over a sequence to be restarted from the beginning. + // It enables reusing the iterator for multiple traversals without needing to recreate it. + Reset() +} + +// StopIterator is an interface for stopping Iterator. +type StopIterator[T any] interface { + Iterator[T] + + // Stop indicates that the iterator will no longer be used. + // After a call to Stop, future calls to Next may panic. + // Stop may be called multiple times; + // all calls after the first will have no effect. + Stop() +} + +// DeleteIter is an Iter that implements a Delete method. +type DeleteIterator[T any] interface { + Iterator[T] + + // Delete deletes the current iterator element; + // that is, the one returned by the last call to Next. + // Delete should panic if called before Next or after + // Next returns false. + Delete() +} + +// SetIterator is an Iter that implements a Set method. +type SetIterator[T any] interface { + Iterator[T] + + // Set replaces the current iterator element with v. + // Set should panic if called before Next or after + // Next returns false. + Set(v T) +} + +// PrevIterator is an iterator with a Prev method. +type PrevIterator[T any] interface { + Iterator[T] + + // Prev moves the iterator to the previous position. + // After calling Prev, Next will return the value at + // that position in the container. For example, after + // it.Next() returning (v, true) + // it.Prev() + // another call to it.Next will again return (v, true). + // Calling Prev before calling Next may panic. + // Calling Prev after Next returns false will move + // to the last element, or, if there are no elements, + // to the iterator's initial state. + Prev() +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Functions that create an Iterator from some other type. // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// FromSlice returns an iterator over a slice of data. +func FromSlice[T any](slice []T) *SliceIterator[T] { + return &SliceIterator[T]{slice: slice, index: -1} +} + +func ToSlice[T any](iter Iterator[T]) []T { + result := []T{} + for item, ok := iter.Next(); ok; item, ok = iter.Next() { + result = append(result, item) + } + return result +} + +type SliceIterator[T any] struct { + slice []T + index int +} + +func (iter *SliceIterator[T]) HasNext() bool { + return iter.index < len(iter.slice)-1 +} + +func (iter *SliceIterator[T]) Next() (T, bool) { + iter.index++ + + ok := iter.index >= 0 && iter.index < len(iter.slice) + + var item T + if ok { + item = iter.slice[iter.index] + } + + return item, ok +} + +// Prev implements PrevIterator. +func (iter *SliceIterator[T]) Prev() { + if iter.index == -1 { + panic("Next function should be called Prev") + } + if iter.HasNext() { + iter.index-- + } else { + iter.index = len(iter.slice) - 1 + } +} + +// Set implements SetIterator. +func (iter *SliceIterator[T]) Set(value T) { + if iter.index == -1 { + panic("Next function should be called Set") + } + if iter.index >= len(iter.slice) || len(iter.slice) == 0 { + panic("No element in current iterator") + } + iter.slice[iter.index] = value +} + +func (iter *SliceIterator[T]) Reset() { + iter.index = -1 +} + +// FromRange creates a iterator which returns the numeric range between start inclusive and end +// exclusive by the step size. start should be less than end, step shoud be positive. +func FromRange[T constraints.Integer | constraints.Float](start, end, step T) *RangeIterator[T] { + if end < start { + panic("RangeIterator: start should be before end") + } else if step <= 0 { + panic("RangeIterator: step should be positive") + } + + return &RangeIterator[T]{start: start, end: end, step: step, current: start} +} + +type RangeIterator[T constraints.Integer | constraints.Float] struct { + start, end, step, current T +} + +func (iter *RangeIterator[T]) HasNext() bool { + return iter.current < iter.end +} + +func (iter *RangeIterator[T]) Next() (T, bool) { + if iter.current >= iter.end { + var zero T + return zero, false + } + num := iter.current + iter.current += iter.step + return num, true +} + +func (iter *RangeIterator[T]) Reset() { + iter.current = iter.start +} + +// FromChannel creates an iterator which returns items received from the provided channel. +// The iteration continues until the channel is closed. +func FromChannel[T any](channel <-chan T) *ChannelIterator[T] { + return &ChannelIterator[T]{channel: channel} +} + +type ChannelIterator[T any] struct { + channel <-chan T +} + +func (iter *ChannelIterator[T]) Next() (T, bool) { + item, ok := <-iter.channel + return item, ok +} + +func (iter *ChannelIterator[T]) HasNext() bool { + return len(iter.channel) == 0 +} + +// ToChannel create a new goroutine to pull items from the channel iterator to the returned channel. +func ToChannel[T any](ctx context.Context, iter Iterator[T], buffer int) <-chan T { + result := make(chan T, buffer) + + go func() { + defer close(result) + + for item, ok := iter.Next(); ok; item, ok = iter.Next() { + select { + case result <- item: + case <-ctx.Done(): + return + } + } + }() + + return result +} diff --git a/iterator/iterator_test.go b/iterator/iterator_test.go new file mode 100644 index 00000000..3a6e606f --- /dev/null +++ b/iterator/iterator_test.go @@ -0,0 +1,178 @@ +// Copyright 2022 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package iterator implements some feature of C++ STL iterators +package iterator + +import ( + "context" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestSliceIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSliceIterator") + + // HashNext + t.Run("slice iterator HasNext: ", func(t *testing.T) { + iter1 := FromSlice([]int{1, 2, 3, 4}) + for { + item, _ := iter1.Next() + + if item == 4 { + assert.Equal(false, iter1.HasNext()) + break + } else { + assert.Equal(true, iter1.HasNext()) + } + } + + iter2 := FromSlice([]int{}) + assert.Equal(false, iter2.HasNext()) + }) + + //Next + t.Run("slice iterator Next: ", func(t *testing.T) { + iter1 := FromSlice([]int{1, 2, 3, 4}) + for i := 0; i < 4; i++ { + item, ok := iter1.Next() + if !ok { + break + } + assert.Equal(i+1, item) + } + + iter2 := FromSlice([]int{}) + _, ok := iter2.Next() + assert.Equal(false, ok) + }) + + // Reset + t.Run("slice iterator Reset: ", func(t *testing.T) { + iter1 := FromSlice([]int{1, 2, 3, 4}) + for i := 0; i < 4; i++ { + item, ok := iter1.Next() + if !ok { + break + } + assert.Equal(i+1, item) + } + + iter1.Reset() + + for i := 0; i < 4; i++ { + item, ok := iter1.Next() + if !ok { + break + } + assert.Equal(i+1, item) + } + }) + + t.Run("slice iterator ToSlice: ", func(t *testing.T) { + iter := FromSlice([]int{1, 2, 3, 4}) + item, _ := iter.Next() + assert.Equal(1, item) + + data := ToSlice[int](iter) + assert.Equal([]int{2, 3, 4}, data) + }) +} + +func TestRangeIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRangeIterator") + + t.Run("range iterator: ", func(t *testing.T) { + iter := FromRange(1, 4, 1) + + item, ok := iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(2, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(3, item) + assert.Equal(true, ok) + + _, ok = iter.Next() + assert.Equal(false, ok) + assert.Equal(false, iter.HasNext()) + + iter.Reset() + + item, ok = iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(2, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(3, item) + assert.Equal(true, ok) + + _, ok = iter.Next() + assert.Equal(false, ok) + assert.Equal(false, iter.HasNext()) + }) + + t.Run("range iterator reset: ", func(t *testing.T) { + iter := FromRange(1, 4, 1) + + item, ok := iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(2, item) + assert.Equal(true, ok) + + iter.Reset() + + item, ok = iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(2, item) + assert.Equal(true, ok) + + item, ok = iter.Next() + assert.Equal(3, item) + assert.Equal(true, ok) + + _, ok = iter.Next() + assert.Equal(false, ok) + assert.Equal(false, iter.HasNext()) + }) + +} + +func TestChannelIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRangeIterator") + + var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4}) + + ctx, cancel := context.WithCancel(context.Background()) + iter = FromChannel(ToChannel(ctx, iter, 0)) + item, ok := iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + assert.Equal(true, iter.HasNext()) + + cancel() + + _, ok = iter.Next() + assert.Equal(false, ok) +} diff --git a/iterator/operation.go b/iterator/operation.go new file mode 100644 index 00000000..6340c5a0 --- /dev/null +++ b/iterator/operation.go @@ -0,0 +1,139 @@ +// Copyright 2022 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package iterator provides a way to iterate over values stored in containers. +// note: +// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go. +// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs. +// 3. It is currently under development, unstable, and will not be completed for some time in the future. +// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it. +// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413 +package iterator + +// Map creates a new iterator which applies a function to all items of input iterator. +func Map[T any, U any](iter Iterator[T], iteratee func(item T) U) Iterator[U] { + return &mapIterator[T, U]{ + iter: iter, + iteratee: iteratee, + } +} + +type mapIterator[T any, U any] struct { + iter Iterator[T] + iteratee func(T) U +} + +func (mr *mapIterator[T, U]) HasNext() bool { + return mr.iter.HasNext() +} + +func (mr *mapIterator[T, U]) Next() (U, bool) { + var zero U + item, ok := mr.iter.Next() + if !ok { + return zero, false + } + return mr.iteratee(item), true +} + +// Filter creates a new iterator that returns only the items that pass specified predicate function. +func Filter[T any](iter Iterator[T], predicateFunc func(item T) bool) Iterator[T] { + return &filterIterator[T]{iter: iter, predicateFunc: predicateFunc} +} + +type filterIterator[T any] struct { + iter Iterator[T] + predicateFunc func(T) bool +} + +func (fr *filterIterator[T]) Next() (T, bool) { + for item, ok := fr.iter.Next(); ok; item, ok = fr.iter.Next() { + if fr.predicateFunc(item) { + return item, true + } + } + var zero T + return zero, false +} + +func (fr *filterIterator[T]) HasNext() bool { + return fr.iter.HasNext() +} + +// Join creates an iterator that join all elements of iters[0], then all elements of iters[1] and so on. +func Join[T any](iters ...Iterator[T]) Iterator[T] { + return &joinIterator[T]{ + iters: iters, + } +} + +type joinIterator[T any] struct { + iters []Iterator[T] +} + +func (iter *joinIterator[T]) Next() (T, bool) { + for len(iter.iters) > 0 { + item, ok := iter.iters[0].Next() + if ok { + return item, true + } + iter.iters = iter.iters[1:] + } + var zero T + return zero, false +} + +func (iter *joinIterator[T]) HasNext() bool { + if len(iter.iters) == 0 { + return false + } + if len(iter.iters) == 1 { + return iter.iters[0].HasNext() + } + + result := iter.iters[0].HasNext() + + for i := 1; i < len(iter.iters); i++ { + it := iter.iters[i] + hasNext := it.HasNext() + result = result || hasNext + } + + return result +} + +// Reduce reduces iter to a single value using the reduction function reducer +func Reduce[T any, U any](iter Iterator[T], initial U, reducer func(U, T) U) U { + acc := initial + + for item, ok := iter.Next(); ok; item, ok = iter.Next() { + acc = reducer(acc, item) + } + + return acc +} + +func Take[T any](it Iterator[T], num int) Iterator[T] { + return &takeIterator[T]{it: it, num: num} +} + +type takeIterator[T any] struct { + it Iterator[T] + num int +} + +func (iter *takeIterator[T]) Next() (T, bool) { + if iter.num <= 0 { + var zero T + return zero, false + } + item, ok := iter.it.Next() + if ok { + iter.num-- + } + return item, ok +} + +func (iter *takeIterator[T]) HasNext() bool { + return iter.num > 0 +} diff --git a/iterator/operation_test.go b/iterator/operation_test.go new file mode 100644 index 00000000..e0889e9a --- /dev/null +++ b/iterator/operation_test.go @@ -0,0 +1,83 @@ +// Copyright 2022 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package iterator provides a way to iterate over values stored in containers. +// note: +// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go. +// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs. +// 3. It is currently under development, unstable, and will not be completed for some time in the future. +// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it. +// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413 +package iterator + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestMapIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapIterator") + + var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4}) + + iter = Map(iter, func(n int) int { return n / 2 }) + + result := ToSlice(iter) + assert.Equal([]int{0, 1, 1, 2}, result) +} + +func TestFilterIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilterIterator") + + var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4}) + + iter = Filter(iter, func(n int) bool { return n < 3 }) + + result := ToSlice(iter) + assert.Equal([]int{1, 2}, result) +} + +func TestJoinIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestJoinIterator") + + var iter1 Iterator[int] = FromSlice([]int{1, 2}) + var iter2 Iterator[int] = FromSlice([]int{3, 4}) + + var iter Iterator[int] = Join(iter1, iter2) + + item, ok := iter.Next() + assert.Equal(1, item) + assert.Equal(true, ok) + + assert.Equal([]int{2, 3, 4}, ToSlice(iter)) +} + +func TestReduce(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReduce") + + var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4}) + sum := Reduce(iter, 0, func(a, b int) int { return a + b }) + assert.Equal(10, sum) +} + +func TestTakeIterator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTakeIterator") + + var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4, 5}) + + iter = Take(iter, 3) + + result := ToSlice(iter) + assert.Equal([]int{1, 2, 3}, result) +} diff --git a/logo.png b/logo.png index 19fad302..481f8906 100644 Binary files a/logo.png and b/logo.png differ diff --git a/maputil/concurrentmap.go b/maputil/concurrentmap.go new file mode 100644 index 00000000..e229f5a7 --- /dev/null +++ b/maputil/concurrentmap.go @@ -0,0 +1,156 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package maputil includes some functions to manipulate map. +package maputil + +import ( + "fmt" + "sync" +) + +const defaultShardCount = 32 + +// ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines. +type ConcurrentMap[K comparable, V any] struct { + shardCount uint64 + locks []sync.RWMutex + maps []map[K]V +} + +// NewConcurrentMap create a ConcurrentMap with specific shard count. +// Play: https://go.dev/play/p/3PenTPETJT0 +func NewConcurrentMap[K comparable, V any](shardCount int) *ConcurrentMap[K, V] { + if shardCount <= 0 { + shardCount = defaultShardCount + } + + cm := &ConcurrentMap[K, V]{ + shardCount: uint64(shardCount), + locks: make([]sync.RWMutex, shardCount), + maps: make([]map[K]V, shardCount), + } + + for i := range cm.maps { + cm.maps[i] = make(map[K]V) + } + + return cm +} + +// Set the value for a key. +// Play: https://go.dev/play/p/3PenTPETJT0 +func (cm *ConcurrentMap[K, V]) Set(key K, value V) { + shard := cm.getShard(key) + + cm.locks[shard].Lock() + cm.maps[shard][key] = value + + cm.locks[shard].Unlock() +} + +// Get the value stored in the map for a key, or nil if no. +// Play: https://go.dev/play/p/3PenTPETJT0 +func (cm *ConcurrentMap[K, V]) Get(key K) (V, bool) { + shard := cm.getShard(key) + + cm.locks[shard].RLock() + value, ok := cm.maps[shard][key] + cm.locks[shard].RUnlock() + + return value, ok +} + +// GetOrSet returns the existing value for the key if present. +// Otherwise, it sets and returns the given value. +// Play: https://go.dev/play/p/aDcDApOK01a +func (cm *ConcurrentMap[K, V]) GetOrSet(key K, value V) (actual V, ok bool) { + shard := cm.getShard(key) + + cm.locks[shard].RLock() + if actual, ok := cm.maps[shard][key]; ok { + cm.locks[shard].RUnlock() + return actual, ok + } + cm.locks[shard].RUnlock() + + // lock again + cm.locks[shard].Lock() + if actual, ok = cm.maps[shard][key]; ok { + cm.locks[shard].Unlock() + return + } + + cm.maps[shard][key] = value + cm.locks[shard].Unlock() + + return value, ok +} + +// Delete the value for a key. +// Play: https://go.dev/play/p/uTIJZYhpVMS +func (cm *ConcurrentMap[K, V]) Delete(key K) { + shard := cm.getShard(key) + + cm.locks[shard].Lock() + delete(cm.maps[shard], key) + cm.locks[shard].Unlock() +} + +// GetAndDelete returns the existing value for the key if present and then delete the value for the key. +// Otherwise, do nothing, just return false +// Play: https://go.dev/play/p/ZyxeIXSZUiM +func (cm *ConcurrentMap[K, V]) GetAndDelete(key K) (actual V, ok bool) { + shard := cm.getShard(key) + + cm.locks[shard].RLock() + if actual, ok = cm.maps[shard][key]; ok { + cm.locks[shard].RUnlock() + cm.Delete(key) + return + } + cm.locks[shard].RUnlock() + + return actual, false +} + +// Has checks if map has the value for a key. +// Play: https://go.dev/play/p/C8L4ul9TVwf +func (cm *ConcurrentMap[K, V]) Has(key K) bool { + _, ok := cm.Get(key) + return ok +} + +// Range calls iterator sequentially for each key and value present in each of the shards in the map. +// If iterator returns false, range stops the iteration. +// Play: https://go.dev/play/p/iqcy7P8P0Pr +func (cm *ConcurrentMap[K, V]) Range(iterator func(key K, value V) bool) { + for shard := range cm.locks { + cm.locks[shard].RLock() + + for k, v := range cm.maps[shard] { + if !iterator(k, v) { + cm.locks[shard].RUnlock() + return + } + } + cm.locks[shard].RUnlock() + } +} + +// getShard get shard by a key. +func (cm *ConcurrentMap[K, V]) getShard(key K) uint64 { + hash := fnv32(fmt.Sprintf("%v", key)) + return uint64(hash) % cm.shardCount +} + +func fnv32(key string) uint32 { + hash := uint32(2166136261) + const prime32 = uint32(16777619) + keyLength := len(key) + for i := 0; i < keyLength; i++ { + hash *= prime32 + hash ^= uint32(key[i]) + } + return hash +} diff --git a/maputil/concurrentmap_test.go b/maputil/concurrentmap_test.go new file mode 100644 index 00000000..8dfae97e --- /dev/null +++ b/maputil/concurrentmap_test.go @@ -0,0 +1,174 @@ +package maputil + +import ( + "fmt" + "sync" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestConcurrentMap_Set_Get(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_Set_Get") + + cm := NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(10) + + for i := 0; i < 10; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + for j := 0; j < 10; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + assert.Equal(n, val) + assert.Equal(true, ok) + }(j) + } +} + +func TestConcurrentMap_GetOrSet(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_GetOrSet") + + cm := NewConcurrentMap[string, int](100) + + var wg sync.WaitGroup + wg.Add(5) + + for i := 0; i < 5; i++ { + go func(n int) { + val, ok := cm.GetOrSet(fmt.Sprintf("%d", n), n) + assert.Equal(n, val) + assert.Equal(false, ok) + wg.Done() + }(i) + } + wg.Wait() + + for j := 0; j < 5; j++ { + go func(n int) { + val, ok := cm.Get(fmt.Sprintf("%d", n)) + assert.Equal(n, val) + assert.Equal(true, ok) + }(j) + } +} + +func TestConcurrentMap_Delete(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_Delete") + + cm := NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(10) + + for i := 0; i < 10; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(10) + + for i := 0; i < 10; i++ { + go func(n int) { + cm.Delete(fmt.Sprintf("%d", n)) + wg2.Done() + }(i) + } + wg2.Wait() + + for j := 0; j < 10; j++ { + go func(n int) { + _, ok := cm.Get(fmt.Sprintf("%d", n)) + assert.Equal(false, ok) + }(j) + } +} + +func TestConcurrentMap_GetAndDelete(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_GetAndDelete") + + cm := NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(10) + for i := 0; i < 10; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + var wg2 sync.WaitGroup + wg2.Add(10) + for j := 0; j < 10; j++ { + go func(n int) { + val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n)) + assert.Equal(n, val) + assert.Equal(true, ok) + + _, ok = cm.Get(fmt.Sprintf("%d", n)) + assert.Equal(false, ok) + wg2.Done() + }(j) + } + + wg2.Wait() +} + +func TestConcurrentMap_Has(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_Has") + + cm := NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(10) + + for i := 0; i < 10; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + for j := 0; j < 10; j++ { + go func(n int) { + ok := cm.Has(fmt.Sprintf("%d", n)) + assert.Equal(true, ok) + }(j) + } +} + +func TestConcurrentMap_Range(t *testing.T) { + assert := internal.NewAssert(t, "TestConcurrentMap_Range") + + cm := NewConcurrentMap[string, int](100) + + var wg1 sync.WaitGroup + wg1.Add(10) + + for i := 0; i < 10; i++ { + go func(n int) { + cm.Set(fmt.Sprintf("%d", n), n) + wg1.Done() + }(i) + } + wg1.Wait() + + cm.Range(func(key string, value int) bool { + assert.Equal(key, fmt.Sprintf("%d", value)) + return true + }) +} diff --git a/maputil/map.go b/maputil/map.go new file mode 100644 index 00000000..a033cf0d --- /dev/null +++ b/maputil/map.go @@ -0,0 +1,682 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package maputil includes some functions to manipulate map. +package maputil + +import ( + "fmt" + "reflect" + "sort" + "strings" + + "golang.org/x/exp/constraints" + + "github.com/duke-git/lancet/v2/slice" +) + +// Keys returns a slice of the map's keys. +// Play: https://go.dev/play/p/xNB5bTb97Wd +func Keys[K comparable, V any](m map[K]V) []K { + keys := make([]K, len(m)) + + var i int + for k := range m { + keys[i] = k + i++ + } + + return keys +} + +// Values returns a slice of the map's values. +// Play: https://go.dev/play/p/CBKdUc5FTW6 +func Values[K comparable, V any](m map[K]V) []V { + values := make([]V, len(m)) + + var i int + for _, v := range m { + values[i] = v + i++ + } + + return values +} + +// KeysBy creates a slice whose element is the result of function mapper invoked by every map's key. +// Play: https://go.dev/play/p/hI371iB8Up8 +func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T { + keys := make([]T, 0, len(m)) + + for k := range m { + keys = append(keys, mapper(k)) + } + + return keys +} + +// ValuesBy creates a slice whose element is the result of function mapper invoked by every map's value. +// Play: https://go.dev/play/p/sg9-oRidh8f +func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T { + keys := make([]T, 0, len(m)) + + for _, v := range m { + keys = append(keys, mapper(v)) + } + + return keys +} + +// Merge maps, next key will overwrite previous key. +// Play: https://go.dev/play/p/H95LENF1uB- +func Merge[K comparable, V any](maps ...map[K]V) map[K]V { + size := 0 + for i := range maps { + size += len(maps[i]) + } + + result := make(map[K]V, size) + + for _, m := range maps { + for k, v := range m { + result[k] = v + } + } + + return result +} + +// ForEach executes iteratee funcation for every key and value pair in map. +// Play: https://go.dev/play/p/OaThj6iNVXK +func ForEach[K comparable, V any](m map[K]V, iteratee func(key K, value V)) { + for k, v := range m { + iteratee(k, v) + } +} + +// Filter iterates over map, return a new map contains all key and value pairs pass the predicate function. +// Play: https://go.dev/play/p/fSvF3wxuNG7 +func Filter[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if predicate(k, v) { + result[k] = v + } + } + return result +} + +// FilterByKeys iterates over map, return a new map whose keys are all given keys. +// Play: https://go.dev/play/p/7ov6BJHbVqh +func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if slice.Contain(keys, k) { + result[k] = v + } + } + return result +} + +// FilterByValues iterates over map, return a new map whose values are all given values. +// Play: https://go.dev/play/p/P3-9MdcXegR +func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if slice.Contain(values, v) { + result[k] = v + } + } + return result +} + +// OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true. +// Play: https://go.dev/play/p/YJM4Hj5hNwm +func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if !predicate(k, v) { + result[k] = v + } + } + return result +} + +// OmitByKeys the opposite of FilterByKeys, extracts all the map elements which keys are not omitted. +// Play: https://go.dev/play/p/jXGrWDBfSRp +func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if !slice.Contain(keys, k) { + result[k] = v + } + } + return result +} + +// OmitByValues the opposite of FilterByValues. remov all elements whose value are in the give slice. +// Play: https://go.dev/play/p/XB7Y10uw20_U +func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V { + result := make(map[K]V) + + for k, v := range m { + if !slice.Contain(values, v) { + result[k] = v + } + } + return result +} + +// Intersect iterates over maps, return a new map of key and value pairs in all given maps. +// Play: https://go.dev/play/p/Zld0oj3sjcC +func Intersect[K comparable, V any](maps ...map[K]V) map[K]V { + if len(maps) == 0 { + return map[K]V{} + } + if len(maps) == 1 { + return maps[0] + } + + var result map[K]V + + reducer := func(m1, m2 map[K]V) map[K]V { + m := make(map[K]V) + for k, v1 := range m1 { + if v2, ok := m2[k]; ok && reflect.DeepEqual(v1, v2) { + m[k] = v1 + } + } + return m + } + + reduceMaps := make([]map[K]V, 2) + result = reducer(maps[0], maps[1]) + + for i := 2; i < len(maps); i++ { + reduceMaps[0] = result + reduceMaps[1] = maps[i] + result = reducer(reduceMaps[0], reduceMaps[1]) + } + + return result +} + +// Minus creates a map of whose key in mapA but not in mapB. +// Play: https://go.dev/play/p/3u5U9K7YZ9m +func Minus[K comparable, V any](mapA, mapB map[K]V) map[K]V { + result := make(map[K]V) + + for k, v := range mapA { + if _, ok := mapB[k]; !ok { + result[k] = v + } + } + return result +} + +// IsDisjoint two map are disjoint if they have no keys in common. +// Play: https://go.dev/play/p/N9qgYg_Ho6f +func IsDisjoint[K comparable, V any](mapA, mapB map[K]V) bool { + for k := range mapA { + if _, ok := mapB[k]; ok { + return false + } + } + return true +} + +// Entry is a key/value pairs. +type Entry[K comparable, V any] struct { + Key K + Value V +} + +// Entries transforms a map into array of key/value pairs. +// Play: https://go.dev/play/p/Ltb11LNcElY +func Entries[K comparable, V any](m map[K]V) []Entry[K, V] { + entries := make([]Entry[K, V], 0, len(m)) + + for k, v := range m { + entries = append(entries, Entry[K, V]{ + Key: k, + Value: v, + }) + } + + return entries +} + +// FromEntries creates a map based on a slice of key/value pairs +// Play: https://go.dev/play/p/fTdu4sCNjQO +func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V { + result := make(map[K]V, len(entries)) + + for _, v := range entries { + result[v.Key] = v.Value + } + + return result +} + +// Transform a map to another type map. +// Play: https://go.dev/play/p/P6ovfToM3zj +func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 { + result := make(map[K2]V2, len(m)) + + for k1, v1 := range m { + k2, v2 := iteratee(k1, v1) + result[k2] = v2 + } + + return result +} + +// MapKeys transforms a map to other type map by manipulating it's keys. +// Play: https://go.dev/play/p/8scDxWeBDKd +func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V { + result := make(map[T]V, len(m)) + + for k, v := range m { + result[iteratee(k, v)] = v + } + + return result +} + +// MapValues transforms a map to other type map by manipulating it's values. +// Play: https://go.dev/play/p/g92aY3fc7Iw +func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T { + result := make(map[K]T, len(m)) + + for k, v := range m { + result[k] = iteratee(k, v) + } + + return result +} + +// HasKey checks if map has key or not. +// This function is used to replace the following boilerplate code: +// _, haskey := amap["baz"]; +// +// if haskey { +// fmt.Println("map has key baz") +// } +// +// Play: https://go.dev/play/p/isZZHOsDhFc +func HasKey[K comparable, V any](m map[K]V, key K) bool { + _, haskey := m[key] + return haskey +} + +// MapToStruct converts map to struct +// Play: https://go.dev/play/p/7wYyVfX38Dp +func MapToStruct(m map[string]any, structObj any) error { + for k, v := range m { + err := setStructField(structObj, k, v) + if err != nil { + return err + } + } + + return nil +} + +func setStructField(structObj any, fieldName string, fieldValue any) error { + structVal := reflect.ValueOf(structObj).Elem() + + fName := getFieldNameByJsonTag(structObj, fieldName) + if fName == "" { + return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName) + } + + fieldVal := structVal.FieldByName(fName) + + if !fieldVal.IsValid() { + return fmt.Errorf("No such field: %s in obj", fieldName) + } + + if !fieldVal.CanSet() { + return fmt.Errorf("Cannot set %s field value", fieldName) + } + + val := reflect.ValueOf(fieldValue) + + if fieldVal.Type() != val.Type() { + + if val.CanConvert(fieldVal.Type()) { + fieldVal.Set(val.Convert(fieldVal.Type())) + return nil + } + + if m, ok := fieldValue.(map[string]any); ok { + + if fieldVal.Kind() == reflect.Struct { + return MapToStruct(m, fieldVal.Addr().Interface()) + } + + if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct { + if fieldVal.IsNil() { + fieldVal.Set(reflect.New(fieldVal.Type().Elem())) + } + + return MapToStruct(m, fieldVal.Interface()) + } + + } + + return fmt.Errorf("Map value type don't match struct field type") + } + + fieldVal.Set(val) + + return nil +} + +func getFieldNameByJsonTag(structObj any, jsonTag string) string { + s := reflect.TypeOf(structObj).Elem() + + for i := 0; i < s.NumField(); i++ { + field := s.Field(i) + tag := field.Tag + name, _, _ := strings.Cut(tag.Get("json"), ",") + if name == jsonTag { + return field.Name + } + } + + return "" +} + +// ToSortedSlicesDefault converts a map to two slices sorted by key: one for the keys and another for the values. +// Play: https://go.dev/play/p/43gEM2po-qy +func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) { + keys := make([]K, 0, len(m)) + + // store the map’s keys into a slice + for k := range m { + keys = append(keys, k) + } + + // sort the slice of keys + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + + // adjust the order of values according to the sorted keys + sortedValues := make([]V, len(keys)) + for i, k := range keys { + sortedValues[i] = m[k] + } + + return keys, sortedValues +} + +// ToSortedSlicesWithComparator converts a map to two slices sorted by key and using a custom comparison function: +// one for the keys and another for the values. +// Play: https://go.dev/play/p/0nlPo6YLdt3 +func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) { + keys := make([]K, 0, len(m)) + + // store the map’s keys into a slice + for k := range m { + keys = append(keys, k) + } + + // sort the key slice using the provided comparison function + sort.Slice(keys, func(i, j int) bool { + return comparator(keys[i], keys[j]) + }) + + // adjust the order of values according to the sorted keys + sortedValues := make([]V, len(keys)) + for i, k := range keys { + sortedValues[i] = m[k] + } + + return keys, sortedValues +} + +// GetOrSet returns value of the given key or set the given value value if not present. +// Play: https://go.dev/play/p/IVQwO1OkEJC +func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V { + if v, ok := m[key]; ok { + return v + } + + m[key] = value + + return value +} + +// SortByKey sorts the map by its keys and returns a new map with sorted keys. +// Play: https://go.dev/play/p/PVdmBSnm6P_W +func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) (sortedKeysMap map[K]V) { + keys := make([]K, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { + return less(keys[i], keys[j]) + }) + + sortedKeysMap = make(map[K]V, len(m)) + for _, k := range keys { + sortedKeysMap[k] = m[k] + } + + return +} + +var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{ + reflect.String: convertNormal, + reflect.Int: convertNormal, + reflect.Int16: convertNormal, + reflect.Int32: convertNormal, + reflect.Int64: convertNormal, + reflect.Uint: convertNormal, + reflect.Uint16: convertNormal, + reflect.Uint32: convertNormal, + reflect.Uint64: convertNormal, + reflect.Float32: convertNormal, + reflect.Float64: convertNormal, + reflect.Uint8: convertNormal, + reflect.Int8: convertNormal, + reflect.Struct: convertNormal, + reflect.Complex64: convertNormal, + reflect.Complex128: convertNormal, +} + +var _ = func() struct{} { + mapHandlers[reflect.Map] = convertMap + mapHandlers[reflect.Array] = convertSlice + mapHandlers[reflect.Slice] = convertSlice + return struct{}{} +}() + +// MapTo try to map any interface to struct or base type +/* + Eg: + v := map[string]interface{}{ + "service":map[string]interface{}{ + "ip":"127.0.0.1", + "port":1234, + }, + version:"v1.0.01" + } + + type Target struct { + Service struct { + Ip string `json:"ip"` + Port int `json:"port"` + } `json:"service"` + Ver string `json:"version"` + } + + var dist Target + if err := maputil.MapTo(v,&dist); err != nil { + log.Println(err) + return + } + + log.Println(dist) + +*/ +// Play: https://go.dev/play/p/4K7KBEPgS5M +func MapTo(src any, dst any) error { + dstRef := reflect.ValueOf(dst) + + if dstRef.Kind() != reflect.Ptr { + return fmt.Errorf("dst is not ptr") + } + + dstElem := dstRef.Type().Elem() + if dstElem.Kind() == reflect.Struct { + srcMap := src.(map[string]interface{}) + return MapToStruct(srcMap, dst) + } + + dstRef = reflect.Indirect(dstRef) + + srcRef := reflect.ValueOf(src) + if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface { + srcRef = srcRef.Elem() + } + + if f, ok := mapHandlers[srcRef.Kind()]; ok { + return f(srcRef, dstRef) + } + + return fmt.Errorf("no implemented:%s", srcRef.Type()) +} + +func convertNormal(src reflect.Value, dst reflect.Value) error { + if dst.CanSet() { + if src.Type() == dst.Type() { + dst.Set(src) + } else if src.CanConvert(dst.Type()) { + dst.Set(src.Convert(dst.Type())) + } else { + return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String()) + } + } + return nil +} + +func convertSlice(src reflect.Value, dst reflect.Value) error { + if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice { + return fmt.Errorf("error type:%s", dst.Type().String()) + } + l := src.Len() + target := reflect.MakeSlice(dst.Type(), l, l) + if dst.CanSet() { + dst.Set(target) + } + for i := 0; i < l; i++ { + srcValue := src.Index(i) + if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface { + srcValue = srcValue.Elem() + } + if f, ok := mapHandlers[srcValue.Kind()]; ok { + err := f(srcValue, dst.Index(i)) + if err != nil { + return err + } + } + } + + return nil +} + +func convertMap(src reflect.Value, dst reflect.Value) error { + if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct { + if src.Kind() == reflect.Interface && dst.IsValid() { + return convertMap(src.Elem(), dst) + } else { + return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String()) + } + } + dstType := dst.Type() + num := dstType.NumField() + + exist := map[string]int{} + + for i := 0; i < num; i++ { + k := dstType.Field(i).Tag.Get("json") + if k == "" { + k = dstType.Field(i).Name + } + if strings.Contains(k, ",") { + taglist := strings.Split(k, ",") + if taglist[0] == "" { + k = dstType.Field(i).Name + } else { + k = taglist[0] + + } + + } + exist[k] = i + } + + keys := src.MapKeys() + + for _, key := range keys { + if index, ok := exist[key.String()]; ok { + v := dst.Field(index) + + if v.Kind() == reflect.Struct { + err := convertMap(src.MapIndex(key), v) + if err != nil { + return err + } + } else { + if v.CanSet() { + if v.Type() == src.MapIndex(key).Elem().Type() { + v.Set(src.MapIndex(key).Elem()) + } else if src.MapIndex(key).Elem().CanConvert(v.Type()) { + v.Set(src.MapIndex(key).Elem().Convert(v.Type())) + } else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil { + err := f(src.MapIndex(key).Elem(), v) + if err != nil { + return err + } + } else { + return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type()) + } + } + } + } + } + + return nil +} + +// GetOrDefault returns the value of the given key or a default value if the key is not present. +// Play: https://go.dev/play/p/99QjSYSBdiM +func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V { + if v, ok := m[key]; ok { + return 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 +} diff --git a/maputil/map_example_test.go b/maputil/map_example_test.go new file mode 100644 index 00000000..cff20e22 --- /dev/null +++ b/maputil/map_example_test.go @@ -0,0 +1,853 @@ +package maputil + +import ( + "fmt" + "sort" + "strconv" + "time" +) + +func ExampleKeys() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + keys := Keys(m) + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [1 2 3 4 5] +} + +func ExampleValues() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + values := Values(m) + sort.Strings(values) + + fmt.Println(values) + + // Output: + // [a a b c d] +} + +func ExampleKeysBy() { + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + } + + keys := KeysBy(m, func(n int) int { + return n + 1 + }) + + sort.Ints(keys) + + fmt.Println(keys) + + // Output: + // [2 3 4] +} + +func ExampleValuesBy() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + values := ValuesBy(m, func(v string) string { + switch v { + case "a": + return "a-1" + case "b": + return "b-2" + case "c": + return "c-3" + default: + return "" + } + }) + + sort.Strings(values) + + fmt.Println(values) + + // Output: + // [a-1 b-2 c-3] +} +func ExampleMerge() { + m1 := map[int]string{ + 1: "a", + 2: "b", + } + m2 := map[int]string{ + 1: "c", + 3: "d", + } + + result := Merge(m1, m2) + + fmt.Println(result) + + // Output: + // map[1:c 2:b 3:d] +} + +func ExampleForEach() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + } + + var sum int + ForEach(m, func(_ string, value int) { + sum += value + }) + + fmt.Println(sum) + + // Output: + // 10 +} + +func ExampleFilter() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + result := Filter(m, isEven) + + fmt.Println(result) + + // Output: + // map[b:2 d:4] +} + +func ExampleFilterByKeys() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := FilterByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2] +} + +func ExampleFilterByValues() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := FilterByValues(m, []int{3, 4}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4] +} + +func ExampleIntersect() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 1, + "b": 2, + "c": 6, + "d": 7, + } + + m3 := map[string]int{ + "a": 1, + "b": 9, + "e": 9, + } + + result1 := Intersect(m1) + result2 := Intersect(m1, m2) + result3 := Intersect(m1, m2, m3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // map[a:1 b:2 c:3] + // map[a:1 b:2] + // map[a:1] +} + +func ExampleMinus() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 11, + "b": 22, + "d": 33, + } + + result := Minus(m1, m2) + + fmt.Println(result) + + // Output: + // map[c:3] +} + +func ExampleIsDisjoint() { + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "d": 22, + } + + m3 := map[string]int{ + "a": 22, + } + + result1 := IsDisjoint(m1, m2) + result2 := IsDisjoint(m1, m3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleEntries() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Entries(m) + + sort.Slice(result, func(i, j int) bool { + return result[i].Value < result[j].Value + }) + + fmt.Println(result) + + // Output: + // [{a 1} {b 2} {c 3}] +} + +func ExampleFromEntries() { + + result := FromEntries([]Entry[string, int]{ + {Key: "a", Value: 1}, + {Key: "b", Value: 2}, + {Key: "c", Value: 3}, + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} + +func ExampleTransform() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Transform(m, func(k string, v int) (string, string) { + return k, strconv.Itoa(v) + }) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} + +func ExampleMapKeys() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := MapKeys(m, func(k int, _ string) string { + return strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c] +} + +func ExampleMapValues() { + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := MapValues(m, func(k int, v string) string { + return v + strconv.Itoa(k) + }) + + fmt.Println(result) + + // Output: + // map[1:a1 2:b2 3:c3] +} + +func ExampleOmitBy() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + result := OmitBy(m, isEven) + + fmt.Println(result) + + // Output: + // map[a:1 c:3 e:5] +} + +func ExampleOmitByKeys() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := OmitByKeys(m, []string{"a", "b"}) + + fmt.Println(result) + + // Output: + // map[c:3 d:4 e:5] +} + +func ExampleOmitByValues() { + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + result := OmitByValues(m, []int{4, 5}) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} + +func ExampleMapTo() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} + +func ExampleHasKey() { + m := map[string]int{ + "a": 1, + "b": 2, + } + + result1 := HasKey(m, "a") + result2 := HasKey(m, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleMapToStruct() { + + personReqMap := map[string]any{ + "name": "Nothin", + "max_age": 35, + "page": 1, + "pageSize": 10, + } + + type PersonReq struct { + Name string `json:"name"` + MaxAge int `json:"max_age"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + } + var personReq PersonReq + _ = MapToStruct(personReqMap, &personReq) + fmt.Println(personReq) + + // Output: + // {Nothin 35 1 10} +} + +func ExampleToSortedSlicesDefault() { + m := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + + keys, values := ToSortedSlicesDefault(m) + + fmt.Println(keys) + fmt.Println(values) + + // Output: + // [1 2 3] + // [a b c] +} + +func ExampleToSortedSlicesWithComparator() { + m1 := map[time.Time]string{ + time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today", + time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday", + time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow", + } + + keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool { + return a.Before(b) + }) + + m2 := map[int]string{ + 1: "a", + 3: "c", + 2: "b", + } + keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool { + return a > b + }) + + fmt.Println(keys1) + fmt.Println(values1) + + fmt.Println(keys2) + fmt.Println(values2) + + // Output: + // [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC] + // [yesterday today tomorrow] + // [3 2 1] + // [c b a] +} + +func ExampleGetOrSet() { + m := map[int]string{ + 1: "a", + } + + result1 := GetOrSet(m, 1, "1") + result2 := GetOrSet(m, 2, "b") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // a + // b +} + +func ExampleSortByKey() { + m := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + + result := SortByKey(m, func(a, b int) bool { + return a < b + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:b 3:c 4:d] +} + +func ExampleOrderedMap_Set() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} + +func ExampleOrderedMap_Get() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} + +func ExampleOrderedMap_Front() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} + +func ExampleOrderedMap_Back() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + backElement, ok := om.Back() + fmt.Println(backElement) + fmt.Println(ok) + + // Output: + // {c 3} + // true +} + +func ExampleOrderedMap_Delete() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} + +func ExampleOrderedMap_Clear() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} + +func ExampleOrderedMap_Len() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} + +func ExampleOrderedMap_Keys() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} + +func ExampleOrderedMap_Values() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} + +func ExampleOrderedMap_Contains() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleOrderedMap_Range() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} + +func ExampleOrderedMap_Elements() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} + +func ExampleOrderedMap_Iter() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} + +func ExampleOrderedMap_ReverseIter() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} + +func ExampleOrderedMap_SortByKey() { + om := NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} + +func ExampleOrderedMap_MarshalJSON() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} + +func ExampleOrderedMap_UnmarshalJSON() { + // om := NewOrderedMap[string, int]() + + // data := []byte(`{"a":1,"b":2,"c":3}`) + + // om.UnmarshalJSON(data) + + // 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] +} diff --git a/maputil/map_test.go b/maputil/map_test.go new file mode 100644 index 00000000..b85fc47a --- /dev/null +++ b/maputil/map_test.go @@ -0,0 +1,928 @@ +package maputil + +import ( + "math/cmplx" + "sort" + "strconv" + "testing" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestKeys(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestKeys") + + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + keys := Keys(m) + sort.Ints(keys) + + assert.Equal([]int{1, 2, 3, 4, 5}, keys) +} + +func TestValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestValues") + + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + 4: "c", + 5: "d", + } + + values := Values(m) + sort.Strings(values) + + assert.Equal([]string{"a", "a", "b", "c", "d"}, values) +} + +func TestKeysBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestKeysBy") + + m := map[int]string{ + 1: "a", + 2: "a", + 3: "b", + } + + keys := KeysBy(m, func(n int) int { + return n + 1 + }) + + sort.Ints(keys) + + assert.Equal([]int{2, 3, 4}, keys) +} + +func TestValuesBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestValuesBy") + + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + values := ValuesBy(m, func(v string) string { + switch v { + case "a": + return "a-1" + case "b": + return "b-2" + case "c": + return "c-3" + default: + return "" + } + }) + + sort.Strings(values) + + assert.Equal([]string{"a-1", "b-2", "c-3"}, values) +} + +func TestMerge(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMerge") + + m1 := map[int]string{ + 1: "a", + 2: "b", + } + m2 := map[int]string{ + 2: "c", + 3: "d", + } + + expected := map[int]string{ + 1: "a", + 2: "c", + 3: "d", + } + acturl := Merge(m1, m2) + + assert.Equal(expected, acturl) +} + +func TestForEach(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestForEach") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + } + + var sum int + + ForEach(m, func(_ string, value int) { + sum += value + }) + + assert.Equal(10, sum) +} + +func TestFilter(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilter") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + acturl := Filter(m, isEven) + + assert.Equal(map[string]int{ + "b": 2, + "d": 4, + }, acturl) +} + +func TestFilterByKeys(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilterByKeys") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + acturl := FilterByKeys(m, []string{"a", "b"}) + + assert.Equal(map[string]int{ + "a": 1, + "b": 2, + }, acturl) +} + +func TestFilterByValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilterByValues") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + acturl := FilterByValues(m, []int{3, 4}) + + assert.Equal(map[string]int{ + "c": 3, + "d": 4, + }, acturl) +} + +func TestOmitBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOmitBy") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + isEven := func(_ string, value int) bool { + return value%2 == 0 + } + + acturl := OmitBy(m, isEven) + + assert.Equal(map[string]int{ + "a": 1, + "c": 3, + "e": 5, + }, acturl) +} + +func TestOmitByKeys(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOmitByKeys") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + acturl := OmitByKeys(m, []string{"a", "b"}) + + assert.Equal(map[string]int{ + "c": 3, + "d": 4, + "e": 5, + }, acturl) +} + +func TestOmitByValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOmitByValues") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + } + + acturl := OmitByValues(m, []int{4, 5}) + + assert.Equal(map[string]int{ + "a": 1, + "b": 2, + "c": 3, + }, acturl) +} + +func TestIntersect(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIntersect") + + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 1, + "b": 2, + "c": 6, + "d": 7, + } + + m3 := map[string]int{ + "a": 1, + "b": 9, + "e": 9, + } + + assert.Equal(map[string]int{"a": 1, "b": 2, "c": 3}, Intersect(m1)) + assert.Equal(map[string]int{"a": 1, "b": 2}, Intersect(m1, m2)) + assert.Equal(map[string]int{"a": 1}, Intersect(m1, m2, m3)) +} + +func TestMinus(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMinus") + + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "a": 11, + "b": 22, + "d": 33, + } + + assert.Equal(map[string]int{"c": 3}, Minus(m1, m2)) +} + +func TestIsDisjoint(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMinus") + + m1 := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + m2 := map[string]int{ + "d": 22, + } + + assert.Equal(true, IsDisjoint(m1, m2)) + + m3 := map[string]int{ + "a": 22, + } + + assert.Equal(false, IsDisjoint(m1, m3)) +} + +func TestEntries(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEntries") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Entries(m) + + sort.Slice(result, func(i, j int) bool { + return result[i].Value < result[j].Value + }) + + expected := []Entry[string, int]{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}} + + assert.Equal(expected, result) +} + +func TestFromEntries(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromEntries") + + result := FromEntries([]Entry[string, int]{ + {Key: "a", Value: 1}, + {Key: "b", Value: 2}, + {Key: "c", Value: 3}, + }) + + assert.Equal(3, len(result)) + assert.Equal(1, result["a"]) + assert.Equal(2, result["b"]) + assert.Equal(3, result["c"]) +} + +func TestTransform(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTransform") + + m := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + result := Transform(m, func(k string, v int) (string, string) { + return k, strconv.Itoa(v) + }) + + expected := map[string]string{ + "a": "1", + "b": "2", + "c": "3", + } + + assert.Equal(expected, result) +} + +func TestMapKeys(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapKeys") + + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := MapKeys(m, func(k int, _ string) string { + return strconv.Itoa(k) + }) + + expected := map[string]string{ + "1": "a", + "2": "b", + "3": "c", + } + + assert.Equal(expected, result) +} + +func TestMapValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapKeys") + + m := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + + result := MapValues(m, func(k int, v string) string { + return v + strconv.Itoa(k) + }) + + expected := map[int]string{ + 1: "a1", + 2: "b2", + 3: "c3", + } + + assert.Equal(expected, result) +} + +func TestHasKey(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHasKey") + + m := map[string]int{ + "a": 1, + "b": 2, + } + + assert.Equal(true, HasKey(m, "a")) + assert.Equal(false, HasKey(m, "c")) +} + +func TestMapToStruct(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapToStruct") + + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr *Address `json:"address,omitempty"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + m := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapToStruct(m, &p) + assert.IsNil(err) + assert.Equal(m["name"], p.Name) + assert.Equal(m["age"], p.Age) + assert.Equal(m["phone"], p.Phone) + assert.Equal("test", p.Addr.Street) + assert.Equal(1, p.Addr.Number) +} + +func TestToSortedSlicesDefault(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToSortedSlicesDefault") + + testCases1 := []struct { + name string + inputMap map[string]any + expKeys []string + expValues []any + }{ + { + name: "Empty Map", + inputMap: map[string]any{}, + expKeys: []string{}, + expValues: []any{}, + }, + { + name: "Unsorted Map", + inputMap: map[string]any{"c": 3, "a": 1.6, "b": 2}, + expKeys: []string{"a", "b", "c"}, + expValues: []any{1.6, 2, 3}, + }, + } + + for _, tc := range testCases1 { + t.Run(tc.name, func(t *testing.T) { + keys, values := ToSortedSlicesDefault(tc.inputMap) + assert.Equal(tc.expKeys, keys) + assert.Equal(tc.expValues, values) + }) + } + + testCases2 := map[int64]string{ + 7: "seven", + 3: "three", + 5: "five", + } + actualK2, actualV2 := ToSortedSlicesDefault(testCases2) + case2Keys := []int64{3, 5, 7} + case2Values := []string{"three", "five", "seven"} + assert.Equal(case2Keys, actualK2) + assert.Equal(case2Values, actualV2) +} + +func TestToSortedSlicesWithComparator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToSortedSlicesWithComparator") + + type Account struct { + ID int + Name string + CreateTime time.Time + FavorComplexNumber complex128 + Signal chan struct{} + } + + type testCase struct { + name string + inputMap map[Account]any + lessFunc func(a, b Account) bool + expKeys []Account + expValues []any + } + + now := time.Now() + tomorrow := now.AddDate(0, 0, 1) + yesterday := now.AddDate(0, 0, -1) + + account1 := Account{ID: 1, Name: "cya", CreateTime: now, FavorComplexNumber: complex(1.2, 3), + Signal: make(chan struct{}, 10)} + account2 := Account{ID: 2, Name: "Cannian", CreateTime: tomorrow, FavorComplexNumber: complex(1.2, 2), + Signal: make(chan struct{}, 7)} + account3 := Account{ID: 3, Name: "Clauviou", CreateTime: yesterday, FavorComplexNumber: complex(3, 4), + Signal: make(chan struct{}, 3)} + account1.Signal <- struct{}{} + account2.Signal <- struct{}{} + account2.Signal <- struct{}{} + + testCases := []testCase{ + { + name: "Sorted by Account ID", + inputMap: map[Account]any{ + account1: 100, + account2: 200, + account3: 300, + }, + lessFunc: func(a, b Account) bool { return a.ID < b.ID }, + expKeys: []Account{account1, account2, account3}, + expValues: []any{100, 200, 300}, + }, + { + name: "Reverse Sorted by Account ID", + inputMap: map[Account]any{ + account1: 100, + account2: 200, + account3: 300, + }, + lessFunc: func(a, b Account) bool { return a.ID > b.ID }, + expKeys: []Account{account3, account2, account1}, + expValues: []any{300, 200, 100}, + }, + { + name: "Sorted by Account Name", + inputMap: map[Account]any{ + account1: 100, + account2: 200, + account3: 300, + }, + lessFunc: func(a, b Account) bool { return a.Name < b.Name }, + expKeys: []Account{account2, account3, account1}, + expValues: []any{200, 300, 100}, + }, + { + name: "Empty Map", + inputMap: map[Account]any{}, + lessFunc: func(a, b Account) bool { return a.ID < b.ID }, + expKeys: []Account{}, + expValues: []any{}, + }, + { + name: "Sorted by Account CreateTime", + inputMap: map[Account]any{ + account1: "now", + account2: "tomorrow", + account3: "yesterday", + }, + lessFunc: func(a, b Account) bool { return a.CreateTime.Before(b.CreateTime) }, + expKeys: []Account{account3, account1, account2}, + expValues: []any{"yesterday", "now", "tomorrow"}, + }, + { + name: "Sorted by Account FavorComplexNumber", + inputMap: map[Account]any{ + account1: "1.2+3i", + account2: "1.2+2i", + account3: "3+4i", + }, + lessFunc: func(a, b Account) bool { return cmplx.Abs(a.FavorComplexNumber) < cmplx.Abs(b.FavorComplexNumber) }, + expKeys: []Account{account2, account1, account3}, + expValues: []any{"1.2+2i", "1.2+3i", "3+4i"}, + }, + { + name: "Sort by the buffer capacity of the channel", + inputMap: map[Account]any{ + account1: 10, + account2: 7, + account3: 3, + }, + lessFunc: func(a, b Account) bool { + return cap(a.Signal) < cap(b.Signal) + }, + expKeys: []Account{account3, account2, account1}, + expValues: []any{3, 7, 10}, + }, + { + name: "Sort by the number of elements in the channel", + inputMap: map[Account]any{ + account1: 1, + account2: 2, + account3: 0, + }, + lessFunc: func(a, b Account) bool { return len(a.Signal) < len(b.Signal) }, + expKeys: []Account{account3, account1, account2}, + expValues: []any{0, 1, 2}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + keys, values := ToSortedSlicesWithComparator(tc.inputMap, tc.lessFunc) + assert.Equal(tc.expKeys, keys) + assert.Equal(tc.expValues, values) + }) + } +} + +func TestGetOrSet(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGetOrSet") + + m := map[int]string{ + 1: "a", + } + + result1 := GetOrSet(m, 1, "1") + result2 := GetOrSet(m, 2, "b") + + assert.Equal("a", result1) + assert.Equal("b", result2) +} + +func TestSortByKey(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSortByKey") + + m1 := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + expected1 := map[int]string{ + 1: "a", + 2: "b", + 3: "c", + 4: "d", + } + + result1 := SortByKey(m1, func(a, b int) bool { + return a < b + }) + + assert.Equal(expected1, result1) + + m2 := map[string]int{ + "c": 3, + "a": 1, + "d": 4, + "b": 2, + } + expected2 := map[string]int{ + "d": 4, + "c": 3, + "b": 2, + "a": 1, + } + + result2 := SortByKey(m2, func(a, b string) bool { + return a > b + }) + + assert.Equal(expected2, result2) +} + +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Address *Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func TestStructType(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStructType") + + src := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(src, &p) + assert.IsNil(err) + + assert.Equal(src["name"], p.Name) + assert.Equal(src["age"], p.Age) + assert.Equal(src["phone"], p.Phone) + assert.Equal("test", p.Address.Street) + assert.Equal(1, p.Address.Number) +} + +func TestBaseType(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBaseType") + + tc := map[string]interface{}{ + "i32": -32, + "i8": -8, + "i16": -16, + "i64": -64, + "i": -1, + "u32": 32, + "u8": 8, + "u16": 16, + "u64": 64, + "u": 1, + "tf": true, + "f32": 1.32, + "f64": 1.64, + "str": "hello mapto", + "complex": 1 + 3i, + } + + type BaseType struct { + I int `json:"i"` + I8 int8 `json:"i8"` + I16 int16 `json:"i16"` + I32 int32 `json:"i32"` + I64 int64 `json:"i64"` + U uint `json:"u"` + U8 uint8 `json:"u8"` + U16 uint16 `json:"u16"` + U32 uint32 `json:"u32"` + U64 uint64 `json:"u64"` + F32 float32 `json:"f32"` + F64 float64 `json:"f64"` + Tf bool `json:"tf"` + Str string `json:"str"` + Comp complex128 `json:"complex"` + } + + var dist BaseType + err := MapTo(tc, &dist) + assert.IsNil(err) + + var number float64 + + MapTo(tc["i"], &number) + assert.EqualValues(-1, number) + + MapTo(tc["i8"], &number) + assert.EqualValues(-8, number) + + MapTo(tc["i16"], &number) + assert.EqualValues(-16, number) + + MapTo(tc["i32"], &number) + assert.EqualValues(-32, number) + + MapTo(tc["i64"], &number) + assert.EqualValues(-64, number) + + MapTo(tc["u"], &number) + assert.EqualValues(1, number) + + MapTo(tc["u8"], &number) + assert.EqualValues(8, number) + + MapTo(tc["u16"], &number) + assert.EqualValues(16, number) + + MapTo(tc["u32"], &number) + assert.EqualValues(32, number) + + MapTo(tc["u64"], &number) + assert.EqualValues(64, number) +} + +func TestGetOrDefault(t *testing.T) { + + t.Parallel() + + assert := internal.NewAssert(t, "GetOrDefault") + + m1 := map[int]string{ + 3: "c", + 1: "a", + 4: "d", + 2: "b", + } + result1 := GetOrDefault(m1, 1, "123") + assert.Equal("a", result1) + + 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) + } +} diff --git a/maputil/orderedmap.go b/maputil/orderedmap.go new file mode 100644 index 00000000..fc450cd0 --- /dev/null +++ b/maputil/orderedmap.go @@ -0,0 +1,431 @@ +package maputil + +import ( + "container/list" + "encoding/json" + "fmt" + "reflect" + "sort" + "strconv" + "sync" +) + +// OrderedMap is a map that maintains the order of keys. +type OrderedMap[K comparable, V any] struct { + mu sync.RWMutex + + data map[K]V + order *list.List + index map[K]*list.Element +} + +// NewOrderedMap creates a new OrderedMap. +func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] { + return &OrderedMap[K, V]{ + data: make(map[K]V), + order: list.New(), + index: make(map[K]*list.Element), + } +} + +// Sets the given key-value pair. +// Play: https://go.dev/play/p/Y4ZJ_oOc1FU +func (om *OrderedMap[K, V]) Set(key K, value V) { + om.mu.Lock() + defer om.mu.Unlock() + + if elem, ok := om.index[key]; ok { + om.data[key] = value + om.order.MoveToBack(elem) + + return + } + + om.data[key] = value + + elem := om.order.PushBack(key) + om.index[key] = elem +} + +// Get returns the value for the given key. +// Play: https://go.dev/play/p/Y4ZJ_oOc1FU +func (om *OrderedMap[K, V]) Get(key K) (V, bool) { + om.mu.RLock() + defer om.mu.RUnlock() + + value, ok := om.data[key] + + return value, ok +} + +// Delete deletes the given key. +// Play: https://go.dev/play/p/5bIi4yaZ3K- +func (om *OrderedMap[K, V]) Delete(key K) { + om.mu.Lock() + defer om.mu.Unlock() + + if elem, ok := om.index[key]; ok { + om.order.Remove(elem) + delete(om.data, key) + delete(om.index, key) + } +} + +// Clear clears the map. +// Play: https://go.dev/play/p/8LwoJyEfuFr +func (om *OrderedMap[K, V]) Clear() { + om.mu.Lock() + defer om.mu.Unlock() + + om.data = make(map[K]V) + om.order.Init() + om.index = make(map[K]*list.Element) +} + +// Front returns the first key-value pair. +// Play: https://go.dev/play/p/ty57XSimpoe +func (om *OrderedMap[K, V]) Front() (struct { + Key K + Value V +}, bool) { + om.mu.RLock() + defer om.mu.RUnlock() + + if elem := om.order.Front(); elem != nil { + key := elem.Value.(K) + value := om.data[key] + + return struct { + Key K + Value V + }{ + Key: key, + Value: value, + }, true + } + + return struct { + Key K + Value V + }{}, false +} + +// Back returns the last key-value pair. +// Play: https://go.dev/play/p/rQMjp1yQmpa +func (om *OrderedMap[K, V]) Back() (struct { + Key K + Value V +}, bool) { + om.mu.RLock() + defer om.mu.RUnlock() + + if elem := om.order.Back(); elem != nil { + key := elem.Value.(K) + value := om.data[key] + + return struct { + Key K + Value V + }{ + Key: key, + Value: value, + }, true + } + + return struct { + Key K + Value V + }{}, false +} + +// Range calls the given function for each key-value pair. +// Play: https://go.dev/play/p/U-KpORhc7LZ +func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) { + om.mu.RLock() + defer om.mu.RUnlock() + + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + key := elem.Value.(K) + value := om.data[key] + + if !iteratee(key, value) { + break + } + } +} + +// Keys returns the keys in order. +// Play: https://go.dev/play/p/Vv_y9ExKclA +func (om *OrderedMap[K, V]) Keys() []K { + om.mu.RLock() + defer om.mu.RUnlock() + + keys := make([]K, 0, len(om.data)) + + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + keys = append(keys, elem.Value.(K)) + } + + return keys +} + +// Values returns the values in order. +// Play: https://go.dev/play/p/TWj5n1-PUfx +func (om *OrderedMap[K, V]) Values() []V { + om.mu.RLock() + defer om.mu.RUnlock() + + values := make([]V, 0, len(om.data)) + + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + key := elem.Value.(K) + values = append(values, om.data[key]) + } + + return values +} + +// Len returns the number of key-value pairs. +// Play: https://go.dev/play/p/cLe6z2VX5N- +func (om *OrderedMap[K, V]) Len() int { + om.mu.RLock() + defer om.mu.RUnlock() + + return len(om.data) +} + +// Contains returns true if the given key exists. +// Play: https://go.dev/play/p/QuwqqnzwDNX +func (om *OrderedMap[K, V]) Contains(key K) bool { + om.mu.RLock() + defer om.mu.RUnlock() + + _, ok := om.data[key] + + return ok +} + +// Elements returns the key-value pairs in order. +// Play: https://go.dev/play/p/4BHG4kKz6bB +func (om *OrderedMap[K, V]) Elements() []struct { + Key K + Value V +} { + om.mu.RLock() + defer om.mu.RUnlock() + + elements := make([]struct { + Key K + Value V + }, 0, len(om.data)) + + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + key := elem.Value.(K) + value := om.data[key] + elements = append(elements, struct { + Key K + Value V + }{Key: key, Value: value}) + } + + return elements +} + +// Iter returns a channel that yields key-value pairs in order. +// Play: https://go.dev/play/p/tlq2tdvicPt +func (om *OrderedMap[K, V]) Iter() <-chan struct { + Key K + Value V +} { + ch := make(chan struct { + Key K + Value V + }) + + go func() { + om.mu.RLock() + defer om.mu.RUnlock() + defer close(ch) + + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + key := elem.Value.(K) + value := om.data[key] + + ch <- struct { + Key K + Value V + }{Key: key, Value: value} + } + }() + + return ch +} + +// ReverseIter returns a channel that yields key-value pairs in reverse order. +// Play: https://go.dev/play/p/8Q0ssg6hZzO +func (om *OrderedMap[K, V]) ReverseIter() <-chan struct { + Key K + Value V +} { + ch := make(chan struct { + Key K + Value V + }) + + go func() { + om.mu.RLock() + defer om.mu.RUnlock() + defer close(ch) + + for elem := om.order.Back(); elem != nil; elem = elem.Prev() { + key := elem.Value.(K) + value := om.data[key] + + ch <- struct { + Key K + Value V + }{Key: key, Value: value} + } + }() + + return ch +} + +// SortByValue sorts the map by key given less function. +// Play: https://go.dev/play/p/N7hjD_alZPq +func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) { + om.mu.Lock() + defer om.mu.Unlock() + + keys := make([]K, 0, om.order.Len()) + for elem := om.order.Front(); elem != nil; elem = elem.Next() { + keys = append(keys, elem.Value.(K)) + } + + sort.Slice(keys, func(i, j int) bool { + return less(keys[i], keys[j]) + }) + + om.order.Init() + om.index = make(map[K]*list.Element) + for _, key := range keys { + elem := om.order.PushBack(key) + om.index[key] = elem + } +} + +// MarshalJSON implements the json.Marshaler interface. +// Play: https://go.dev/play/p/C-wAwydIAC7 +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { + om.mu.RLock() + defer om.mu.RUnlock() + + tempMap := make(map[string]V) + for e := om.order.Front(); e != nil; e = e.Next() { + key := e.Value.(K) + keyStr, err := keyToString(key) + if err != nil { + return nil, err + } + tempMap[keyStr] = om.data[key] + } + + return json.Marshal(tempMap) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Play: https://go.dev/play/p/8C3MvJ3-mut +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error { + om.mu.Lock() + defer om.mu.Unlock() + + tempMap := make(map[string]V) + if err := json.Unmarshal(data, &tempMap); err != nil { + return err + } + + om.data = make(map[K]V) + om.order.Init() + om.index = make(map[K]*list.Element) + + for keyStr, value := range tempMap { + key, err := stringToKey[K](keyStr) + if err != nil { + return err + } + om.data[key] = value + elem := om.order.PushBack(key) + om.index[key] = elem + } + + return nil +} + +func keyToString[K any](key K) (string, error) { + switch v := any(key).(type) { + case int: + return strconv.Itoa(v), nil + case float64: + return strconv.FormatFloat(v, 'f', -1, 64), nil + case string: + return v, nil + default: + // 使用反射将未知类型转换为字符串 + rv := reflect.ValueOf(key) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10), nil + case reflect.Float32, reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil + case reflect.String: + return rv.String(), nil + default: + return "", fmt.Errorf("unsupported key type: %T", key) + } + } +} + +func stringToKey[K any](s string) (K, error) { + var zero K + switch any(zero).(type) { + case int: + value, err := strconv.Atoi(s) + return any(value).(K), err + case float64: + value, err := strconv.ParseFloat(s, 64) + return any(value).(K), err + case string: + return any(s).(K), nil + default: + // 使用反射恢复未知类型的键 + rv := reflect.ValueOf(&zero).Elem() + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return zero, err + } + rv.SetInt(val) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + val, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return zero, err + } + rv.SetUint(val) + case reflect.Float32, reflect.Float64: + val, err := strconv.ParseFloat(s, 64) + if err != nil { + return zero, err + } + rv.SetFloat(val) + case reflect.String: + rv.SetString(s) + default: + return zero, fmt.Errorf("unsupported key type: %T", zero) + } + + return rv.Interface().(K), nil + } +} diff --git a/maputil/orderedmap_test.go b/maputil/orderedmap_test.go new file mode 100644 index 00000000..16539d10 --- /dev/null +++ b/maputil/orderedmap_test.go @@ -0,0 +1,249 @@ +package maputil + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestOrderedMap_Set_Get(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Set_Get") + + val, ok := om.Get("a") + assert.Equal(1, val) + assert.Equal(true, ok) + + om.Set("a", 2) + val, _ = om.Get("a") + assert.Equal(2, val) + + val, ok = om.Get("d") + assert.Equal(false, ok) + assert.Equal(0, val) +} + +func TestOrderedMap_Front_Back(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Front_Back") + + frontElement, ok := om.Front() + assert.Equal("a", frontElement.Key) + assert.Equal(1, frontElement.Value) + assert.Equal(true, ok) + + backElement, ok := om.Back() + assert.Equal("c", backElement.Key) + assert.Equal(3, backElement.Value) + assert.Equal(true, ok) +} + +func TestOrderedMap_Delete_Clear(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Delete_Clear") + + assert.Equal(3, om.Len()) + + om.Delete("b") + assert.Equal(2, om.Len()) + + om.Clear() + assert.Equal(0, om.Len()) +} + +func TestOrderedMap_Keys_Values(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Keys_Values") + + assert.Equal([]string{"a", "b", "c"}, om.Keys()) + assert.Equal([]int{1, 2, 3}, om.Values()) +} + +func TestOrderedMap_Contains(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Contains") + + assert.Equal(true, om.Contains("a")) + assert.Equal(false, om.Contains("d")) +} + +func TestOrderedMap_Eelements(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Eelements") + + elements := []struct { + Key string + Value int + }{ + {"a", 1}, + {"b", 2}, + {"c", 3}, + } + + assert.Equal(elements, om.Elements()) +} + +func TestOrderedMap_Range(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + om.Set("d", 4) + + assert := internal.NewAssert(t, "TestOrderedMap_Range") + + var keys []string + om.Range(func(key string, value int) bool { + keys = append(keys, key) + return key != "c" + }) + + assert.Equal([]string{"a", "b", "c"}, keys) +} + +func TestOrderedMap_Iter(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_Iter") + + var items []struct { + Key string + Value int + } + + iterCh := om.Iter() + + for item := range iterCh { + items = append(items, item) + } + + expected := []struct { + Key string + Value int + }{ + {"a", 1}, + {"b", 2}, + {"c", 3}, + } + + assert.Equal(expected, items) +} + +func TestOrderedMap_ReverseIter(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_ReverseIter") + + var items []struct { + Key string + Value int + } + + iterCh := om.ReverseIter() + + for item := range iterCh { + items = append(items, item) + } + + expected := []struct { + Key string + Value int + }{ + {"c", 3}, + {"b", 2}, + {"a", 1}, + } + + assert.Equal(expected, items) +} + +func TestOrderedMap_SortByKey(t *testing.T) { + assert := internal.NewAssert(t, "TestOrderedMap_SortByKey") + + om := NewOrderedMap[string, int]() + + om.Set("d", 4) + om.Set("b", 2) + om.Set("c", 3) + om.Set("a", 1) + + om.SortByKey(func(a, b string) bool { + return a < b + }) + + assert.Equal([]string{"a", "b", "c", "d"}, om.Keys()) +} + +func TestOrderedMap_MarshalJSON(t *testing.T) { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + assert := internal.NewAssert(t, "TestOrderedMap_MarshalJSON") + + jsonBytes, err := om.MarshalJSON() + + if err != nil { + t.Errorf("MarshalJSON error: %v", err) + } + + assert.Equal(`{"a":1,"b":2,"c":3}`, string(jsonBytes)) +} + +func TestOrderedMap_UnmarshalJSON(t *testing.T) { + assert := internal.NewAssert(t, "TestOrderedMap_UnmarshalJSON") + + jsonStr := `{"a":1,"b":2,"c":3}` + + om := NewOrderedMap[string, int]() + err := om.UnmarshalJSON([]byte(jsonStr)) + if err != nil { + t.Errorf("MarshalJSON error: %v", err) + } + + assert.Equal(3, om.Len()) + assert.Equal(true, om.Contains("a")) + assert.Equal(true, om.Contains("b")) + assert.Equal(true, om.Contains("c")) +} diff --git a/mathutil/mathutil.go b/mathutil/mathutil.go index aada9d24..eac02393 100644 --- a/mathutil/mathutil.go +++ b/mathutil/mathutil.go @@ -9,9 +9,12 @@ import ( "math" "strconv" "strings" + + "golang.org/x/exp/constraints" ) -// Exponent calculate x^n +// Exponent calculate x^n. +// Play: https://go.dev/play/p/uF3HGNPk8wr func Exponent(x, n int64) int64 { if n == 0 { return 1 @@ -26,7 +29,8 @@ func Exponent(x, n int64) int64 { return t * t } -// Fibonacci calculate fibonacci number before n +// Fibonacci calculate fibonacci number before n. +// Play: https://go.dev/play/p/IscseUNMuUc func Fibonacci(first, second, n int) int { if n <= 0 { return 0 @@ -40,45 +44,55 @@ func Fibonacci(first, second, n int) int { } } -// Factorial calculate x! -func Factorial(x uint) uint { - var f uint = 1 - for ; x > 1; x-- { - f *= x +// Factorial calculate n!. +// Play: https://go.dev/play/p/tt6LdOK67Nx +func Factorial(n uint) uint { + if n == 0 || n == 1 { + return 1 } - return f + + result := uint(1) + for i := uint(2); i <= n; i++ { + result *= i + } + + return result } -// Percent calculate the percentage of val to total +// Percent calculate the percentage of value to total. +// Play: https://go.dev/play/p/s0NdFCtwuyd func Percent(val, total float64, n int) float64 { if total == 0 { return float64(0) } tmp := val / total * 100 - res := RoundToFloat(tmp, n) + result := RoundToFloat(tmp, n) - return res + return result } -// RoundToString round up to n decimal places -func RoundToString(x float64, n int) string { +// RoundToString round off to n decimal places. +// Play: https://go.dev/play/p/kZwpBRAcllO +func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string { tmp := math.Pow(10.0, float64(n)) - x *= tmp - x = math.Round(x) - res := strconv.FormatFloat(x/tmp, 'f', n, 64) - return res + x *= T(tmp) + r := math.Round(float64(x)) + result := strconv.FormatFloat(r/tmp, 'f', n, 64) + return result } -// RoundToFloat round up to n decimal places -func RoundToFloat(x float64, n int) float64 { +// RoundToFloat round off to n decimal places. +// Play: https://go.dev/play/p/ghyb528JRJL +func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 { tmp := math.Pow(10.0, float64(n)) - x *= tmp - x = math.Round(x) - return x / tmp + x *= T(tmp) + r := math.Round(float64(x)) + return r / tmp } -// TruncRound round off n decimal places -func TruncRound(x float64, n int) float64 { +// TruncRound round off n decimal places. +// Play: https://go.dev/play/p/aumarSHIGzP +func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T { floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x) temp := strings.Split(floatStr, ".") var newFloat string @@ -87,6 +101,359 @@ func TruncRound(x float64, n int) float64 { } else { newFloat = temp[0] + "." + temp[1][:n] } - res, _ := strconv.ParseFloat(newFloat, 64) - return res + result, _ := strconv.ParseFloat(newFloat, 64) + return T(result) +} + +// FloorToFloat round down to n decimal places. +// Play: https://go.dev/play/p/vbCBrQHZEED +func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 { + tmp := math.Pow(10.0, float64(n)) + x *= T(tmp) + r := math.Floor(float64(x)) + return r / tmp +} + +// FloorToString round down to n decimal places. +// Play: https://go.dev/play/p/Qk9KPd2IdDb +func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string { + tmp := math.Pow(10.0, float64(n)) + x *= T(tmp) + r := math.Floor(float64(x)) + result := strconv.FormatFloat(r/tmp, 'f', n, 64) + return result +} + +// CeilToFloat round up to n decimal places. +// Play: https://go.dev/play/p/8hOeSADZPCo +func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 { + tmp := math.Pow(10.0, float64(n)) + x *= T(tmp) + r := math.Ceil(float64(x)) + return r / tmp +} + +// CeilToString round up to n decimal places. +// Play: https://go.dev/play/p/wy5bYEyUKKG +func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string { + tmp := math.Pow(10.0, float64(n)) + x *= T(tmp) + r := math.Ceil(float64(x)) + result := strconv.FormatFloat(r/tmp, 'f', n, 64) + return result +} + +// Max return max value of numbers. +// Play: https://go.dev/play/p/cN8DHI0rTkH +func Max[T constraints.Ordered](items ...T) T { + if len(items) < 1 { + panic("mathutil.Max: empty list") + } + + max := items[0] + + for _, v := range items { + if max < v { + max = v + } + } + + return max +} + +// MaxBy return the maximum value of a slice using the given comparator function. +// Play: https://go.dev/play/p/pbe2MT-7DV2 +func MaxBy[T any](slice []T, comparator func(T, T) bool) T { + var max T + + if len(slice) == 0 { + return max + } + + max = slice[0] + + for i := 1; i < len(slice); i++ { + val := slice[i] + + if comparator(val, max) { + max = val + } + } + + return max +} + +// Min return min value of numbers. +// Play: https://go.dev/play/p/21BER_mlGUj +func Min[T constraints.Ordered](items ...T) T { + if len(items) < 1 { + panic("mathutil.min: empty list") + } + + min := items[0] + + for _, v := range items { + if min > v { + min = v + } + } + + return min +} + +// MinBy return the minimum value of a slice using the given comparator function. +// Play: https://go.dev/play/p/XuJDKrDdglW +func MinBy[T any](slice []T, comparator func(T, T) bool) T { + var min T + + if len(slice) == 0 { + return min + } + + min = slice[0] + + for i := 1; i < len(slice); i++ { + val := slice[i] + + if comparator(val, min) { + min = val + } + } + + return min +} + +// Sum return sum of passed numbers. +// Play: https://go.dev/play/p/1To2ImAMJA7 +func Sum[T constraints.Integer | constraints.Float](numbers ...T) T { + var sum T + + for _, v := range numbers { + sum += v + } + + return sum +} + +// Average return average value of numbers. +// Play: https://go.dev/play/p/Vv7LBwER-pz +func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 { + var sum float64 + for _, num := range numbers { + sum += float64(num) + } + return sum / float64(len(numbers)) +} + +// Range creates a slice of numbers from start with specified count, element step is 1. +// Play: https://go.dev/play/p/9ke2opxa8ZP +func Range[T constraints.Integer | constraints.Float](start T, count int) []T { + size := count + if count < 0 { + size = -count + } + + result := make([]T, size) + + for i, j := 0, start; i < size; i, j = i+1, j+1 { + result[i] = j + } + + return result +} + +// RangeWithStep creates a slice of numbers from start to end with specified step. +// Play: https://go.dev/play/p/akLWz0EqOSM +func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T { + result := []T{} + + if start >= end || step == 0 { + return result + } + + for i := start; i < end; i += step { + result = append(result, i) + } + + return result +} + +// AngleToRadian converts angle value to radian value. +// Play: https://go.dev/play/p/CIvlICqrHql +func AngleToRadian(angle float64) float64 { + radian := angle * (math.Pi / 180) + return radian +} + +// RadianToAngle converts radian value to angle value. +// Play: https://go.dev/play/p/dQtmOTUOMgi +func RadianToAngle(radian float64) float64 { + angle := radian * (180 / math.Pi) + return angle +} + +// PointDistance get two points distance. +// Play: https://go.dev/play/p/RrG4JIaziM8 +func PointDistance(x1, y1, x2, y2 float64) float64 { + a := x1 - x2 + b := y1 - y2 + c := math.Pow(a, 2) + math.Pow(b, 2) + + return math.Sqrt(c) +} + +// IsPrime checks if number is prime number. +// Play: https://go.dev/play/p/Rdd8UTHZJ7u +func IsPrime(n int) bool { + if n < 2 { + return false + } + + for i := 2; i <= int(math.Sqrt(float64(n))); i++ { + if n%i == 0 { + return false + } + } + + return true +} + +// GCD return greatest common divisor (GCD) of integers. +// Play: https://go.dev/play/p/CiEceLSoAKB +func GCD[T constraints.Integer](integers ...T) T { + result := integers[0] + + for k := range integers { + result = gcd(integers[k], result) + + if result == 1 { + return 1 + } + } + + return result +} + +// find greatest common divisor (GCD) +func gcd[T constraints.Integer](a, b T) T { + if b == 0 { + return a + } + + return gcd(b, a%b) +} + +// LCM return Least Common Multiple (LCM) of integers. +// Play: https://go.dev/play/p/EjcZxfY7G_g +func LCM[T constraints.Integer](integers ...T) T { + result := integers[0] + + for k := range integers { + result = lcm(integers[k], result) + } + + return result +} + +// find Least Common Multiple (LCM) via GCD. +func lcm[T constraints.Integer](a, b T) T { + if a == 0 || b == 0 { + panic("lcm function: provide non zero integers only.") + } + return a * b / gcd(a, b) +} + +// Cos returns the cosine of the radian argument. +// Play: https://go.dev/play/p/Sm89LoIfvFq +func Cos(radian float64, precision ...int) float64 { + t := 1.0 / (2.0 * math.Pi) + radian *= t + radian -= 0.25 + math.Floor(radian+0.25) + radian *= 16.0 * (math.Abs(radian) - 0.5) + radian += 0.225 * radian * (math.Abs(radian) - 1.0) + + if len(precision) == 1 { + return TruncRound(radian, precision[0]) + } + + return TruncRound(radian, 3) +} + +// Sin returns the sine of the radian argument. +// Play: https://go.dev/play/p/TWMQlMywDsP +func Sin(radian float64, precision ...int) float64 { + return Cos((math.Pi/2)-radian, precision...) +} + +// Log returns the logarithm of base n. +// Play: https://go.dev/play/p/_d4bi8oyhat +func Log(n, base float64) float64 { + return math.Log(n) / math.Log(base) +} + +// Abs returns the absolute value of x. +// Play: https://go.dev/play/p/fsyBh1Os-1d +func Abs[T constraints.Integer | constraints.Float](x T) T { + if x < 0 { + return (-x) + } + + return x +} + +// Div returns the result of x divided by y. +// Play: https://go.dev/play/p/WLxDdGXXYat +func Div[T constraints.Float | constraints.Integer](x T, y T) float64 { + return float64(x) / float64(y) +} + +// Variance returns the variance of numbers. +// Play: https://go.dev/play/p/uHuV4YgXf8F +func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 { + n := len(numbers) + if n == 0 { + return 0 + } + + avg := Average(numbers...) + var sum float64 + + for _, v := range numbers { + sum += (float64(v) - avg) * (float64(v) - avg) + } + + return sum / float64(n) +} + +// StdDev returns the standard deviation of numbers. +// 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: https://go.dev/play/p/MgobwH_FOxj +func Permutation(n, k uint) uint { + if n < k { + return 0 + } + + nFactorial := Factorial(n) + nMinusKFactorial := Factorial(n - k) + + return nFactorial / nMinusKFactorial +} + +// Combination calculate C(n, k). +// Play: https://go.dev/play/p/ENFQRDQUFi9 +func Combination(n, k uint) uint { + if n < k { + return 0 + } + + nFactorial := Factorial(n) + kFactorial := Factorial(k) + nMinusKFactorial := Factorial(n - k) + + return nFactorial / (kFactorial * nMinusKFactorial) } diff --git a/mathutil/mathutil_exmaple_test.go b/mathutil/mathutil_exmaple_test.go new file mode 100644 index 00000000..ed840acb --- /dev/null +++ b/mathutil/mathutil_exmaple_test.go @@ -0,0 +1,528 @@ +package mathutil + +import ( + "fmt" + "math" +) + +func ExampleExponent() { + result1 := Exponent(10, 0) + result2 := Exponent(10, 1) + result3 := Exponent(10, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 10 + // 100 +} + +func ExampleFibonacci() { + result1 := Fibonacci(1, 1, 1) + result2 := Fibonacci(1, 1, 2) + result3 := Fibonacci(1, 1, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 1 + // 5 +} + +func ExampleFactorial() { + result1 := Factorial(1) + result2 := Factorial(2) + result3 := Factorial(3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 6 +} + +func ExamplePercent() { + result1 := Percent(1, 2, 2) + result2 := Percent(0.1, 0.3, 2) + result3 := Percent(-30305, 408420, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 50 + // 33.33 + // -7.42 +} + +func ExampleRoundToFloat() { + result1 := RoundToFloat(0.124, 2) + result2 := RoundToFloat(0.125, 2) + result3 := RoundToFloat(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} + +func ExampleRoundToString() { + result1 := RoundToString(0.124, 2) + result2 := RoundToString(0.125, 2) + result3 := RoundToString(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.13 + // 0.125 +} + +func ExampleTruncRound() { + result1 := TruncRound(0.124, 2) + result2 := TruncRound(0.125, 2) + result3 := TruncRound(0.125, 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.12 + // 0.12 + // 0.125 +} + +func ExampleCeilToFloat() { + result1 := CeilToFloat(3.14159, 1) + result2 := CeilToFloat(3.14159, 2) + result3 := CeilToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5 +} + +func ExampleCeilToString() { + result1 := CeilToString(3.14159, 1) + result2 := CeilToString(3.14159, 2) + result3 := CeilToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.2 + // 3.15 + // 5.0000 +} + +func ExampleFloorToFloat() { + result1 := FloorToFloat(3.14159, 1) + result2 := FloorToFloat(3.14159, 2) + result3 := FloorToFloat(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5 +} + +func ExampleFloorToString() { + result1 := FloorToString(3.14159, 1) + result2 := FloorToString(3.14159, 2) + result3 := FloorToString(5, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3.1 + // 3.14 + // 5.0000 +} + +func ExampleAverage() { + result1 := Average(1, 2) + result2 := RoundToFloat(Average(1.2, 1.4), 1) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1.5 + // 1.3 +} + +func ExampleSum() { + result1 := Sum(1, 2) + result2 := Sum(0.1, float64(1)) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.1 +} + +func ExampleMax() { + result1 := Max(1, 2, 3) + result2 := Max(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 3 + // 1.4 +} + +func ExampleMaxBy() { + result1 := MaxBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result2 := MaxBy([]string{"abd", "abc", "ab"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + result3 := MaxBy([]string{}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abc + // abd + // +} + +func ExampleMin() { + result1 := Min(1, 2, 3) + result2 := Min(1.2, 1.4, 1.1, 1.4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 1.1 +} + +func ExampleMinBy() { + result1 := MinBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result2 := MinBy([]string{"ab", "ac", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + result3 := MinBy([]string{}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // a + // ab + // +} + +func ExampleRange() { + result1 := Range(1, 4) + result2 := Range(1, -4) + result3 := Range(-4, 4) + result4 := Range(1.0, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [-4 -3 -2 -1] + // [1 2 3 4] +} + +func ExampleRangeWithStep() { + result1 := RangeWithStep(1, 4, 1) + result2 := RangeWithStep(1, -1, 0) + result3 := RangeWithStep(-4, 1, 2) + result4 := RangeWithStep(1.0, 4.0, 1.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [1 2 3] + // [] + // [-4 -2 0] + // [1 2.1 3.2] +} + +func ExampleAngleToRadian() { + result1 := AngleToRadian(45) + result2 := AngleToRadian(90) + result3 := AngleToRadian(180) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 0.7853981633974483 + // 1.5707963267948966 + // 3.141592653589793 +} + +func ExampleRadianToAngle() { + result1 := RadianToAngle(math.Pi) + result2 := RadianToAngle(math.Pi / 2) + result3 := RadianToAngle(math.Pi / 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 180 + // 90 + // 45 +} + +func ExamplePointDistance() { + result1 := PointDistance(1, 1, 4, 5) + + fmt.Println(result1) + + // Output: + // 5 +} + +func ExampleIsPrime() { + result1 := IsPrime(-1) + result2 := IsPrime(0) + result3 := IsPrime(1) + result4 := IsPrime(2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} + +func ExampleGCD() { + result1 := GCD(1, 1) + result2 := GCD(1, -1) + result3 := GCD(-1, 1) + result4 := GCD(-1, -1) + result5 := GCD(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // 1 + // -1 + // -1 + // 3 +} + +func ExampleLCM() { + result1 := LCM(1, 1) + result2 := LCM(1, 2) + result3 := LCM(3, 6, 9) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 2 + // 18 +} + +func ExampleCos() { + result1 := Cos(0) + result2 := Cos(90) + result3 := Cos(180) + result4 := Cos(math.Pi) + result5 := Cos(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 1 + // -0.447 + // -0.598 + // -1 + // 0 +} + +func ExampleSin() { + result1 := Sin(0) + result2 := Sin(90) + result3 := Sin(180) + result4 := Sin(math.Pi) + result5 := Sin(math.Pi / 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 0 + // 0.894 + // -0.801 + // 0 + // 1 +} + +func ExampleLog() { + result1 := Log(8, 2) + result2 := TruncRound(Log(5, 2), 2) + result3 := TruncRound(Log(27, 3), 0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 3 + // 2.32 + // 3 +} + +func ExampleAbs() { + result1 := Abs(-1) + result2 := Abs(-0.1) + result3 := Abs(float32(0.2)) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // 1 + // 0.1 + // 0.2 +} + +func ExampleDiv() { + result1 := Div(9, 4) + result2 := Div(1, 2) + result3 := Div(0, 666) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + // Output: + // 2.25 + // 0.5 + // 0 +} + +func ExampleVariance() { + result1 := Variance([]int{1, 2, 3, 4, 5}) + result2 := Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 2 + // 2.42 +} + +func ExampleStdDev() { + result1 := TruncRound(StdDev([]int{1, 2, 3, 4, 5}), 2) + result2 := TruncRound(StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1.41 + // 1.55 +} + +func ExamplePermutation() { + result1 := Permutation(5, 3) + result2 := Permutation(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 60 + // 120 +} + +func ExampleCombination() { + result1 := Combination(5, 3) + result2 := Combination(5, 5) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1 +} diff --git a/mathutil/mathutil_test.go b/mathutil/mathutil_test.go index e2fd1e10..dd079ce7 100644 --- a/mathutil/mathutil_test.go +++ b/mathutil/mathutil_test.go @@ -1,12 +1,15 @@ package mathutil import ( + "math" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestExponent(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestExponent") assert.Equal(int64(1), Exponent(10, 0)) @@ -15,6 +18,8 @@ func TestExponent(t *testing.T) { } func TestFibonacci(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestFibonacci") assert.Equal(0, Fibonacci(1, 1, 0)) @@ -24,6 +29,8 @@ func TestFibonacci(t *testing.T) { } func TestFactorial(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestFactorial") assert.Equal(uint(1), Factorial(0)) @@ -33,40 +40,503 @@ func TestFactorial(t *testing.T) { } func TestPercent(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestPercent") - assert.Equal(float64(50), Percent(1, 2, 2)) - assert.Equal(float64(33.33), Percent(0.1, 0.3, 2)) + assert.EqualValues(50, Percent(1, 2, 2)) + assert.EqualValues(33.33, Percent(0.1, 0.3, 2)) + assert.EqualValues(-7.42, Percent(-30305, 408420, 2)) } func TestRoundToFloat(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRoundToFloat") - assert.Equal(RoundToFloat(0, 0), float64(0)) - assert.Equal(RoundToFloat(0, 1), float64(0)) - assert.Equal(RoundToFloat(0.124, 2), float64(0.12)) - assert.Equal(RoundToFloat(0.125, 2), float64(0.13)) - assert.Equal(RoundToFloat(0.125, 3), float64(0.125)) - assert.Equal(RoundToFloat(33.33333, 2), float64(33.33)) + assert.Equal(float64(0), RoundToFloat(0, 0)) + assert.Equal(float64(0), RoundToFloat(0, 1)) + assert.Equal(0.12, RoundToFloat(0.124, 2)) + assert.Equal(0.13, RoundToFloat(0.125, 2)) + assert.Equal(0.125, RoundToFloat(0.125, 3)) + assert.Equal(33.33, RoundToFloat(33.33333, 2)) } func TestRoundToString(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRoundToString") - assert.Equal(RoundToString(0, 0), "0") - assert.Equal(RoundToString(0, 1), "0.0") - assert.Equal(RoundToString(0.124, 2), "0.12") - assert.Equal(RoundToString(0.125, 2), "0.13") - assert.Equal(RoundToString(0.125, 3), "0.125") + assert.Equal("0", RoundToString(0, 0)) + assert.Equal("0.0", RoundToString(0, 1)) + assert.Equal("0.12", RoundToString(0.124, 2)) + assert.Equal("0.13", RoundToString(0.125, 2)) + assert.Equal("0.125", RoundToString(0.125, 3)) + assert.Equal("54.321", RoundToString(54.321, 3)) + assert.Equal("17.000", RoundToString(17, 3)) } func TestTruncRound(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestTruncRound") - assert.Equal(TruncRound(0, 0), float64(0)) - assert.Equal(TruncRound(0, 1), float64(0)) - assert.Equal(TruncRound(0.124, 2), float64(0.12)) - assert.Equal(TruncRound(0.125, 2), float64(0.12)) - assert.Equal(TruncRound(0.125, 3), float64(0.125)) - assert.Equal(TruncRound(33.33333, 2), float64(33.33)) + assert.Equal(float64(0), TruncRound(float64(0), 0)) + assert.Equal(float64(0), TruncRound(float64(0), 1)) + assert.Equal(float32(0), TruncRound(float32(0), 0)) + assert.Equal(float32(0), TruncRound(float32(0), 1)) + assert.Equal(0, TruncRound(0, 0)) + assert.Equal(uint64(0), TruncRound(uint64(0), 1)) + assert.Equal(0.12, TruncRound(0.124, 2)) + assert.Equal(0.12, TruncRound(0.125, 2)) + assert.Equal(0.125, TruncRound(0.125, 3)) + assert.Equal(33.33, TruncRound(33.33333, 2)) +} + +func TestFloorToFloat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFloorToFloat") + + assert.Equal(3.14, FloorToFloat(3.14159, 2)) + assert.Equal(3.141, FloorToFloat(3.14159, 3)) + assert.Equal(5.0, FloorToFloat(5, 4)) + assert.Equal(2.0, FloorToFloat(9/4, 2)) +} + +func TestFloorToString(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFloorToString") + + assert.Equal("3.14", FloorToString(3.14159, 2)) + assert.Equal("3.141", FloorToString(3.14159, 3)) + assert.Equal("5.0000", FloorToString(5, 4)) +} + +func TestCeilToFloat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCeilToFloat") + + assert.Equal(3.15, CeilToFloat(3.14159, 2)) + assert.Equal(3.142, CeilToFloat(3.14159, 3)) + assert.Equal(5.0, CeilToFloat(5, 4)) + assert.Equal(2.25, CeilToFloat(float32(9)/float32(4), 2)) + assert.Equal(0.15, CeilToFloat(float64(1)/float64(7), 2)) +} + +func TestCeilToString(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCeilToFloat") + + assert.Equal("3.15", CeilToString(3.14159, 2)) + assert.Equal("3.142", CeilToString(3.14159, 3)) + assert.Equal("5.0000", CeilToString(5, 4)) + assert.Equal("2.25", CeilToString(float32(9)/float32(4), 2)) + assert.Equal("0.15", CeilToString(float64(1)/float64(7), 2)) +} + +func TestAverage(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAverage") + + tests := []struct { + numbers []int + expected float64 + }{ + {[]int{0}, 0}, + {[]int{1, 1, 1}, 1}, + {[]int{1, 2, 3, 4}, 2.5}, + {[]int{1, 2, 3, 4, 5}, 3}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Average(tt.numbers...)) + } + + avg := Average(1.1, 1.2, 1.3, 1.4) + assert.Equal(1.25, avg) +} + +func TestSum(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSum") + + assert.Equal(1, Sum(0, 1)) + assert.Equal(1.1, Sum(0.1, float64(1))) +} + +func TestMax(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMax") + + 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))) +} + +func TestMaxBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "MaxBy") + + res1 := MaxBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + assert.Equal("abc", res1) + + res2 := MaxBy([]string{"abd", "abc", "ab"}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + assert.Equal("abd", res2) + + res3 := MaxBy([]string{}, func(v1, v2 string) bool { + return len(v1) > len(v2) + }) + assert.Equal("", res3) +} + +func TestMin(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMin") + + 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) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMinBy") + + res1 := MinBy([]string{"a", "ab", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + assert.Equal("a", res1) + + res2 := MinBy([]string{"ab", "ac", "abc"}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + assert.Equal("ab", res2) + + res3 := MinBy([]string{}, func(v1, v2 string) bool { + return len(v1) < len(v2) + }) + assert.Equal("", res3) +} + +func TestRange(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRange") + + result1 := Range(1, 4) + result2 := Range(1, -4) + result3 := Range(-4, 4) + result4 := Range(1.0, 4) + result5 := Range(1, 0) + + assert.Equal([]int{1, 2, 3, 4}, result1) + assert.Equal([]int{1, 2, 3, 4}, result2) + assert.Equal([]int{-4, -3, -2, -1}, result3) + assert.Equal([]float64{1.0, 2.0, 3.0, 4.0}, result4) + assert.Equal([]int{}, result5) +} + +func TestRangeWithStep(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRangeWithStep") + + result1 := RangeWithStep(1, 4, 1) + result2 := RangeWithStep(1, -1, 0) + result3 := RangeWithStep(-4, 1, 2) + result4 := RangeWithStep(1.0, 4.0, 1.1) + + assert.Equal([]int{1, 2, 3}, result1) + assert.Equal([]int{}, result2) + assert.Equal([]int{-4, -2, 0}, result3) + assert.Equal([]float64{1.0, 2.1, 3.2}, result4) +} + +func TestAngleToRadian(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAngleToRadian") + + result1 := AngleToRadian(45) + result2 := AngleToRadian(90) + result3 := AngleToRadian(180) + + assert.Equal(0.7853981633974483, result1) + assert.Equal(1.5707963267948966, result2) + assert.Equal(3.141592653589793, result3) +} + +func TestRadianToAngle(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAngleToRadian") + + result1 := RadianToAngle(math.Pi) + result2 := RadianToAngle(math.Pi / 2) + result3 := RadianToAngle(math.Pi / 4) + + assert.Equal(float64(180), result1) + assert.Equal(float64(90), result2) + assert.Equal(float64(45), result3) +} + +func TestPointDistance(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPointDistance") + + result1 := PointDistance(1, 1, 4, 5) + + assert.Equal(float64(5), result1) +} + +func TestIsPrime(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsPrime") + + assert.Equal(false, IsPrime(-1)) + assert.Equal(false, IsPrime(0)) + assert.Equal(false, IsPrime(1)) + assert.Equal(true, IsPrime(2)) + assert.Equal(true, IsPrime(3)) + assert.Equal(false, IsPrime(4)) +} + +func TestGCD(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGCD") + + assert.Equal(1, GCD(1, 1)) + assert.Equal(1, GCD(1, -1)) + assert.Equal(-1, GCD(-1, 1)) + assert.Equal(-1, GCD(-1, -1)) + + assert.Equal(1, GCD(1, 0)) + assert.Equal(1, GCD(0, 1)) + assert.Equal(-1, GCD(-1, 0)) + assert.Equal(-1, GCD(0, -1)) + + assert.Equal(1, GCD(1, -2)) + assert.Equal(1, GCD(-2, 1)) + assert.Equal(-1, GCD(-1, 2)) + assert.Equal(-1, GCD(2, -1)) + + assert.Equal(-1, GCD(-1, -2)) + assert.Equal(-1, GCD(-2, -1)) + + assert.Equal(-9, GCD(-27, -36)) + assert.Equal(3, GCD(3, 6, 9)) +} + +func TestLCM(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLCM") + + assert.Equal(1, LCM(1)) + assert.Equal(-1, LCM(-1)) + assert.Equal(-1, LCM(1, -1)) + assert.Equal(1, LCM(-1, 1)) + assert.Equal(1, LCM(1, 1)) + assert.Equal(-1, LCM(-1, -1)) + assert.Equal(2, LCM(1, 2)) + assert.Equal(18, LCM(3, 6, 9)) +} + +func TestCos(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCos") + + assert.EqualValues(1, Cos(0)) + assert.EqualValues(-0.447, Cos(90)) + assert.EqualValues(-0.598, Cos(180)) + assert.EqualValues(-1, Cos(math.Pi)) + assert.EqualValues(0, Cos(math.Pi/2)) +} + +func TestSin(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSin") + + assert.EqualValues(0, Sin(0)) + assert.EqualValues(0.894, Sin(90)) + assert.EqualValues(-0.801, Sin(180)) + assert.EqualValues(0, Sin(math.Pi)) + assert.EqualValues(1, Sin(math.Pi/2)) +} + +func TestLog(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestLog") + + assert.EqualValues(3, Log(8, 2)) + assert.EqualValues(3, TruncRound(Log(27, 3), 0)) + assert.EqualValues(2.32, TruncRound(Log(5, 2), 2)) +} + +func TestAbs(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAbs") + + assert.Equal(0, Abs(0)) + assert.Equal(1, Abs(-1)) + + assert.Equal(0.1, Abs(-0.1)) + + assert.Equal(int64(1), Abs(int64(-1))) + assert.Equal(float32(1), Abs(float32(-1))) + assert.Equal(math.Inf(1), Abs(math.Inf(-1))) +} + +func TestDiv(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDiv") + + assert.Equal(float64(2), Div(4, 2)) + assert.Equal(2.5, Div(5, 2)) + assert.Equal(0.5, Div(1, 2)) + assert.Equal(0.5, Div(1, 2)) + assert.Equal(math.Inf(1), Div(8, 0)) + assert.Equal(math.Inf(-1), Div(-8, 0)) + assert.Equal(true, math.IsNaN(Div(0, 0))) +} + +func TestVariance(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestVariance") + + testIntNumbers := []struct { + numbers []int + expected float64 + }{ + {[]int{0}, 0}, + {[]int{1, 1, 1}, 0}, + {[]int{1, 2, 3, 4}, 1.25}, + {[]int{1, 2, 3, 4, 5}, 2.0}, + } + + for _, tt := range testIntNumbers { + assert.Equal(tt.expected, TruncRound(Variance(tt.numbers), 2)) + } + + testFloatNumbers := []struct { + numbers []float64 + expected float64 + }{ + {[]float64{0}, 0}, + {[]float64{1, 1, 1}, 0}, + {[]float64{1.1, 2.2, 3.3, 4.4}, 1.51}, + } + + for _, tt := range testFloatNumbers { + assert.Equal(tt.expected, TruncRound(Variance(tt.numbers), 2)) + } +} + +func TestStdDev(t *testing.T) { + + t.Parallel() + + assert := internal.NewAssert(t, "TestStdDev") + + testIntNumbers := []struct { + numbers []int + expected float64 + }{ + {[]int{0}, 0}, + {[]int{1, 1, 1}, 0}, + {[]int{1, 2, 3, 4}, 1.118}, + {[]int{1, 2, 3, 4, 5}, 1.414}, + } + + for _, tt := range testIntNumbers { + assert.Equal(tt.expected, TruncRound(StdDev(tt.numbers), 3)) + } + + testFloatNumbers := []struct { + numbers []float64 + expected float64 + }{ + {[]float64{0}, 0}, + {[]float64{1, 1, 1}, 0}, + {[]float64{1.1, 2.2, 3.3, 4.4}, 1.229}, + } + + for _, tt := range testFloatNumbers { + assert.Equal(tt.expected, TruncRound(StdDev(tt.numbers), 3)) + } +} + +func TestPermutation(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPermutation") + + tests := []struct { + n uint + k uint + expected uint + }{ + {1, 1, 1}, + {1, 0, 1}, + {0, 1, 0}, + {0, 0, 1}, + {3, 2, 6}, + {4, 2, 12}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Permutation(tt.n, tt.k)) + } +} + +func TestCombination(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCombination") + + tests := []struct { + n uint + k uint + expected uint + }{ + {1, 1, 1}, + {1, 0, 1}, + {0, 1, 0}, + {0, 0, 1}, + {3, 2, 3}, + {4, 2, 6}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Combination(tt.n, tt.k)) + } } diff --git a/netutil/http.go b/netutil/http.go index c46c8c05..5fa0d194 100644 --- a/netutil/http.go +++ b/netutil/http.go @@ -6,47 +6,58 @@ // HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `url` is required. // HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `params` is variable, the order is: // params[0] is header which type should be http.Header or map[string]string, -// params[1] is query param which type should be url.Values or map[string]interface{}, +// params[1] is query string param which type should be url.Values or map[string]string, when content-type header is +// multipart/form-data or application/x-www-form-urlencoded // params[2] is post body which type should be []byte. // params[3] is http client which type should be http.Client. package netutil import ( + "bytes" + "context" + "crypto/tls" "encoding/json" "errors" "fmt" + "io" + "mime/multipart" "net/http" + "net/url" + "os" "sort" "strings" + "time" + + "github.com/duke-git/lancet/v2/slice" ) -//HttpGet send get http request -func HttpGet(url string, params ...interface{}) (*http.Response, error) { +// HttpGet send get http request. +func HttpGet(url string, params ...any) (*http.Response, error) { return doHttpRequest(http.MethodGet, url, params...) } -//HttpPost send post http request -func HttpPost(url string, params ...interface{}) (*http.Response, error) { +// HttpPost send post http request. +func HttpPost(url string, params ...any) (*http.Response, error) { return doHttpRequest(http.MethodPost, url, params...) } -//HttpPut send put http request -func HttpPut(url string, params ...interface{}) (*http.Response, error) { +// HttpPut send put http request. +func HttpPut(url string, params ...any) (*http.Response, error) { return doHttpRequest(http.MethodPut, url, params...) } -//HttpDelete send delete http request -func HttpDelete(url string, params ...interface{}) (*http.Response, error) { +// HttpDelete send delete http request. +func HttpDelete(url string, params ...any) (*http.Response, error) { return doHttpRequest(http.MethodDelete, url, params...) } -// HttpPatch send patch http request -func HttpPatch(url string, params ...interface{}) (*http.Response, error) { +// HttpPatch send patch http request. +func HttpPatch(url string, params ...any) (*http.Response, error) { return doHttpRequest(http.MethodPatch, url, params...) } -// ParseHttpResponse decode http response to specified interface -func ParseHttpResponse(resp *http.Response, obj interface{}) error { +// ParseHttpResponse decode http response to specified interface. +func ParseHttpResponse(resp *http.Response, obj any) error { if resp == nil { return errors.New("InvalidResp") } @@ -54,12 +65,13 @@ func ParseHttpResponse(resp *http.Response, obj interface{}) error { return json.NewDecoder(resp.Body).Decode(obj) } -// ConvertMapToQueryString convert map to sorted url query string -func ConvertMapToQueryString(param map[string]interface{}) string { +// ConvertMapToQueryString convert map to sorted url query string. +// Play: https://go.dev/play/p/jnNt_qoSnRi +func ConvertMapToQueryString(param map[string]any) string { if param == nil { return "" } - var keys []string + keys := make([]string, 0, len(param)) for key := range param { keys = append(keys, key) } @@ -76,3 +88,321 @@ func ConvertMapToQueryString(param map[string]interface{}) string { } return build.String() } + +// HttpRequest struct is a composed http request +type HttpRequest struct { + RawURL string + Method string + Headers http.Header + QueryParams url.Values + FormData url.Values + File *File + Body []byte +} + +// HttpClientConfig contains some configurations for http client +type HttpClientConfig struct { + Timeout time.Duration + SSLEnabled bool + TLSConfig *tls.Config + Compressed bool + HandshakeTimeout time.Duration + ResponseTimeout time.Duration + Verbose bool + Proxy *url.URL +} + +// defaultHttpClientConfig defalut client config. +var defaultHttpClientConfig = &HttpClientConfig{ + Timeout: 50 * time.Second, + Compressed: false, + HandshakeTimeout: 10 * time.Second, + ResponseTimeout: 10 * time.Second, +} + +// HttpClient is used for sending http request. +type HttpClient struct { + *http.Client + TLS *tls.Config + Request *http.Request + Config HttpClientConfig + Context context.Context +} + +// NewHttpClient make a HttpClient instance. +func NewHttpClient() *HttpClient { + client := &HttpClient{ + Client: &http.Client{ + Timeout: defaultHttpClientConfig.Timeout, + Transport: &http.Transport{ + TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout, + ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout, + DisableCompression: !defaultHttpClientConfig.Compressed, + }, + }, + Config: *defaultHttpClientConfig, + } + + return client +} + +// NewHttpClientWithConfig make a HttpClient instance with pass config. +func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient { + if config == nil { + config = defaultHttpClientConfig + } + + client := &HttpClient{ + Client: &http.Client{ + Transport: &http.Transport{ + TLSHandshakeTimeout: config.HandshakeTimeout, + ResponseHeaderTimeout: config.ResponseTimeout, + DisableCompression: !config.Compressed, + }, + }, + Config: *config, + } + + if config.SSLEnabled { + client.TLS = config.TLSConfig + } + + if config.Proxy != nil { + transport := client.Client.Transport.(*http.Transport) + transport.Proxy = http.ProxyURL(config.Proxy) + } + + return client +} + +// SendRequest send http request. +// Play: https://go.dev/play/p/jUSgynekH7G +func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, error) { + err := validateRequest(request) + if err != nil { + return nil, err + } + + rawUrl := request.RawURL + + req, err := http.NewRequest(request.Method, rawUrl, bytes.NewBuffer(request.Body)) + + if client.Context != nil { + req, err = http.NewRequestWithContext(client.Context, request.Method, rawUrl, bytes.NewBuffer(request.Body)) + } + + if err != nil { + return nil, err + } + + client.setTLS(rawUrl) + client.setHeader(req, request.Headers) + + err = client.setQueryParam(req, rawUrl, request.QueryParams) + if err != nil { + return nil, err + } + + if request.FormData != nil { + if request.File != nil { + err = client.setFormData(req, request.FormData, setFile(request.File)) + } else { + err = client.setFormData(req, request.FormData, nil) + } + } + + client.Request = req + + resp, err := client.Client.Do(req) + if err != nil { + return nil, err + } + + return resp, nil +} + +// AsyncSendRequest send http request with goroutine, pop response and error to channels +func (client *HttpClient) AsyncSendRequest(request *HttpRequest, respChan chan *http.Response, errChan chan error) { + go func() { + defer func() { + if err := recover(); err != nil { + errChan <- fmt.Errorf("%v", err) + } + }() + resp, err := client.SendRequest(request) + if err != nil { + errChan <- err + } + respChan <- resp + }() +} + +// DecodeResponse decode response into target object. +// Play: https://go.dev/play/p/jUSgynekH7G +func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error { + if resp == nil { + return errors.New("invalid target param") + } + defer resp.Body.Close() + return json.NewDecoder(resp.Body).Decode(target) +} + +// setTLS set http client transport TLSClientConfig +func (client *HttpClient) setTLS(rawUrl string) { + if strings.HasPrefix(rawUrl, "https") { + if transport, ok := client.Client.Transport.(*http.Transport); ok { + transport.TLSClientConfig = client.TLS + } + } +} + +// setHeader set http request header +func (client *HttpClient) setHeader(req *http.Request, headers http.Header) { + if headers == nil { + headers = make(http.Header) + } + + if _, ok := headers["Accept"]; !ok { + headers["Accept"] = []string{"*/*"} + } + if _, ok := headers["Accept-Encoding"]; !ok && client.Config.Compressed { + headers["Accept-Encoding"] = []string{"deflate, gzip"} + } + + req.Header = headers +} + +// setQueryParam set http request query string param +func (client *HttpClient) setQueryParam(req *http.Request, reqUrl string, queryParam url.Values) error { + if queryParam != nil { + if !strings.Contains(reqUrl, "?") { + reqUrl = reqUrl + "?" + queryParam.Encode() + } else { + reqUrl = reqUrl + "&" + queryParam.Encode() + } + u, err := url.Parse(reqUrl) + if err != nil { + return err + } + req.URL = u + } + return nil +} + +// setFormData set http request FormData param +func (client *HttpClient) setFormData(req *http.Request, values url.Values, setFile SetFileFunc) error { + if setFile != nil { + err := setFile(req, values) + if err != nil { + return err + } + } else { + formData := []byte(values.Encode()) + req.Body = io.NopCloser(bytes.NewReader(formData)) + req.ContentLength = int64(len(formData)) + } + return nil +} + +type SetFileFunc func(req *http.Request, values url.Values) error + +// File struct is a combination of file attributes +type File struct { + Content []byte + Path string + FieldName string + FileName string +} + +// setFile set parameters for http request formdata file upload +func setFile(f *File) SetFileFunc { + return func(req *http.Request, values url.Values) error { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + for key, vals := range values { + for _, val := range vals { + err := writer.WriteField(key, val) + if err != nil { + return err + } + } + } + + if f.Content != nil { + part, err := writer.CreateFormFile(f.FieldName, f.FileName) + if err != nil { + return err + } + part.Write(f.Content) + } else if f.Path != "" { + file, err := os.Open(f.Path) + if err != nil { + return err + } + defer file.Close() + + part, err := writer.CreateFormFile(f.FieldName, f.FileName) + if err != nil { + return err + } + _, err = io.Copy(part, file) + if err != nil { + return err + } + } + + err := writer.Close() + if err != nil { + return err + } + + req.Body = io.NopCloser(body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req.ContentLength = int64(body.Len()) + + return nil + } +} + +// validateRequest check if a request has url, and valid method. +func validateRequest(req *HttpRequest) error { + if req.RawURL == "" { + return errors.New("invalid request url") + } + + // common HTTP methods + methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", + "HEAD", "CONNECT", "OPTIONS", "TRACE"} + + if !slice.Contain(methods, strings.ToUpper(req.Method)) { + return errors.New("invalid request method") + } + + return nil +} + +// StructToUrlValues convert struct to url valuse, +// only convert the field which is exported and has `json` tag. +// Play: https://go.dev/play/p/pFqMkM40w9z +func StructToUrlValues(targetStruct any) (url.Values, error) { + result := url.Values{} + + var m map[string]interface{} + + jsonBytes, err := json.Marshal(targetStruct) + if err != nil { + return nil, err + } + + err = json.Unmarshal(jsonBytes, &m) + if err != nil { + return nil, err + } + + for k, v := range m { + result.Add(k, fmt.Sprintf("%v", v)) + } + + return result, nil +} diff --git a/netutil/http_test.go b/netutil/http_test.go index 701f9984..f3044a31 100644 --- a/netutil/http_test.go +++ b/netutil/http_test.go @@ -1,12 +1,17 @@ package netutil import ( + "bytes" "encoding/json" "io/ioutil" - "log" + "net/http" + "net/http/httptest" + "net/url" + "os" "testing" + "time" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestHttpGet(t *testing.T) { @@ -17,12 +22,12 @@ func TestHttpGet(t *testing.T) { resp, err := HttpGet(url, header) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: " + err.Error()) + return } - body, _ := ioutil.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) { @@ -39,11 +44,32 @@ func TestHttpPost(t *testing.T) { resp, err := HttpPost(url, header, nil, bodyParams) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: " + err.Error()) + return + } + + defer resp.Body.Close() + t.Log("response status:", resp.StatusCode) +} + +func TestHttpPostFormData(t *testing.T) { + apiUrl := "https://jsonplaceholder.typicode.com/todos" + header := map[string]string{ + "Content-Type": "application/x-www-form-urlencoded", + } + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "TestToDo") + + resp, err := HttpPost(apiUrl, header, nil, postData) + if err != nil { + t.Log("net error: " + err.Error()) + return } - body, _ := ioutil.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) { @@ -61,11 +87,12 @@ func TestHttpPut(t *testing.T) { resp, err := HttpPut(url, header, nil, bodyParams) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: ", err.Error()) + return } - body, _ := ioutil.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) { @@ -83,32 +110,37 @@ func TestHttpPatch(t *testing.T) { resp, err := HttpPatch(url, header, nil, bodyParams) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: ", err.Error()) + return } - body, _ := ioutil.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) { url := "https://jsonplaceholder.typicode.com/todos/1" resp, err := HttpDelete(url) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: ", err.Error()) + return } - body, _ := ioutil.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) { + t.Parallel() + assert := internal.NewAssert(t, "TestConvertMapToQueryString") - var m = map[string]interface{}{ + var m = map[string]any{ "c": 3, "a": 1, "b": 2, } + assert.Equal("a=1&b=2&c=3", ConvertMapToQueryString(m)) } @@ -120,8 +152,8 @@ func TestParseResponse(t *testing.T) { resp, err := HttpGet(url, header) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: ", err.Error()) + return } type Todo struct { @@ -134,8 +166,269 @@ func TestParseResponse(t *testing.T) { toDoResp := &Todo{} err = ParseHttpResponse(resp, toDoResp) if err != nil { - log.Fatal(err) - t.FailNow() + t.Log("net error: ", err.Error()) + return } + t.Log("response: ", toDoResp) } + +func TestHttpClient_Get(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHttpClient_Get") + + request := &HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + t.Log("net error: ", err.Error()) + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + t.FailNow() + } + + assert.Equal(1, todo.Id) +} + +func TestHttpClent_Post(t *testing.T) { + header := http.Header{} + header.Add("Content-Type", "multipart/form-data") + + postData := url.Values{} + postData.Add("userId", "1") + postData.Add("title", "testItem") + + request := &HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos", + Method: "POST", + Headers: header, + FormData: postData, + } + + httpClient := NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil { + t.Log("net error: ", err.Error()) + return + } + + defer resp.Body.Close() + t.Log("response status:", resp.StatusCode) +} + +func TestStructToUrlValues(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStructToUrlValues") + + type CommReq struct { + Version string `json:"version"` + } + + type TodoQuery struct { + Id int `json:"id"` + UserId int `json:"userId"` + Name string `json:"name,omitempty"` + CommReq `json:",inline"` + } + item1 := TodoQuery{ + Id: 1, + UserId: 123, + Name: "", + CommReq: CommReq{ + Version: "1.0", + }, + } + + todoValues, err := StructToUrlValues(item1) + if err != nil { + t.Errorf("params is invalid: %v", err) + } + + assert.Equal("1", todoValues.Get("id")) + assert.Equal("123", todoValues.Get("userId")) + assert.Equal("", todoValues.Get("name")) + assert.Equal("1.0", todoValues.Get("version")) +} + +func handleFileRequest(t *testing.T, _ http.ResponseWriter, r *http.Request) { + err := r.ParseMultipartForm(1024) + if err != nil { + t.Fatal(err) + } + + key1 := r.FormValue("key1") + expectedKey1 := "value1" + if key1 != expectedKey1 { + t.Fatalf("expected %s, got %s", expectedKey1, key1) + } + + key2 := r.FormValue("key2") + expectedKey2 := "value2" + if key2 != expectedKey2 { + t.Fatalf("expected %s, got %s", expectedKey2, key2) + } + + file, header, err := r.FormFile("image") + if err != nil { + t.Fatal(err) + } + + expectedFileName := "testImage.jpg" + if header.Filename != expectedFileName { + t.Fatalf("expected %s, got %s", expectedFileName, header.Filename) + } + + defer file.Close() + + content, err := ioutil.ReadAll(file) + if err != nil { + t.Fatal(err) + } + + expectedContent := []byte("file content") + if !bytes.Equal(content, expectedContent) { + t.Fatalf("expected %s, got %s", string(expectedContent), string(content)) + } +} + +func TestSendRequestWithFileContent(t *testing.T) { + handler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + handleFileRequest(t, writer, request) + }) + + server := httptest.NewServer(handler) + defer server.Close() + + client := NewHttpClient() + request := &HttpRequest{ + RawURL: server.URL, + Method: "POST", + File: &File{Content: []byte("file content"), FieldName: "image", FileName: "testImage.jpg"}, + FormData: url.Values{"key1": {"value1"}, "key2": {"value2"}}, + } + + resp, err := client.SendRequest(request) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode) + } +} + +func TestSendRequestWithFilePath(t *testing.T) { + handler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + handleFileRequest(t, writer, request) + }) + + server := httptest.NewServer(handler) + defer server.Close() + + tmpFile, err := ioutil.TempFile("", "testImage.jpg") + if err != nil { + t.Fatal(err) + } + + defer os.Remove(tmpFile.Name()) + + tmpFile.Write([]byte("file content")) + tmpFile.Close() + + client := NewHttpClient() + request := &HttpRequest{ + RawURL: server.URL, + Method: "POST", + File: &File{Path: tmpFile.Name(), FieldName: "image", FileName: "testImage.jpg"}, + FormData: url.Values{"key1": {"value1"}, "key2": {"value2"}}, + } + + resp, err := client.SendRequest(request) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode) + } +} + +func TestHttpClient_AsyncSendRequest(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHttpClient_Get") + + request := &HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := NewHttpClient() + respCh := make(chan *http.Response, 20) + errCh := make(chan error, 20) + for i := 0; i < 50; i++ { + httpClient.AsyncSendRequest(request, respCh, errCh) + } + for i := 0; i < 50; i++ { + select { + case resp := <-respCh: + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + var todo Todo + err := httpClient.DecodeResponse(resp, &todo) + if err != nil { + t.FailNow() + } + assert.Equal(1, todo.Id) + case err := <-errCh: + if err != nil { + t.Log("net error: ", err.Error()) + } + } + } +} + +func TestProxy(t *testing.T) { + config := &HttpClientConfig{ + HandshakeTimeout: 20 * time.Second, + ResponseTimeout: 40 * time.Second, + // Use the proxy ip to add it here + //Proxy: &url.URL{ + // Scheme: "http", + // Host: "46.17.63.166:18888", + //}, + } + httpClient := NewHttpClientWithConfig(config) + resp, err := httpClient.Get("https://www.ipplus360.com/getLocation") + if err != nil { + t.Log("net error: ", err.Error()) + return + } + + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode) + } +} diff --git a/netutil/net.go b/netutil/net.go index fc735f34..1b3b2fe1 100644 --- a/netutil/net.go +++ b/netutil/net.go @@ -1,31 +1,94 @@ package netutil import ( + "bytes" "encoding/json" - "io/ioutil" + "errors" + "fmt" + "io" + "mime/multipart" "net" "net/http" + "net/url" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "time" + + "github.com/duke-git/lancet/v2/fileutil" ) -// GetInternalIp return internal ipv4 +// GetInternalIp return internal ipv4. +// Play: https://go.dev/play/p/5mbu-gFp7ei func GetInternalIp() string { - addr, err := net.InterfaceAddrs() + addrs, err := net.InterfaceAddrs() if err != nil { panic(err.Error()) } - for _, a := range addr { - if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { - if ipNet.IP.To4() != nil { - return ipNet.IP.String() - } + + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + + if ip != nil && (ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast()) { + continue + } + + if ipv4 := ip.To4(); ipv4 != nil { + return ipv4.String() } } return "" } +// func GetInternalIp() string { +// addr, err := net.InterfaceAddrs() +// if err != nil { +// panic(err.Error()) +// } +// for _, a := range addr { +// if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { +// if ipNet.IP.To4() != nil { +// return ipNet.IP.String() +// } +// } +// } + +// return "" +// } + +// GetRequestPublicIp return the requested public ip. +// Play: https://go.dev/play/p/kxU-YDc_eBo +func GetRequestPublicIp(req *http.Request) string { + var ip string + for _, ip = range strings.Split(req.Header.Get("X-Forwarded-For"), ",") { + if ip = strings.TrimSpace(ip); ip != "" && !IsInternalIP(net.ParseIP(ip)) { + return ip + } + } + + if ip = strings.TrimSpace(req.Header.Get("X-Real-Ip")); ip != "" && !IsInternalIP(net.ParseIP(ip)) { + return ip + } + + if ip, _, _ = net.SplitHostPort(req.RemoteAddr); !IsInternalIP(net.ParseIP(ip)) { + return ip + } + + return ip +} + // GetPublicIpInfo return public ip information -// return the PublicIpInfo struct +// return the PublicIpInfo struct. +// Play: https://go.dev/play/p/YDxIfozsRHR func GetPublicIpInfo() (*PublicIpInfo, error) { resp, err := http.Get("http://ip-api.com/json/") if err != nil { @@ -33,7 +96,7 @@ func GetPublicIpInfo() (*PublicIpInfo, error) { } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -47,7 +110,8 @@ func GetPublicIpInfo() (*PublicIpInfo, error) { return &ip, nil } -// GetIps return all ipv4 of system +// GetIps return all ipv4 of system. +// Play: https://go.dev/play/p/NUFfcEmukx1 func GetIps() []string { var ips []string @@ -67,7 +131,8 @@ func GetIps() []string { return ips } -// GetMacAddrs get mac address +// GetMacAddrs get mac address. +// Play: https://go.dev/play/p/Rq9UUBS_Xp1 func GetMacAddrs() []string { var macAddrs []string @@ -103,7 +168,8 @@ type PublicIpInfo struct { Ip string `json:"query"` } -// IsPublicIP verify a ip is public or not +// IsPublicIP verify a ip is public or not. +// Play: https://go.dev/play/p/nmktSQpJZnn func IsPublicIP(IP net.IP) bool { if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() { return false @@ -122,3 +188,216 @@ func IsPublicIP(IP net.IP) bool { } return false } + +// IsInternalIP verify an ip is intranet or not. +// Play: https://go.dev/play/p/sYGhXbgO4Cb +func IsInternalIP(IP net.IP) bool { + if IP.IsLoopback() { + return true + } + if ip4 := IP.To4(); ip4 != nil { + return ip4[0] == 10 || + (ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || + (ip4[0] == 169 && ip4[1] == 254) || + (ip4[0] == 192 && ip4[1] == 168) + } + return false +} + +// EncodeUrl encode url. +// Play: https://go.dev/play/p/bsZ6BRC4uKI +func EncodeUrl(urlStr string) (string, error) { + URL, err := url.Parse(urlStr) + if err != nil { + return "", err + } + + URL.RawQuery = URL.Query().Encode() + + return URL.String(), nil +} + +// DownloadFile will upload the file to a server. +func UploadFile(filepath string, server string) (bool, error) { + if !fileutil.IsExist(filepath) { + return false, errors.New("file not exist") + } + + fileInfo, err := os.Stat(filepath) + if err != nil { + return false, err + } + + bodyBuffer := &bytes.Buffer{} + writer := multipart.NewWriter(bodyBuffer) + + formFile, err := writer.CreateFormFile("uploadfile", fileInfo.Name()) + if err != nil { + return false, err + } + + srcFile, err := os.Open(filepath) + if err != nil { + return false, err + } + defer srcFile.Close() + + _, err = io.Copy(formFile, srcFile) + if err != nil { + return false, err + } + + contentType := writer.FormDataContentType() + writer.Close() + + _, err = http.Post(server, contentType, bodyBuffer) + if err != nil { + return false, err + } + + return true, nil +} + +// DownloadFile will download the file exist in url to a local file. +func DownloadFile(filepath string, url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + + return err +} + +// IsPingConnected checks if can ping specified host or not. +// Play: https://go.dev/play/p/q8OzTijsA87 +func IsPingConnected(host string) bool { + cmd := exec.Command("ping", host, "-c", "4", "-W", "6") + + if runtime.GOOS == "windows" { + cmd = exec.Command("ping", host, "-n", "4", "-w", "6") + } + + err := cmd.Run() + if err != nil { + return false + } + return true +} + +// IsTelnetConnected checks if can telnet specified host or not. +// Play: https://go.dev/play/p/yiLCGtQv_ZG +func IsTelnetConnected(host string, port string) bool { + adder := host + ":" + port + conn, err := net.DialTimeout("tcp", adder, 5*time.Second) + + if err != nil { + return false + } + + defer conn.Close() + + 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 +} diff --git a/netutil/net_example_test.go b/netutil/net_example_test.go new file mode 100644 index 00000000..cd56a0da --- /dev/null +++ b/netutil/net_example_test.go @@ -0,0 +1,240 @@ +package netutil + +import ( + "fmt" + "net" + "net/http" +) + +func ExampleGetInternalIp() { + internalIp := GetInternalIp() + + result := IsInternalIP(net.ParseIP(internalIp)) + fmt.Println(result) + + // Output: + // true +} + +// func ExampleGetPublicIpInfo() { +// ipInfo, err := GetPublicIpInfo() +// if err != nil { +// return +// } + +// result := IsPublicIP(net.ParseIP(ipInfo.Ip)) +// fmt.Println(result) + +// // Output: +// // true +// } + +func ExampleGetRequestPublicIp() { + ip := "36.112.24.10" + + request := http.Request{ + Method: "GET", + Header: http.Header{ + "X-Forwarded-For": {ip}, + }, + } + publicIp := GetRequestPublicIp(&request) + + fmt.Println(publicIp) + + // Output: + // 36.112.24.10 +} + +func ExampleIsInternalIP() { + ip1 := IsInternalIP(net.ParseIP("127.0.0.1")) + ip2 := IsInternalIP(net.ParseIP("192.168.0.1")) + ip3 := IsInternalIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // true + // true + // false +} + +func ExampleIsPublicIP() { + ip1 := IsPublicIP(net.ParseIP("127.0.0.1")) + ip2 := IsPublicIP(net.ParseIP("192.168.0.1")) + ip3 := IsPublicIP(net.ParseIP("36.112.24.10")) + + fmt.Println(ip1) + fmt.Println(ip2) + fmt.Println(ip3) + + // Output: + // false + // false + // true +} + +func ExampleEncodeUrl() { + urlAddr := "http://www.lancet.com?a=1&b=[2]" + + encodedUrl, err := EncodeUrl(urlAddr) + if err != nil { + return + } + + fmt.Println(encodedUrl) + + // Output: + // http://www.lancet.com?a=1&b=%5B2%5D +} + +func ExampleHttpClient_DecodeResponse() { + request := &HttpRequest{ + RawURL: "https://jsonplaceholder.typicode.com/todos/1", + Method: "GET", + } + + httpClient := NewHttpClient() + resp, err := httpClient.SendRequest(request) + if err != nil || resp.StatusCode != 200 { + return + } + + type Todo struct { + UserId int `json:"userId"` + Id int `json:"id"` + Title string `json:"title"` + Completed bool `json:"completed"` + } + + var todo Todo + err = httpClient.DecodeResponse(resp, &todo) + if err != nil { + return + } + + fmt.Println(todo.Id) + + // Output: + // 1 +} + +func ExampleStructToUrlValues() { + type TodoQuery struct { + Id int `json:"id"` + UserId int `json:"userId"` + Name string `json:"name,omitempty"` + Status string + } + item1 := TodoQuery{ + Id: 1, + UserId: 123, + Name: "test", + Status: "completed", + } + queryValues1, err := StructToUrlValues(item1) + if err != nil { + return + } + + item2 := TodoQuery{ + Id: 2, + UserId: 456, + } + queryValues2, _ := StructToUrlValues(item2) + + fmt.Println(queryValues1.Get("id")) + fmt.Println(queryValues1.Get("userId")) + fmt.Println(queryValues1.Get("name")) + fmt.Println(queryValues1.Get("status")) + + fmt.Println(queryValues2.Get("id")) + fmt.Println(queryValues2.Get("userId")) + fmt.Println(queryValues2.Get("name")) + + // Output: + // 1 + // 123 + // test + // + // 2 + // 456 + // +} + +func ExampleConvertMapToQueryString() { + var m = map[string]any{ + "c": 3, + "a": 1, + "b": 2, + } + + qs := ConvertMapToQueryString(m) + + fmt.Println(qs) + + // Output: + // a=1&b=2&c=3 +} + +func ExampleIsPingConnected() { + // result1 := IsPingConnected("bing.com") + result2 := IsPingConnected("www.!@#&&&.com") + + // fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false +} + +func ExampleIsTelnetConnected() { + result1 := IsTelnetConnected("bing.com", "80") + result2 := IsTelnetConnected("www.baidu.com", "123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 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 + // +} + +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 + // +} diff --git a/netutil/net_internal.go b/netutil/net_internal.go index f7960912..512a9c8a 100644 --- a/netutil/net_internal.go +++ b/netutil/net_internal.go @@ -4,13 +4,14 @@ import ( "bytes" "errors" "fmt" + "io" "io/ioutil" "net/http" "net/url" "strings" ) -func doHttpRequest(method, reqUrl string, params ...interface{}) (*http.Response, error) { +func doHttpRequest(method, reqUrl string, params ...any) (*http.Response, error) { if len(reqUrl) == 0 { return nil, errors.New("url should be specified") } @@ -60,7 +61,7 @@ func doHttpRequest(method, reqUrl string, params ...interface{}) (*http.Response return resp, e } -func setHeaderAndQueryParam(req *http.Request, reqUrl string, header, queryParam interface{}) error { +func setHeaderAndQueryParam(req *http.Request, reqUrl string, header, queryParam any) error { err := setHeader(req, header) if err != nil { return err @@ -72,38 +73,35 @@ func setHeaderAndQueryParam(req *http.Request, reqUrl string, header, queryParam return nil } -func setHeaderAndQueryAndBody(req *http.Request, reqUrl string, header, queryParam, body interface{}) error { - err := setHeader(req, header) - if err != nil { +func setHeaderAndQueryAndBody(req *http.Request, reqUrl string, header, queryParam, body any) error { + if err := setHeader(req, header); err != nil { return err - } - err = setQueryParam(req, reqUrl, queryParam) - if err != nil { + } else if err = setQueryParam(req, reqUrl, queryParam); err != nil { return err - } - err = setBodyByte(req, body) - if err != nil { + } else if err = setBodyByte(req, body); err != nil { return err } return nil } -func setHeader(req *http.Request, header interface{}) error { - if header != nil { - switch v := header.(type) { - case map[string]string: - for k := range v { - req.Header.Add(k, v[k]) - } - case http.Header: - for k, vv := range v { - for _, vvv := range vv { - req.Header.Add(k, vvv) - } +func setHeader(req *http.Request, header any) error { + if header == nil { + return nil + } + + switch v := header.(type) { + case map[string]string: + for k := range v { + req.Header.Add(k, v[k]) + } + case http.Header: + for k, vv := range v { + for _, vvv := range vv { + req.Header.Add(k, vvv) } - default: - return errors.New("header params type should be http.Header or map[string]string") } + default: + return errors.New("header params type should be http.Header or map[string]string") } if host := req.Header.Get("Host"); host != "" { @@ -122,20 +120,22 @@ func setUrl(req *http.Request, reqUrl string) error { return nil } -func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error { +func setQueryParam(req *http.Request, reqUrl string, queryParam any) error { + if queryParam == nil { + return nil + } + var values url.Values - if queryParam != nil { - switch v := queryParam.(type) { - case map[string]interface{}: - values = url.Values{} - for k := range v { - values.Set(k, fmt.Sprintf("%v", v[k])) - } - case url.Values: - values = v - default: - return errors.New("query params type should be url.Values or map[string]interface{}") + switch v := queryParam.(type) { + case map[string]string: + values = url.Values{} + for k := range v { + values.Set(k, v[k]) } + case url.Values: + values = v + default: + return errors.New("query string params type should be url.Values or map[string]string") } // set url @@ -155,20 +155,42 @@ func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) err return nil } -func setBodyByte(req *http.Request, body interface{}) error { - if body != nil { - switch b := body.(type) { - case []byte: - req.Body = ioutil.NopCloser(bytes.NewReader(b)) - req.ContentLength = int64(len(b)) - default: - return errors.New("body type should be []byte") +func setBodyByte(req *http.Request, body any) error { + if body == nil { + return nil + } + var bodyReader *bytes.Reader + switch b := body.(type) { + case io.Reader: + buf := bytes.NewBuffer(nil) + if _, err := io.Copy(buf, b); err != nil { + return err + } + req.Body = ioutil.NopCloser(buf) + req.ContentLength = int64(buf.Len()) + case []byte: + bodyReader = bytes.NewReader(b) + req.Body = ioutil.NopCloser(bodyReader) + req.ContentLength = int64(bodyReader.Len()) + case map[string]interface{}: + values := url.Values{} + for k := range b { + values.Set(k, fmt.Sprintf("%v", b[k])) } + bodyReader = bytes.NewReader([]byte(values.Encode())) + req.Body = ioutil.NopCloser(bodyReader) + req.ContentLength = int64(bodyReader.Len()) + case url.Values: + bodyReader = bytes.NewReader([]byte(b.Encode())) + req.Body = ioutil.NopCloser(bodyReader) + req.ContentLength = int64(bodyReader.Len()) + default: + return fmt.Errorf("invalid body type: %T", b) } return nil } -func getClient(client interface{}) (*http.Client, error) { +func getClient(client any) (*http.Client, error) { c := http.Client{} if client != nil { switch v := client.(type) { diff --git a/netutil/net_test.go b/netutil/net_test.go index 7773c376..a080a487 100644 --- a/netutil/net_test.go +++ b/netutil/net_test.go @@ -2,12 +2,15 @@ package netutil import ( "net" + "net/http" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestGetInternalIp(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestGetInternalIp") internalIp := GetInternalIp() @@ -15,16 +18,44 @@ func TestGetInternalIp(t *testing.T) { assert.IsNotNil(ip) } -func TestGetPublicIpInfo(t *testing.T) { +func TestGetRequestPublicIp(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestGetPublicIpInfo") - publicIpInfo, err := GetPublicIpInfo() - assert.IsNil(err) + ip := "36.112.24.10" + + request := http.Request{ + Method: "GET", + Header: http.Header{ + "X-Forwarded-For": {ip}, + }, + } + publicIp := GetRequestPublicIp(&request) + assert.Equal(publicIp, ip) - t.Logf("public ip info is: %+v \n", *publicIpInfo) + request = http.Request{ + Method: "GET", + Header: http.Header{ + "X-Real-Ip": {ip}, + }, + } + publicIp = GetRequestPublicIp(&request) + assert.Equal(publicIp, ip) } +// func TestGetPublicIpInfo(t *testing.T) { +// assert := internal.NewAssert(t, "TestGetPublicIpInfo") + +// publicIpInfo, err := GetPublicIpInfo() +// assert.IsNil(err) + +// t.Logf("public ip info is: %+v \n", *publicIpInfo) +// } + func TestIsPublicIP(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsPublicIP") ips := []net.IP{ @@ -43,6 +74,27 @@ func TestIsPublicIP(t *testing.T) { } } +func TestIsInternalIP(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsInternalIP") + + ips := []net.IP{ + net.ParseIP("127.0.0.1"), + net.ParseIP("192.168.0.1"), + net.ParseIP("10.91.210.131"), + net.ParseIP("172.20.16.1"), + net.ParseIP("36.112.24.10"), + } + + expected := []bool{true, true, true, true, false} + + for i := 0; i < len(ips); i++ { + actual := IsInternalIP(ips[i]) + assert.Equal(expected[i], actual) + } +} + func TestGetIps(t *testing.T) { ips := GetIps() t.Log(ips) @@ -52,3 +104,156 @@ func TestGetMacAddrs(t *testing.T) { macAddrs := GetMacAddrs() t.Log(macAddrs) } + +func TestEncodeUrl(t *testing.T) { + assert := internal.NewAssert(t, "TestEncodeUrl") + + urlAddr := "http://www.lancet.com?a=1&b=[2]" + encodedUrl, err := EncodeUrl(urlAddr) + if err != nil { + t.Fail() + } + + expected := "http://www.lancet.com?a=1&b=%5B2%5D" + assert.Equal(expected, encodedUrl) +} + +func TestIsPingConnected(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsPingConnected") + + // in github action env, this will fail + // result1 := IsPingConnected("www.baidu.com") + // assert.Equal(true, result1) + + result2 := IsPingConnected("www.!@#&&&.com") + assert.Equal(false, result2) +} + +func TestTelnetConnected(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTelnetConnected") + + result1 := IsTelnetConnected("bing.com", "80") + assert.Equal(true, result1) + + 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) + } +} diff --git a/pointer/pointer.go b/pointer/pointer.go new file mode 100644 index 00000000..c9a760c3 --- /dev/null +++ b/pointer/pointer.go @@ -0,0 +1,87 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license. + +// Package pointer contains some util functions to operate go pointer. +package pointer + +import ( + "reflect" +) + +// Of returns a pointer to the value `v`. +// Play: https://go.dev/play/p/HFd70x4DrMj +func Of[T any](v T) *T { + if IsNil(v) { + return nil + } + return &v +} + +// Unwrap returns the value from the pointer. +// +// Play: https://go.dev/play/p/cgeu3g7cjWb +// Deprecated: Please use UnwrapOr +func Unwrap[T any](p *T) T { + return *p +} + +// UnwarpOr returns the value from the pointer or fallback if the pointer is nil. +// +// Play: https://go.dev/play/p/mmNaLC38W8C +// Deprecated: Please use UnwrapOr +func UnwarpOr[T any](p *T, fallback T) T { + if p == nil { + return fallback + } + return *p +} + +// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil. +// +// Play: https://go.dev/play/p/ZnGIHf8_o4E +// Deprecated: Please use UnwrapOr +func UnwarpOrDefault[T any](p *T) T { + var v T + + if p == nil { + return v + } + return *p +} + +// UnwrapOr returns the value from the pointer or fallback if the pointer is nil. +func UnwrapOr[T any](p *T, fallback ...T) T { + if !IsNil(p) { + return *p + } + if len(fallback) > 0 { + return fallback[0] + } + var t T + return t +} + +// ExtractPointer returns the underlying value by the given interface type +// Play: https://go.dev/play/p/D7HFjeWU2ZP +func ExtractPointer(value any) any { + if IsNil(value) { + return value + } + t := reflect.TypeOf(value) + v := reflect.ValueOf(value) + + if t.Kind() != reflect.Pointer { + return value + } + + if v.Elem().IsValid() { + return ExtractPointer(v.Elem().Interface()) + } + + return nil +} + +// IsNil returns true if the given interface is nil or the underlying value is nil. +func IsNil(i interface{}) bool { + return i == nil || (reflect.ValueOf(i).Kind() == reflect.Ptr && reflect.ValueOf(i).IsNil()) +} diff --git a/pointer/pointer_examples_test.go b/pointer/pointer_examples_test.go new file mode 100644 index 00000000..6bb2a519 --- /dev/null +++ b/pointer/pointer_examples_test.go @@ -0,0 +1,150 @@ +package pointer + +import "fmt" + +func ExampleOf() { + result1 := Of(123) + result2 := Of("abc") + + fmt.Println(*result1) + fmt.Println(*result2) + + // Output: + // 123 + // abc +} + +func ExampleUnwrap() { + a := 123 + b := "abc" + + result1 := Unwrap(&a) + result2 := Unwrap(&b) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 123 + // abc +} + +func ExampleUnwarpOr() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := UnwarpOr(&a, 456) + result2 := UnwarpOr(&b, "abc") + result3 := UnwarpOr(c, 456) + result4 := UnwarpOr(d, "def") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 456 + // def +} + +func ExampleUnwarpOrDefault() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := UnwarpOrDefault(&a) + result2 := UnwarpOrDefault(&b) + result3 := UnwarpOrDefault(c) + result4 := UnwarpOrDefault(d) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 0 + // +} + +func ExampleExtractPointer() { + a := 1 + b := &a + c := &b + d := &c + + result := ExtractPointer(d) + + fmt.Println(result) + + // Output: + // 1 +} + +func ExampleIsNil() { + a := 1 + b := &a + c := &b + d := &c + e := &d + var f *int + + result1 := IsNil(a) + result2 := IsNil(b) + result3 := IsNil(c) + result4 := IsNil(d) + result5 := IsNil(e) + result6 := IsNil(f) + result7 := IsNil(nil) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // false + // false + // false + // false + // false + // true + // true +} + +func ExampleUnwrapOr() { + a := 123 + b := "abc" + + var c *int + var d *string + + result1 := UnwrapOr(&a, 456) + result2 := UnwrapOr(&b, "efg") + result3 := UnwrapOr(c, 456) + result4 := UnwrapOr(d, "def") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 123 + // abc + // 456 + // def +} diff --git a/pointer/pointer_test.go b/pointer/pointer_test.go new file mode 100644 index 00000000..939ce383 --- /dev/null +++ b/pointer/pointer_test.go @@ -0,0 +1,78 @@ +package pointer + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestOf") + + result1 := Of(123) + result2 := Of("abc") + + assert.Equal(123, *result1) + assert.Equal("abc", *result2) +} + +func TestUnwrap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnwrap") + + a := 123 + b := "abc" + + assert.Equal(a, Unwrap(&a)) + assert.Equal(b, Unwrap(&b)) +} + +func TestUnwarpOr(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnwarpOr") + + a := 123 + b := "abc" + + var c *int + var d *string + + assert.Equal(a, UnwarpOr(&a, 456)) + assert.Equal(b, UnwarpOr(&b, "abc")) + assert.Equal(456, UnwarpOr(c, 456)) + assert.Equal("def", UnwarpOr(d, "def")) +} + +func TestUnwrapOrDefault(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnwrapOrDefault") + + a := 123 + b := "abc" + + var c *int + var d *string + + assert.Equal(a, UnwarpOrDefault(&a)) + assert.Equal(b, UnwarpOrDefault(&b)) + assert.Equal(0, UnwarpOrDefault(c)) + assert.Equal("", UnwarpOrDefault(d)) +} + +func TestExtractPointer(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestExtractPointer") + + a := 1 + b := &a + c := &b + d := &c + + assert.Equal(1, ExtractPointer(d)) +} diff --git a/promise/promise.go b/promise/promise.go new file mode 100644 index 00000000..6e413699 --- /dev/null +++ b/promise/promise.go @@ -0,0 +1,279 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package promise contains some functions to support async programming. +package promise + +import ( + "errors" + "fmt" + "sync" + + "github.com/duke-git/lancet/v2/internal" +) + +// Promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value. +// ref : chebyrash/promise (https://github.com/chebyrash/promise) +// see js promise: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +type Promise[T any] struct { + runnable func(resolve func(T), reject func(error)) + result T + err error + + pending bool + + mu *sync.Mutex + wg *sync.WaitGroup +} + +// New create a new promise instance. +func New[T any](runnable func(resolve func(T), reject func(error))) *Promise[T] { + if runnable == nil { + panic("runnable function should not be nil") + } + + p := &Promise[T]{ + runnable: runnable, + pending: true, + mu: &sync.Mutex{}, + wg: &sync.WaitGroup{}, + } + + defer p.run() + + return p +} + +func (p *Promise[T]) run() { + p.wg.Add(1) + + go func() { + defer func() { + if !p.pending { + return + } + + if err := recover(); err != nil { + p.reject(errors.New(fmt.Sprint(err))) + } + }() + + p.runnable(p.resolve, p.reject) + }() +} + +// Resolve returns a Promise that has been resolved with a given value. +func Resolve[T any](resolution T) *Promise[T] { + return &Promise[T]{ + result: resolution, + pending: false, + mu: &sync.Mutex{}, + wg: &sync.WaitGroup{}, + } +} + +func (p *Promise[T]) resolve(value T) { + p.mu.Lock() + defer p.mu.Unlock() + + if !p.pending { + return + } + + p.result = value + p.pending = false + + p.wg.Done() +} + +// Reject returns a Promise that has been rejected with a given error. +func Reject[T any](err error) *Promise[T] { + return &Promise[T]{ + err: err, + pending: false, + mu: &sync.Mutex{}, + wg: &sync.WaitGroup{}, + } +} + +func (p *Promise[T]) reject(err error) { + p.mu.Lock() + defer p.mu.Unlock() + + if !p.pending { + return + } + + p.err = err + p.pending = false + + p.wg.Done() +} + +// Then allows chain calls to other promise methods. +func Then[T1, T2 any](promise *Promise[T1], resolve1 func(value T1) T2) *Promise[T2] { + return New(func(resolve2 func(T2), reject func(error)) { + result, err := promise.Await() + if err != nil { + reject(err) + return + } + resolve2(resolve1(result)) + }) +} + +// Then allows chain calls to other promise methods. +func (p *Promise[T]) Then(resolve func(value T) T) *Promise[T] { + return New(func(_resolve func(T), reject func(error)) { + result, err := p.Await() + if err != nil { + reject(err) + return + } + _resolve(resolve(result)) + }) +} + +// Catch allows to chain promises. +func Catch[T any](promise *Promise[T], rejection func(err error) error) *Promise[T] { + return New(func(resolve func(T), reject func(error)) { + result, err := promise.Await() + if err != nil { + reject(rejection(err)) + return + } + resolve(result) + }) +} + +// Catch chain an existing promise with an intermediate reject function. +func (p *Promise[T]) Catch(reject func(error) error) *Promise[T] { + return New(func(resolve func(T), rej func(error)) { + resutl, err := p.Await() + if err != nil { + rej(reject(err)) + return + } + resolve(resutl) + }) +} + +// Await blocks until the 'runable' to finish execution. +func (p *Promise[T]) Await() (T, error) { + p.wg.Wait() + return p.result, p.err +} + +type tuple[T1, T2 any] struct { + _1 T1 + _2 T2 +} + +// All resolves when all of the promises have resolved, reject immediately upon any of the input promises rejecting. +func All[T any](promises []*Promise[T]) *Promise[[]T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func([]T), reject func(error)) { + valsChan := make(chan tuple[T, int], len(promises)) + errsChan := make(chan error, 1) + + for idx, p := range promises { + idx := idx + _ = Then(p, func(data T) T { + valsChan <- tuple[T, int]{_1: data, _2: idx} + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- err + return err + }) + } + + resolutions := make([]T, len(promises)) + for idx := 0; idx < len(promises); idx++ { + select { + case val := <-valsChan: + resolutions[val._2] = val._1 + case err := <-errsChan: + reject(err) + return + } + } + resolve(resolutions) + }) +} + +// Race will settle the first fullfiled promise among muti promises. +func Race[T any](promises []*Promise[T]) *Promise[T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func(T), reject func(error)) { + valsChan := make(chan T, 1) + errsChan := make(chan error, 1) + + for _, p := range promises { + _ = Then(p, func(data T) T { + valsChan <- data + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- err + return err + }) + } + + select { + case val := <-valsChan: + resolve(val) + case err := <-errsChan: + reject(err) + } + }) +} + +// Any resolves as soon as any of the input's Promises resolve, with the value of the resolved Promise. +// Any rejects if all of the given Promises are rejected with a combination of all errors. +func Any[T any](promises []*Promise[T]) *Promise[T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func(T), reject func(error)) { + valsChan := make(chan T, 1) + errsChan := make(chan tuple[error, int], len(promises)) + + for idx, p := range promises { + idx := idx + _ = Then(p, func(data T) T { + valsChan <- data + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- tuple[error, int]{_1: err, _2: idx} + return err + }) + } + + errs := make([]error, len(promises)) + for idx := 0; idx < len(promises); idx++ { + select { + case val := <-valsChan: + resolve(val) + return + case err := <-errsChan: + errs[err._2] = err._1 + } + } + + errCombo := errs[0] + for _, err := range errs[1:] { + errCombo = internal.JoinError(err) + } + + reject(errCombo) + }) +} diff --git a/promise/promise_example_test.go b/promise/promise_example_test.go new file mode 100644 index 00000000..0494d43d --- /dev/null +++ b/promise/promise_example_test.go @@ -0,0 +1,195 @@ +package promise + +import ( + "errors" + "fmt" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +func ExampleNew() { + p := New(func(resolve func(string), reject func(error)) { + resolve("hello") + }) + + val, err := p.Await() + if err != nil { + return + } + + fmt.Println(val) + + // Output: + // hello +} + +func ExampleThen() { + p1 := New(func(resolve func(string), reject func(error)) { + resolve("hello ") + }) + + p2 := Then(p1, func(s string) string { + return s + "world" + }) + + result, err := p2.Await() + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // hello world +} + +func ExamplePromise_Then() { + p1 := New(func(resolve func(string), reject func(error)) { + resolve("hello ") + }) + + p2 := p1.Then(func(s string) string { + return s + "world" + }) + + result, err := p2.Await() + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // hello world +} + +func ExampleCatch() { + p1 := New(func(resolve func(string), reject func(error)) { + err := errors.New("error1") + reject(err) + }) + + p2 := Catch(p1, func(err error) error { + e := errors.New("error2") + return internal.JoinError(err, e) + }) + + _, err := p1.Await() + + fmt.Println(err.Error()) + + result2, err := p2.Await() + + fmt.Println(result2) + fmt.Println(err.Error()) + + // Output: + // error1 + // + // error1 + // error2 +} + +func ExamplePromise_Catch() { + p1 := New(func(resolve func(string), reject func(error)) { + err := errors.New("error1") + reject(err) + }) + + p2 := p1.Catch(func(err error) error { + e := errors.New("error2") + return internal.JoinError(err, e) + }) + + _, err := p1.Await() + + fmt.Println(err.Error()) + + result2, err := p2.Await() + + fmt.Println(result2) + fmt.Println(err.Error()) + + // Output: + // error1 + // + // error1 + // error2 +} + +func ExampleAll() { + p1 := New(func(resolve func(string), reject func(error)) { + resolve("a") + }) + p2 := New(func(resolve func(string), reject func(error)) { + resolve("b") + }) + p3 := New(func(resolve func(string), reject func(error)) { + resolve("c") + }) + + pms := []*Promise[string]{p1, p2, p3} + p := All(pms) + + result, err := p.Await() + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // [a b c] +} + +func ExampleAny() { + p1 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 250) + resolve("fast") + }) + p2 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 500) + resolve("slow") + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error")) + }) + + pms := []*Promise[string]{p1, p2, p3} + p := Any(pms) + + result, err := p.Await() + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // fast +} + +func ExampleRace() { + p1 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 100) + resolve("fast") + }) + p2 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 300) + resolve("slow") + }) + + pms := []*Promise[string]{p1, p2} + p := Race(pms) + + result, err := p.Await() + if err != nil { + return + } + + fmt.Println(result) + + // Output: + // fast +} diff --git a/promise/promise_test.go b/promise/promise_test.go new file mode 100644 index 00000000..677e46cb --- /dev/null +++ b/promise/promise_test.go @@ -0,0 +1,312 @@ +package promise + +import ( + "errors" + "testing" + "time" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestResolve(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestResolve") + + p := Resolve("abc") + + assert.Equal("abc", p.result) + assert.Equal(false, p.pending) +} + +func TestReject(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReject") + + err := errors.New("error") + p := Reject[string](err) + + assert.Equal("error", p.err.Error()) + assert.Equal(false, p.pending) +} + +func TestThen(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestThen") + + p1 := New(func(resolve func(string), reject func(error)) { + resolve("abc") + }) + + p2 := Then(p1, func(data string) string { + return data + "de" + }) + + val, err := p1.Await() + assert.IsNil(err) + assert.Equal("abc", val) + + val, err = p2.Await() + assert.IsNil(err) + assert.Equal("abcde", val) +} + +func TestPromise_Then(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPromise_Then") + + p1 := New(func(resolve func(int), reject func(error)) { + resolve(1) + }) + + p2 := p1.Then(func(n int) int { + return n + 2 + }) + + val, err := p1.Await() + assert.IsNil(err) + assert.Equal(1, val) + + val, err = p2.Await() + assert.IsNil(err) + assert.Equal(3, val) +} + +func TestCatch(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestCatch") + + p1 := New(func(resolve func(string), reject func(error)) { + err := errors.New("error1") + reject(err) + }) + + p2 := Catch(p1, func(err error) error { + e := errors.New("error2") + return internal.JoinError(err, e) + }) + + val, err := p1.Await() + assert.Equal("", val) + assert.IsNotNil(err) + assert.Equal("error1", err.Error()) + + val, err = p2.Await() + + assert.Equal("", val) + assert.IsNotNil(err) + assert.Equal("error1\nerror2", err.Error()) +} + +func TestPromise_Catch(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPromise_Catch") + + p1 := New(func(resolve func(string), reject func(error)) { + err := errors.New("error1") + reject(err) + }) + + p2 := p1.Catch(func(err error) error { + e := errors.New("error2") + return internal.JoinError(err, e) + }) + + val, err := p1.Await() + assert.Equal("", val) + assert.IsNotNil(err) + assert.Equal("error1", err.Error()) + + val, err = p2.Await() + + assert.Equal("", val) + assert.IsNotNil(err) + assert.Equal("error1\nerror2", err.Error()) +} + +func TestAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPromise_All") + + t.Run("AllPromisesFullfilled", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + resolve("a") + }) + p2 := New(func(resolve func(string), reject func(error)) { + resolve("b") + }) + p3 := New(func(resolve func(string), reject func(error)) { + resolve("c") + }) + + p := All([]*Promise[string]{p1, p2, p3}) + + val, err := p.Await() + assert.Equal([]string{"a", "b", "c"}, val) + assert.IsNil(err) + }) + + t.Run("EmptyPromises", func(_ *testing.T) { + var empty = []*Promise[any]{} + p := All(empty) + assert.IsNil(p) + }) + + t.Run("PromisesContainRejected", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + resolve("a") + }) + p2 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error1")) + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error2")) + }) + + p := All([]*Promise[string]{p1, p2, p3}) + + _, err := p.Await() + + assert.IsNotNil(err) + // assert.Equal("error1", err.Error()) + }) + + t.Run("PromisesOnlyRejected", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error1")) + + }) + p2 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error2")) + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error3")) + }) + + p := All([]*Promise[string]{p1, p2, p3}) + + _, err := p.Await() + + assert.IsNotNil(err) + }) + +} + +func TestAny(t *testing.T) { + assert := internal.NewAssert(t, "TestPromise_Any") + + t.Run("AnyFullfilled", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 250) + resolve("fast") + }) + p2 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 500) + resolve("slow") + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error")) + }) + + p := Any([]*Promise[string]{p1, p2, p3}) + + val, err := p.Await() + assert.Equal("fast", val) + assert.IsNil(err) + }) + + t.Run("EmptyPromises", func(_ *testing.T) { + var empty = []*Promise[any]{} + p := Any(empty) + assert.IsNil(p) + }) + + t.Run("OnlyRejected", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error1")) + }) + p2 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error2")) + }) + + p := Any([]*Promise[string]{p1, p2}) + + _, err := p.Await() + + assert.IsNotNil(err) + }) + +} + +func TestRace(t *testing.T) { + assert := internal.NewAssert(t, "TestPromise_Race") + + t.Run("PromisesFullfilled", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 100) + resolve("a") + }) + p2 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 300) + resolve("b") + }) + + p := Race([]*Promise[string]{p1, p2}) + + val, err := p.Await() + assert.Equal("a", val) + assert.IsNil(err) + }) + + t.Run("EmptyPromises", func(_ *testing.T) { + var empty = []*Promise[any]{} + p := Race(empty) + assert.IsNil(p) + }) + + t.Run("PromisesContainRejected", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + time.Sleep(time.Millisecond * 100) + resolve("a") + }) + p2 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error1")) + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error2")) + }) + + p := Race([]*Promise[string]{p1, p2, p3}) + + val, err := p.Await() + + assert.IsNotNil(err) + assert.Equal("", val) + }) + + t.Run("PromisesOnlyRejected", func(_ *testing.T) { + p1 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error1")) + + }) + p2 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error2")) + }) + p3 := New(func(resolve func(string), reject func(error)) { + reject(errors.New("error3")) + }) + + p := Race([]*Promise[string]{p1, p2, p3}) + + _, err := p.Await() + + assert.IsNotNil(err) + }) + +} diff --git a/random/random.go b/random/random.go index d84f5d37..993e23b5 100644 --- a/random/random.go +++ b/random/random.go @@ -8,36 +8,159 @@ import ( crand "crypto/rand" "fmt" "io" + "math" "math/rand" + "os" "time" + "unsafe" + + "github.com/duke-git/lancet/v2/mathutil" ) -// RandString generate random string -// see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go -func RandString(length int) string { - const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + MaximumCapacity = math.MaxInt32>>1 + 1 + Numeral = "0123456789" + LowwerLetters = "abcdefghijklmnopqrstuvwxyz" + UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?" + AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars +) - b := make([]byte, length) - r := rand.New(rand.NewSource(time.Now().UnixNano())) - for i := range b { - b[i] = letters[r.Int63()%int64(len(letters))] +var rn = rand.NewSource(time.Now().UnixNano()) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// RandBool generates a random boolean value (true or false). +// Play: https://go.dev/play/p/to6BLc26wBv +func RandBool() bool { + return rand.Intn(2) == 1 +} + +// RandBoolSlice generates a random boolean slice of specified length. +// Play: https://go.dev/play/p/o-VSjPjnILI +func RandBoolSlice(length int) []bool { + if length <= 0 { + return []bool{} } - return string(b) + + result := make([]bool, length) + for i := range result { + result[i] = RandBool() + } + + return result } -// RandInt generate random int between min and max, maybe min, not be max +// RandInt generate random int between [min, max). +// Play: https://go.dev/play/p/pXyyAAI5YxD func RandInt(min, max int) int { if min == max { return min } + if max < min { min, max = max, min } - r := rand.New(rand.NewSource(time.Now().UnixNano())) - return r.Intn(max-min) + min + + if min == 0 && max == math.MaxInt { + return rand.Int() + } + + return rand.Intn(max-min) + min +} + +// RandIntSlice generates a slice of random integers. +// The generated integers are between min and max (exclusive). +// Play: https://go.dev/play/p/GATTQ5xTEG8 +func RandIntSlice(length, min, max int) []int { + if length <= 0 || min > max { + return []int{} + } + + result := make([]int, length) + for i := range result { + result[i] = RandInt(min, max) + } + + return result } -// RandBytes generate random byte slice +// RandUniqueIntSlice generate a slice of random int of length that do not repeat. +// Play: https://go.dev/play/p/uBkRSOz73Ec +func RandUniqueIntSlice(length, min, max int) []int { + if min > max { + return []int{} + } + if length > max-min { + length = max - min + } + + nums := make([]int, length) + used := make(map[int]struct{}, length) + for i := 0; i < length; { + r := RandInt(min, max) + if _, use := used[r]; use { + continue + } + used[r] = struct{}{} + nums[i] = r + i++ + } + + return nums +} + +// RandFloat generate random float64 number between [min, max) with specific precision. +// Play: https://go.dev/play/p/zbD_tuobJtr +func RandFloat(min, max float64, precision int) float64 { + if min == max { + return min + } + + if max < min { + min, max = max, min + } + + n := rand.Float64()*(max-min) + min + + 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; { + r := RandFloat(min, max, precision) + if _, use := used[r]; use { + continue + } + used[r] = struct{}{} + nums[i] = r + i++ + } + + return nums +} + +// RandBytes generate random byte slice. +// Play: https://go.dev/play/p/EkiLESeXf8d func RandBytes(length int) []byte { if length < 1 { return []byte{} @@ -47,10 +170,168 @@ func RandBytes(length int) []byte { if _, err := io.ReadFull(crand.Reader, b); err != nil { return nil } + return b } -// UUIdV4 generate a random UUID of version 4 according to RFC 4122 +// RandString generate random alphabeta string of specified length. +// Play: https://go.dev/play/p/W2xvRUXA7Mi +func RandString(length int) string { + return random(Letters, length) +} + +// RandString generate a slice of random string of length strLen based on charset. +// chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters +// random.Letters, random.SymbolChars, random.AllChars. or a combination of them. +// Play: https://go.dev/play/p/2_-PiDv3tGn +func RandStringSlice(charset string, sliceLen, strLen int) []string { + if sliceLen <= 0 || strLen <= 0 { + return []string{} + } + + result := make([]string, sliceLen) + + for i := range result { + result[i] = random(charset, strLen) + } + + return result +} + +// RandFromGivenSlice generate a random element from given slice. +// Play: https://go.dev/play/p/UrkWueF6yYo +func RandFromGivenSlice[T any](slice []T) T { + if len(slice) == 0 { + var zero T + return zero + } + return slice[rand.Intn(len(slice))] +} + +// RandSliceFromGivenSlice generate a random slice of length num from given slice. +// - If repeatable is true, the generated slice may contain duplicate elements. +// +// Play: https://go.dev/play/p/68UikN9d6VT +func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T { + if num <= 0 || len(slice) == 0 { + return slice + } + + if !repeatable && num > len(slice) { + num = len(slice) + } + + result := make([]T, num) + if repeatable { + for i := range result { + result[i] = slice[rand.Intn(len(slice))] + } + } else { + shuffled := make([]T, len(slice)) + copy(shuffled, slice) + rand.Shuffle(len(shuffled), func(i, j int) { + shuffled[i], shuffled[j] = shuffled[j], shuffled[i] + }) + result = shuffled[:num] + } + return result +} + +// RandUpper generate a random upper case string of specified length. +// Play: https://go.dev/play/p/29QfOh0DVuh +func RandUpper(length int) string { + return random(UpperLetters, length) +} + +// RandLower generate a random lower case string of specified length. +// Play: https://go.dev/play/p/XJtZ471cmtI +func RandLower(length int) string { + return random(LowwerLetters, length) +} + +// RandNumeral generate a random numeral string of specified length. +// Play: https://go.dev/play/p/g4JWVpHsJcf +func RandNumeral(length int) string { + return random(Numeral, length) +} + +// RandNumeralOrLetter generate a random numeral or alpha string of specified length. +// Play: https://go.dev/play/p/19CEQvpx2jD +func RandNumeralOrLetter(length int) string { + return random(Numeral+Letters, length) +} + +// RandSymbolChar generate a random symbol char of specified length. +// symbol chars: !@#$%^&*()_+-=[]{}|;':\",./<>?. +// Play: https://go.dev/play/p/Im6ZJxAykOm +func RandSymbolChar(length int) string { + return random(SymbolChars, length) +} + +// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数 +func nearestPowerOfTwo(cap int) int { + n := cap - 1 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + if n < 0 { + return 1 + } else if n >= MaximumCapacity { + return MaximumCapacity + } + return n + 1 +} + +// random generate a random string based on given string range. +func random(s string, length int) string { + // 确保随机数生成器的种子是动态的 + pid := os.Getpid() + timestamp := time.Now().UnixNano() + rand.Seed(int64(pid) + timestamp) + + // 仿照strings.Builder + // 创建一个长度为 length 的字节切片 + bytes := make([]byte, length) + strLength := len(s) + if strLength <= 0 { + return "" + } else if strLength == 1 { + for i := 0; i < length; i++ { + bytes[i] = s[0] + } + return *(*string)(unsafe.Pointer(&bytes)) + } + // s的字符需要使用多少个比特位数才能表示完 + // letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快 + letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength)))) + // 最大的字母id掩码 + var letterIdMask int64 = 1<= 0; { + // 检查随机数生成器是否用尽所有随机数 + if remain == 0 { + cache, remain = rn.Int63(), letterIdMax + } + // 从可用字符的字符串中随机选择一个字符 + if idx := int(cache & letterIdMask); idx < strLength { + bytes[i] = s[idx] + i-- + } + // 右移比特位数,为下次选择字符做准备 + cache >>= letterIdBits + remain-- + } + // 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝 + // 将字节切片转换为字符串并返回 + return *(*string)(unsafe.Pointer(&bytes)) +} + +// UUIdV4 generate a random UUID of version 4 according to RFC 4122. +// Play: https://go.dev/play/p/_Z9SFmr28ft func UUIdV4() (string, error) { uuid := make([]byte, 16) @@ -64,3 +345,13 @@ func UUIdV4() (string, error) { return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil } + +// RandNumberOfLength 生成一个长度为len的随机数 +// Play: https://go.dev/play/p/oyZbuV7bu7b +func RandNumberOfLength(len int) int { + m := int(math.Pow10(len) - 1) + i := int(math.Pow10(len - 1)) + ret := rand.Intn(m-i+1) + i + + return ret +} diff --git a/random/random_example_test.go b/random/random_example_test.go new file mode 100644 index 00000000..05cbaec2 --- /dev/null +++ b/random/random_example_test.go @@ -0,0 +1,202 @@ +package random + +import ( + "fmt" + "regexp" + "strconv" +) + +func ExampleRandInt() { + result := RandInt(1, 10) + + if result >= 1 && result < 10 { + fmt.Println("ok") + } + + // Output: + // ok +} + +func ExampleRandBytes() { + bytes := RandBytes(4) + + fmt.Println(len(bytes)) + + // Output: + // 4 +} + +func ExampleRandString() { + pattern := `^[a-zA-Z]+$` + reg := regexp.MustCompile(pattern) + + s := RandString(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleRandFromGivenSlice() { + goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", + "mango", "nectarine", "orange"} + + result := RandFromGivenSlice(goods) + fmt.Println(result) +} + +func ExampleRandSliceFromGivenSlice() { + goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", + "mango", "nectarine", "orange"} + chosen3goods := RandSliceFromGivenSlice(goods, 3, false) + fmt.Println(chosen3goods) +} + +func ExampleRandUpper() { + pattern := `^[A-Z]+$` + reg := regexp.MustCompile(pattern) + + s := RandUpper(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleRandLower() { + pattern := `^[a-z]+$` + reg := regexp.MustCompile(pattern) + + s := RandLower(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleRandNumeral() { + pattern := `^[0-9]+$` + reg := regexp.MustCompile(pattern) + + s := RandNumeral(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleRandNumeralOrLetter() { + pattern := `^[0-9a-zA-Z]+$` + reg := regexp.MustCompile(pattern) + + s := RandNumeralOrLetter(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleUUIdV4() { + pattern := `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$` + reg := regexp.MustCompile(pattern) + + s, _ := UUIdV4() + + result := reg.MatchString(s) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleRandUniqueIntSlice() { + result := RandUniqueIntSlice(5, 0, 10) + + if len(result) == 5 { + fmt.Println("ok") + } + + // Output: + // ok +} + +func ExampleRandSymbolChar() { + pattern := `^[\W|_]+$` + reg := regexp.MustCompile(pattern) + + s := RandSymbolChar(6) + + result1 := reg.MatchString(s) + result2 := len(s) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // 6 +} + +func ExampleRandFloat() { + pattern := `^[\d{1}.\d{2}]+$` + reg := regexp.MustCompile(pattern) + + num := RandFloat(1.0, 5.0, 2) + + // check num is a random float in [1.0, 5.0) + result1 := num >= 1.0 && num < 5.0 + result2 := reg.MatchString(strconv.FormatFloat(num, 'f', -1, 64)) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // true +} + +func ExampleRandFloats() { + isInRange := true + numbers := RandFloats(5, 1.0, 5.0, 2) + for _, n := range numbers { + isInRange = (n >= 1.0 && n < 5.0) + } + + fmt.Println(isInRange) + fmt.Println(len(numbers)) + + // Output: + // true + // 5 +} diff --git a/random/random_test.go b/random/random_test.go index 82fc19ac..602cb15b 100644 --- a/random/random_test.go +++ b/random/random_test.go @@ -3,12 +3,16 @@ package random import ( "reflect" "regexp" + "strconv" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" + "github.com/duke-git/lancet/v2/validator" ) func TestRandString(t *testing.T) { + t.Parallel() + pattern := `^[a-zA-Z]+$` reg := regexp.MustCompile(pattern) @@ -19,7 +23,61 @@ func TestRandString(t *testing.T) { assert.Equal(true, reg.MatchString(randStr)) } +func TestRandUpper(t *testing.T) { + t.Parallel() + + pattern := `^[A-Z]+$` + reg := regexp.MustCompile(pattern) + + randStr := RandUpper(6) + + assert := internal.NewAssert(t, "TestRandUpper") + assert.Equal(6, len(randStr)) + assert.Equal(true, reg.MatchString(randStr)) +} + +func TestRandLower(t *testing.T) { + t.Parallel() + + pattern := `^[a-z]+$` + reg := regexp.MustCompile(pattern) + + randStr := RandLower(6) + + assert := internal.NewAssert(t, "TestRandLower") + assert.Equal(6, len(randStr)) + assert.Equal(true, reg.MatchString(randStr)) +} + +func TestRandNumeral(t *testing.T) { + t.Parallel() + + pattern := `^[0-9]+$` + reg := regexp.MustCompile(pattern) + + randStr := RandNumeral(12) + + assert := internal.NewAssert(t, "TestRandNumeral") + assert.Equal(12, len(randStr)) + assert.Equal(true, reg.MatchString(randStr)) +} + +func TestRandNumeralOrLetter(t *testing.T) { + t.Parallel() + + pattern := `^[0-9a-zA-Z]+$` + reg := regexp.MustCompile(pattern) + + randStr := RandNumeralOrLetter(10) + + assert := internal.NewAssert(t, "TestRandNumeralOrLetter") + assert.Equal(10, len(randStr)) + assert.Equal(true, reg.MatchString(randStr)) +} + func TestRandInt(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandInt") r1 := RandInt(1, 10) @@ -35,6 +93,8 @@ func TestRandInt(t *testing.T) { } func TestRandBytes(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandBytes") randBytes := RandBytes(4) @@ -49,14 +109,270 @@ func TestRandBytes(t *testing.T) { } func TestUUIdV4(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestUUIdV4") uuid, err := UUIdV4() if err != nil { - t.Log(err) t.Fail() } isUUiDV4 := regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$`) assert.Equal(true, isUUiDV4.MatchString(uuid)) } + +func TestRandUniqueIntSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRandUniqueIntSlice") + + r1 := RandUniqueIntSlice(5, 0, 9) + assert.Equal(len(r1), 5) + if hasDuplicate(r1) { + t.Error("hasDuplicate int") + } + + r2 := RandUniqueIntSlice(20, 0, 10) + assert.Equal(len(r2), 10) + if hasDuplicate(r2) { + t.Error("hasDuplicate int") + } + + r3 := RandUniqueIntSlice(10, 20, 10) + assert.Equal(len(r3), 0) + + r4 := RandUniqueIntSlice(0, 20, 10) + assert.Equal(len(r4), 0) +} + +func hasDuplicate(arr []int) bool { + elements := make(map[int]bool) + for _, v := range arr { + if elements[v] { + return true + } + elements[v] = true + } + return false +} + +func TestRandSymbolChar(t *testing.T) { + t.Parallel() + + pattern := `^[\W|_]+$` + reg := regexp.MustCompile(pattern) + + symbolChars := RandSymbolChar(10) + + assert := internal.NewAssert(t, "TestRandSymbolChar") + assert.Equal(10, len(symbolChars)) + assert.Equal(true, reg.MatchString(symbolChars)) +} + +func TestRandFloat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRandFloat") + + r1 := RandFloat(1.1, 10.1, 2) + assert.GreaterOrEqual(r1, 1.1) + assert.Less(r1, 10.1) + + r2 := RandFloat(1.1, 1.1, 2) + assert.Equal(1.1, r2) + + r3 := RandFloat(10.1, 1.1, 2) + assert.GreaterOrEqual(r1, 1.1) + assert.Less(r3, 10.1) +} + +func TestRandFloats(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandFloats") + + numbers := RandFloats(5, 1.0, 5.0, 2) + for _, n := range numbers { + assert.Equal(true, (n >= 1.0 && n < 5.0)) + } + + 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) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandIntSlice") + + t.Run("empty slice", func(t *testing.T) { + numbers := RandIntSlice(-1, 1, 5) + assert.Equal([]int{}, numbers) + + numbers = RandIntSlice(0, 1, 5) + assert.Equal([]int{}, numbers) + + numbers = RandIntSlice(3, 5, 1) + assert.Equal([]int{}, numbers) + }) + + t.Run("random int slice", func(t *testing.T) { + numbers := RandIntSlice(5, 1, 1) + assert.Equal([]int{1, 1, 1, 1, 1}, numbers) + + numbers = RandIntSlice(5, 1, 5) + assert.Equal(5, len(numbers)) + }) +} + +func TestRandStringSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandStringSlice") + + t.Run("empty slice", func(t *testing.T) { + strs := RandStringSlice(Letters, -1, -1) + assert.Equal([]string{}, strs) + + strs = RandStringSlice(Letters, 0, 0) + assert.Equal([]string{}, strs) + + strs = RandStringSlice(Letters, -1, 0) + assert.Equal([]string{}, strs) + + strs = RandStringSlice(Letters, 0, -1) + assert.Equal([]string{}, strs) + + strs = RandStringSlice(Letters, 1, 0) + assert.Equal([]string{}, strs) + + strs = RandStringSlice(Letters, 0, 1) + assert.Equal([]string{}, strs) + }) + + t.Run("random string slice", func(t *testing.T) { + strs := RandStringSlice(Letters, 4, 6) + assert.Equal(4, len(strs)) + + for _, s := range strs { + assert.Equal(true, validator.IsAlpha(s)) + assert.Equal(6, len(s)) + } + }) + + // fail test: chinese character is not supported for now + // t.Run("random string slice of chinese ", func(t *testing.T) { + // strs := RandStringSlice("你好你好你好你好你好你好你好你好你好", 4, 6) + // t.Log(strs) + + // assert.Equal(4, len(strs)) + // for _, s := range strs { + // assert.Equal(true, validator.ContainChinese(s)) + // assert.Equal(6, len(s)) + // } + // }) +} + +func TestRandFromGivenSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandFromGivenSlice") + + randomSet := []any{"a", 8, "王", true, 1.1} + result := RandFromGivenSlice(randomSet) + find := false + for _, v := range randomSet { + if v == result { + find = true + } + } + assert.Equal(true, find) + + emptyAnyRandomSet := []any{} + emptyAnyResult := RandFromGivenSlice(emptyAnyRandomSet) + assert.IsNil(emptyAnyResult) + + emptyIntRandomSet := []int{} + emtpyIntResult := RandFromGivenSlice(emptyIntRandomSet) + assert.Equal(0, emtpyIntResult) +} + +func TestRandSliceFromGivenSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandSliceFromGivenSlice") + + randomSet := []any{"a", 8, "王", true, 1.1} + repeatableResult := RandSliceFromGivenSlice(randomSet, 8, true) + assert.Equal(8, len(repeatableResult)) + unrepeatableResult := RandSliceFromGivenSlice(randomSet, 8, false) + assert.Equal(len(randomSet), len(unrepeatableResult)) + + var findCount int + for _, v := range repeatableResult { + for _, vv := range randomSet { + if v == vv { + findCount++ + } + } + } + assert.Equal(8, findCount) + findCount = 0 + + for _, v := range unrepeatableResult { + for _, vv := range randomSet { + if v == vv { + findCount++ + } + } + } + assert.Equal(len(randomSet), findCount) + + emptyAnyRandomSet := []any{} + emptyAnyResult := RandSliceFromGivenSlice(emptyAnyRandomSet, 3, true) + assert.Equal([]any{}, emptyAnyResult) + + emptyIntRandomSet := []int{} + emtpyIntResult := RandSliceFromGivenSlice(emptyIntRandomSet, 3, true) + assert.Equal([]int{}, emtpyIntResult) + +} + +func TestRandBool(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandBool") + + result := RandBool() + assert.Equal(true, result == true || result == false) +} + +func TestRandBoolSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandBoolSlice") + + t.Run("empty slice", func(t *testing.T) { + bools := RandBoolSlice(-1) + assert.Equal([]bool{}, bools) + + bools = RandBoolSlice(0) + assert.Equal([]bool{}, bools) + }) + + t.Run("random bool slice", func(t *testing.T) { + bools := RandBoolSlice(6) + assert.Equal(6, len(bools)) + + for _, b := range bools { + assert.Equal(true, b == true || b == false) + } + }) +} +func TestRandNumberOfLength(t *testing.T) { + t.Parallel() + randi := RandNumberOfLength(6) + assert := internal.NewAssert(t, "TestRandNumberOfLength") + assert.Equal(6, len(strconv.Itoa(randi))) +} diff --git a/retry/retry.go b/retry/retry.go index 59bbdfdd..c5533c1e 100644 --- a/retry/retry.go +++ b/retry/retry.go @@ -8,6 +8,8 @@ import ( "context" "errors" "fmt" + "math" + "math/rand" "reflect" "runtime" "strings" @@ -18,14 +20,14 @@ const ( // DefaultRetryTimes times of retry DefaultRetryTimes = 5 // DefaultRetryDuration time duration of two retries - DefaultRetryDuration = time.Second * 3 + DefaultRetryLinearInterval = time.Second * 3 ) // RetryConfig is config for retry type RetryConfig struct { - context context.Context - retryTimes uint - retryDuration time.Duration + context context.Context + retryTimes uint + backoffStrategy BackoffStrategy } // RetryFunc is function that retry executes @@ -34,21 +36,72 @@ type RetryFunc func() error // Option is for adding retry config type Option func(*RetryConfig) -// RetryTimes set times of retry +// RetryTimes set times of retry. +// Play: https://go.dev/play/p/ssfVeU2SwLO func RetryTimes(n uint) Option { return func(rc *RetryConfig) { rc.retryTimes = n } } -// RetryDuration set duration of retries -func RetryDuration(d time.Duration) Option { +// RetryWithCustomBackoff set abitary custom backoff strategy +// Play: https://go.dev/play/p/jIm_o2vb5Y4 +func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option { + if backoffStrategy == nil { + panic("programming error: backoffStrategy must be not nil") + } + + return func(rc *RetryConfig) { + rc.backoffStrategy = backoffStrategy + } +} + +// RetryWithLinearBackoff set linear strategy backoff +// Play: https://go.dev/play/p/PDet2ZQZwcB +func RetryWithLinearBackoff(interval time.Duration) Option { + if interval <= 0 { + panic("programming error: retry interval should not be lower or equal to 0") + } + return func(rc *RetryConfig) { - rc.retryDuration = d + rc.backoffStrategy = &linear{ + interval: interval, + } } } -// Context set retry context config +// RetryWithExponentialWithJitterBackoff set exponential strategy backoff +// Play: https://go.dev/play/p/xp1avQmn16X +func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option { + if interval <= 0 { + panic("programming error: retry interval should not be lower or equal to 0") + } + + if maxJitter < 0 { + panic("programming error: retry maxJitter should not be lower to 0") + } + + if base%2 == 0 { + return func(rc *RetryConfig) { + rc.backoffStrategy = &shiftExponentialWithJitter{ + interval: interval, + maxJitter: maxJitter, + shifter: uint64(math.Log2(float64(base))), + } + } + } + + return func(rc *RetryConfig) { + rc.backoffStrategy = &exponentialWithJitter{ + interval: interval, + base: time.Duration(base), + maxJitter: maxJitter, + } + } +} + +// Context set retry context config. +// Play: https://go.dev/play/p/xnAOOXv9GkS func Context(ctx context.Context) Option { return func(rc *RetryConfig) { rc.context = ctx @@ -56,29 +109,38 @@ func Context(ctx context.Context) Option { } // Retry executes the retryFunc repeatedly until it was successful or canceled by the context -// The default times of retries is 5 and the default duration between retries is 3 seconds +// The default times of retries is 5 and the default duration between retries is 3 seconds. +// Play: https://go.dev/play/p/nk2XRmagfVF func Retry(retryFunc RetryFunc, opts ...Option) error { config := &RetryConfig{ - retryTimes: DefaultRetryTimes, - retryDuration: DefaultRetryDuration, - context: context.TODO(), + retryTimes: DefaultRetryTimes, + context: context.TODO(), } for _, opt := range opts { opt(config) } + if config.backoffStrategy == nil { + config.backoffStrategy = &linear{ + interval: DefaultRetryLinearInterval, + } + } + 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.retryDuration): + case <-time.After(config.backoffStrategy.CalculateInterval()): case <-config.context.Done(): return errors.New("retry is cancelled") } - } else { - return nil } i++ } @@ -87,5 +149,60 @@ 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. +type BackoffStrategy interface { + // CalculateInterval returns the time.Duration after which the next retry attempt should be made. + CalculateInterval() time.Duration +} + +// linear is a struct that implements the BackoffStrategy interface using a linear backoff strategy. +type linear struct { + // interval specifies the fixed duration to wait between retry attempts. + interval time.Duration +} + +// CalculateInterval calculates the next interval returns a constant. +func (l *linear) CalculateInterval() time.Duration { + return l.interval +} + +// exponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy. +type exponentialWithJitter struct { + base time.Duration // base is the multiplier for the exponential backoff. + interval time.Duration // interval is the current backoff interval, which will be adjusted over time. + maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval. +} + +// CalculateInterval calculates the next backoff interval with jitter and updates the interval. +func (e *exponentialWithJitter) CalculateInterval() time.Duration { + current := e.interval + e.interval = e.interval * e.base + return current + jitter(e.maxJitter) +} + +// shiftExponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy. +type shiftExponentialWithJitter struct { + interval time.Duration // interval is the current backoff interval, which will be adjusted over time. + maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval. + shifter uint64 // shift by n faster than multiplication +} + +// CalculateInterval calculates the next backoff interval with jitter and updates the interval. +// Uses shift instead of multiplication +func (e *shiftExponentialWithJitter) CalculateInterval() time.Duration { + current := e.interval + e.interval = e.interval << e.shifter + return current + jitter(e.maxJitter) +} + +// Jitter adds a random duration, up to maxJitter, +// to the current interval to introduce randomness and avoid synchronized patterns in retry behavior +func jitter(maxJitter time.Duration) time.Duration { + if maxJitter == 0 { + return 0 + } + return time.Duration(rand.Int63n(int64(maxJitter)) + 1) } diff --git a/retry/retry_example_test.go b/retry/retry_example_test.go new file mode 100644 index 00000000..20cfcda4 --- /dev/null +++ b/retry/retry_example_test.go @@ -0,0 +1,143 @@ +package retry + +import ( + "context" + "errors" + "fmt" + "time" +) + +func ExampleContext() { + ctx, cancel := context.WithCancel(context.TODO()) + + number := 0 + increaseNumber := func() error { + number++ + if number > 3 { + cancel() + } + return errors.New("error occurs") + } + + Retry(increaseNumber, + RetryWithLinearBackoff(time.Microsecond*50), + Context(ctx), + ) + + fmt.Println(number) + + // Output: + // 4 +} + +func ExampleRetryWithLinearBackoff() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} + +type ExampleCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func ExampleRetryWithCustomBackoff() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50})) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} + +func ExampleRetryWithExponentialWithJitterBackoff() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} + +func ExampleRetryTimes() { + number := 0 + + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryTimes(2)) + if err != nil { + fmt.Println(err) + } + + // Output: + // function retry.ExampleRetryTimes.func1 run failed after 2 times retry, last error: error occurs +} + +func ExampleRetry() { + number := 0 + increaseNumber := func() error { + number++ + if number == 3 { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50)) + if err != nil { + return + } + + fmt.Println(number) + + // Output: + // 3 +} diff --git a/retry/retry_test.go b/retry/retry_test.go index f2f0815e..ac2aac74 100644 --- a/retry/retry_test.go +++ b/retry/retry_test.go @@ -6,25 +6,105 @@ import ( "testing" "time" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestRetryFailed(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRetryFailed") + var number int + customError := errors.New("error occurs") + increaseNumber := func() error { + number++ + return customError + } + + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50)) + + assert.IsNotNil(err) + assert.Equal(errors.Is(err, customError), true) + assert.Equal(DefaultRetryTimes, number) +} + +func TestRetryShiftExponentialWithJitterFailed(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryShiftExponentialWithJitterFailed") + var number int increaseNumber := func() error { number++ return errors.New("error occurs") } - err := Retry(increaseNumber, RetryDuration(time.Microsecond*50)) + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25)) assert.IsNotNil(err) assert.Equal(DefaultRetryTimes, number) } +func TestRetryExponentialWithJitterFailed(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryExponentialWithJitterFailed") + + var number int + increaseNumber := func() error { + number++ + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25)) + + assert.IsNotNil(err) + assert.Equal(DefaultRetryTimes, number) +} + +func TestRetryWithExponentialSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWithExponentialSucceeded") + + var number int + increaseNumber := func() error { + number++ + if number == DefaultRetryTimes { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25)) + + assert.IsNil(err) + assert.Equal(DefaultRetryTimes, number) +} + +func TestRetryWithExponentialShiftSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWithExponentialShiftSucceeded") + + var number int + increaseNumber := func() error { + number++ + if number == DefaultRetryTimes { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 4, time.Microsecond*25)) + + assert.IsNil(err) + assert.Equal(DefaultRetryTimes, number) +} + func TestRetrySucceeded(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRetrySucceeded") var number int @@ -36,13 +116,111 @@ func TestRetrySucceeded(t *testing.T) { return errors.New("error occurs") } - err := Retry(increaseNumber, RetryDuration(time.Microsecond*50)) + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50)) assert.IsNil(err) assert.Equal(DefaultRetryTimes, number) } +func TestRetryOneShotSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryOneShotSucceeded") + + var number int + increaseNumber := func() error { + number++ + return nil + } + + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50)) + + assert.IsNil(err) + assert.Equal(1, number) +} + +func TestRetryWitCustomBackoffOneShotSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWitCustomBackoffOneShotSucceeded") + + var number int + increaseNumber := func() error { + number++ + if number == DefaultRetryTimes { + return nil + } + return errors.New("error occurs") + } + + err := Retry(increaseNumber, RetryWithCustomBackoff(&TestCustomBackoffStrategy{interval: time.Microsecond * 50})) + + assert.IsNil(err) + assert.Equal(5, number) +} + +type TestCustomBackoffStrategy struct { + interval time.Duration +} + +func (c *TestCustomBackoffStrategy) CalculateInterval() time.Duration { + return c.interval + 1 +} + +func TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded") + + var number int + increaseNumber := func() error { + number++ + return nil + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25)) + + assert.IsNil(err) + assert.Equal(1, number) +} + +func TestRetryWithExponentialWithJitterBackoffOneShotSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffOneShotSucceeded") + + var number int + increaseNumber := func() error { + number++ + return nil + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25)) + + assert.IsNil(err) + assert.Equal(1, number) +} + +func TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded") + + var number int + increaseNumber := func() error { + number++ + return nil + } + + err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, 0)) + + assert.IsNil(err) + assert.Equal(1, number) +} + func TestSetRetryTimes(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSetRetryTimes") var number int @@ -51,13 +229,15 @@ func TestSetRetryTimes(t *testing.T) { return errors.New("error occurs") } - err := Retry(increaseNumber, RetryDuration(time.Microsecond*50), RetryTimes(3)) + err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50), RetryTimes(3)) assert.IsNotNil(err) assert.Equal(3, number) } func TestCancelRetry(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCancelRetry") ctx, cancel := context.WithCancel(context.TODO()) @@ -71,7 +251,7 @@ func TestCancelRetry(t *testing.T) { } err := Retry(increaseNumber, - RetryDuration(time.Microsecond*50), + RetryWithLinearBackoff(time.Microsecond*50), Context(ctx), ) diff --git a/slice/slice.go b/slice/slice.go index b8ada2f1..1264c0cc 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -1,407 +1,494 @@ -// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Copyright 2021 dudaodong@gmail.com. All rights resulterved. // Use of this source code is governed by MIT license // Package slice implements some functions to manipulate slice. package slice import ( - "errors" "fmt" - "math" "math/rand" "reflect" "sort" "strings" + "sync" + "time" + + "github.com/duke-git/lancet/v2/random" + "golang.org/x/exp/constraints" ) -// Contain check if the value is in the iterable type or not -func Contain(iterableType interface{}, value interface{}) bool { - v := reflect.ValueOf(iterableType) +// Create a static variable to store the hash table. +// This variable has the same lifetime as the entire program and can be shared by functions that are called more than once. +var ( + memoryHashMap = make(map[string]map[any]int) + memoryHashCounter = make(map[string]int) + muForMemoryHash sync.RWMutex +) - switch kind := reflect.TypeOf(iterableType).Kind(); kind { - case reflect.Slice, reflect.Array: - for i := 0; i < v.Len(); i++ { - if v.Index(i).Interface() == value { - return true - } +// Contain check if the target value is in the slice or not. +// Play: https://go.dev/play/p/_454yEHcNjf +func Contain[T comparable](slice []T, target T) bool { + for _, item := range slice { + if item == target { + return true } + } + + return false +} - case reflect.Map: - if v.MapIndex(reflect.ValueOf(value)).IsValid() { +// ContainBy returns true if predicate function return true. +// Play: https://go.dev/play/p/49tkHfX4GNc +func ContainBy[T any](slice []T, predicate func(item T) bool) bool { + for _, item := range slice { + if predicate(item) { return true } - case reflect.String: - s := iterableType.(string) - ss, ok := value.(string) - if !ok { - panic("kind mismatch") - } - - return strings.Contains(s, ss) - default: - panic(fmt.Sprintf("kind %s is not support", iterableType)) } return false } -// ContainSubSlice check if the slice contain subslice or not -func ContainSubSlice(slice interface{}, subslice interface{}) bool { - super := sliceValue(slice) - sub := sliceValue(subslice) - - if super.Type().Elem().Kind() != sub.Type().Elem().Kind() { +// ContainSubSlice check if the slice contain a given subslice or not. +// Play: https://go.dev/play/p/bcuQ3UT6Sev +func ContainSubSlice[T comparable](slice, subSlice []T) bool { + if len(subSlice) == 0 { + return true + } + if len(slice) == 0 { return false } - unique := make(map[interface{}]bool) - for i := 0; i < super.Len(); i++ { - v := super.Index(i).Interface() - unique[v] = true + elementCount := make(map[T]int, len(slice)) + for _, item := range slice { + elementCount[item]++ } - for i := 0; i < sub.Len(); i++ { - v := sub.Index(i).Interface() - if !unique[v] { + + for _, item := range subSlice { + if elementCount[item] == 0 { return false } + elementCount[item]-- } return true } -// Chunk creates an slice of elements split into groups the length of `size`. -func Chunk(slice []interface{}, size int) [][]interface{} { - var res [][]interface{} +// 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 { + result := [][]T{} if len(slice) == 0 || size <= 0 { - return res + return result } - length := len(slice) - if size == 1 || size >= length { - for _, v := range slice { - var tmp []interface{} - tmp = append(tmp, v) - res = append(res, tmp) + currentChunk := []T{} + + for _, item := range slice { + if len(currentChunk) == size { + result = append(result, currentChunk) + currentChunk = []T{} } - return res + currentChunk = append(currentChunk, item) } - // divide slice equally - divideNum := length/size + 1 - for i := 0; i < divideNum; i++ { - if i == divideNum-1 { - if len(slice[i*size:]) > 0 { - res = append(res, slice[i*size:]) - } - } else { - res = append(res, slice[i*size:(i+1)*size]) - } + if len(currentChunk) > 0 { + result = append(result, currentChunk) } - return res + return result } -// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey -func Compact(slice interface{}) interface{} { - sv := sliceValue(slice) +// Compact creates a slice with all falsey values removed. The values false, nil, 0, and "" are falsey. +// Play: https://go.dev/play/p/pO5AnxEr3TK +func Compact[T comparable](slice []T) []T { + var zero T - var indexes []int - for i := 0; i < sv.Len(); i++ { - item := sv.Index(i).Interface() - if item != nil && item != false && item != "" && item != 0 { - indexes = append(indexes, i) + result := make([]T, 0, len(slice)) + + for _, v := range slice { + if v != zero { + result = append(result, v) } } - res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes)) - for i := range indexes { - res.Index(i).Set(sv.Index(indexes[i])) - } - return res.Interface() + return result[:len(result):len(result)] } -// Concat creates a new slice concatenating slice with any additional slices and/or values. -func Concat(slice interface{}, values ...interface{}) interface{} { - sv := sliceValue(slice) - size := sv.Len() - - res := reflect.MakeSlice(sv.Type(), size, size) - for i := 0; i < size; i++ { - res.Index(i).Set(sv.Index(i)) +// Concat creates a new slice concatenating slice with any additional slices. +// Play: https://go.dev/play/p/gPt-q7zr5mk +func Concat[T any](slices ...[]T) []T { + totalLen := 0 + for _, v := range slices { + totalLen += len(v) + if totalLen < 0 { + panic("len out of range") + } } + result := make([]T, 0, totalLen) - for _, v := range values { - if reflect.TypeOf(v).Kind() == reflect.Slice { - vv := reflect.ValueOf(v) - for i := 0; i < vv.Len(); i++ { - res = reflect.Append(res, vv.Index(i)) - } - } else { - res = reflect.Append(res, reflect.ValueOf(v)) - } + for _, v := range slices { + result = append(result, v...) } - return res.Interface() + return result } -// Difference creates an slice of whose element in slice1, not in slice2 -func Difference(slice1, slice2 interface{}) interface{} { - sv := sliceValue(slice1) +// Difference creates a slice of whose element in slice but not in comparedSlice. +// Play: https://go.dev/play/p/VXvadzLzhDa +func Difference[T comparable](slice, comparedSlice []T) []T { + result := []T{} - var indexes []int - for i := 0; i < sv.Len(); i++ { - item := sv.Index(i).Interface() - if !Contain(slice2, item) { - indexes = append(indexes, i) - } + if len(slice) == 0 { + return result } - res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes)) - for i := range indexes { - res.Index(i).Set(sv.Index(indexes[i])) + comparedMap := make(map[T]struct{}, len(comparedSlice)) + for _, v := range comparedSlice { + comparedMap[v] = struct{}{} } - return res.Interface() + + for _, v := range slice { + if _, found := comparedMap[v]; !found { + result = append(result, v) + } + } + + return result } // DifferenceBy it accepts iteratee which is invoked for each element of slice // and values to generate the criterion by which they're compared. -// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy, -// the iterateeFn function signature should be func(index int, value interface{}) interface{}. -func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} { - sv := sliceValue(slice) - smv := sliceValue(comparedSlice) - fn := functionValue(iterateeFn) +// 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 { + result := make([]T, 0) - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, nil) { - panic("function param should be of type func(" + elemType.String() + ")" + elemType.String()) + comparedMap := make(map[T]struct{}, len(comparedSlice)) + for _, item := range comparedSlice { + comparedMap[iteratee(0, item)] = struct{}{} } - slice1 := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len()) - for i := 0; i < sv.Len(); i++ { - slice1.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]) + for i, item := range slice { + transformedItem := iteratee(i, item) + if _, found := comparedMap[transformedItem]; !found { + result = append(result, item) + } } - slice2 := reflect.MakeSlice(smv.Type(), smv.Len(), smv.Len()) - for i := 0; i < smv.Len(); i++ { - slice2.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), smv.Index(i)})[0]) + return result +} + +// DifferenceWith accepts comparator which is invoked to compare elements of slice to values. +// The order and references of result values are determined by the first slice. +// 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 { + getIndex := func(arr []T, item T, comparison func(v1, v2 T) bool) int { + for i, v := range arr { + if comparison(item, v) { + return i + } + } + return -1 } - sliceAfterMap := slice1.Interface() - comparedSliceAfterMap := slice2.Interface() + result := make([]T, 0, len(slice)) - res := reflect.MakeSlice(sv.Type(), 0, 0) - sm := sliceValue(sliceAfterMap) - for i := 0; i < sm.Len(); i++ { - item := sm.Index(i).Interface() - if !Contain(comparedSliceAfterMap, item) { - res = reflect.Append(res, sv.Index(i)) + 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 + } + } + if !found { + result = append(result, v) } } - return res.Interface() + return result } -// Every return true if all of the values in the slice pass the predicate function. -// The function signature should be func(index int, value interface{}) bool . -func Every(slice, function interface{}) bool { - sv := sliceValue(slice) - fn := functionValue(function) - - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) +// Equal checks if two slices are equal: the same length and all elements' order and value are equal. +// Play: https://go.dev/play/p/WcRQJ37ifPa +func Equal[T comparable](slice1, slice2 []T) bool { + if len(slice1) != len(slice2) { + return false } - var currentLength int - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { - currentLength++ + for i := range slice1 { + if slice1[i] != slice2[i] { + return false } } - return currentLength == sv.Len() + return true } -// None return true if all the values in the slice mismatch the criteria -// The function signature should be func(index int, value interface{}) bool . -func None(slice, function interface{}) bool { - sv := sliceValue(slice) - fn := functionValue(function) - - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) +// EqualWith checks if two slices are equal with comparator func. +// Play: https://go.dev/play/p/b9iygtgsHI1 +func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) bool { + if len(slice1) != len(slice2) { + return false } - var currentLength int - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if !flag.Bool() { - currentLength++ + for i, v := range slice1 { + if !comparator(v, slice2[i]) { + return false } } - return currentLength == sv.Len() + return true } -// Some return true if any of the values in the list pass the predicate function. -// The function signature should be func(index int, value interface{}) bool . -func Some(slice, function interface{}) bool { - sv := sliceValue(slice) - fn := functionValue(function) +// 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 + } - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) + seen := make(map[T]int) + for _, v := range slice1 { + seen[v]++ } - has := false - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { - has = true + for _, v := range slice2 { + if seen[v] == 0 { + return false } + seen[v]-- } - return has + return true } -// Filter iterates over elements of slice, returning an slice of all elements `signature` returns truthy for. -// The function signature should be func(index int, value interface{}) bool . -func Filter(slice, function interface{}) interface{} { - sv := sliceValue(slice) - fn := functionValue(function) +// 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 { + for i, v := range slice { + if !predicate(i, v) { + return false + } + } - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) + return true +} + +// None return true if all the values in the slice mismatch the criteria. +// Play: https://go.dev/play/p/KimdalUlC-T +func None[T any](slice []T, predicate func(index int, item T) bool) bool { + l := 0 + for i, v := range slice { + if !predicate(i, v) { + l++ + } } - res := reflect.MakeSlice(sv.Type(), 0, 0) - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { - res = reflect.Append(res, sv.Index(i)) + return l == len(slice) +} + +// Some return true if any of the values in the list pass the predicate function. +// Play: https://go.dev/play/p/4pO9Xf9NDGS +func Some[T any](slice []T, predicate func(index int, item T) bool) bool { + for i, v := range slice { + if predicate(i, v) { + return true } } - return res.Interface() + return false } -// Count iterates over elements of slice, returns a count of all matched elements -// The function signature should be func(index int, value interface{}) bool . -func Count(slice, function interface{}) int { - sv := sliceValue(slice) - fn := functionValue(function) +// Filter iterates over elements of slice, returning an slice of all elements pass the predicate function. +// Play: https://go.dev/play/p/SdPna-7qK4T +func Filter[T any](slice []T, predicate func(index int, item T) bool) []T { + result := make([]T, 0) - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) + for i, v := range slice { + if predicate(i, v) { + result = append(result, v) + } } - var counter int - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { - counter++ + return result +} + +// Count returns the number of occurrences of the given item in the slice. +// Play: https://go.dev/play/p/Mj4oiEnQvRJ +func Count[T comparable](slice []T, item T) int { + count := 0 + + for _, v := range slice { + if item == v { + count++ } } - return counter + return count } -// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices -// The function signature should be func(index int, value interface{}) bool . -func GroupBy(slice, function interface{}) (interface{}, interface{}) { - sv := sliceValue(slice) - fn := functionValue(function) +// CountBy iterates over elements of slice with predicate function, returns the number of all matched elements. +// Play: https://go.dev/play/p/tHOccTMDZCC +func CountBy[T any](slice []T, predicate func(index int, item T) bool) int { + count := 0 - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) + for i, v := range slice { + if predicate(i, v) { + count++ + } } - groupB := reflect.MakeSlice(sv.Type(), 0, 0) - groupA := reflect.MakeSlice(sv.Type(), 0, 0) + return count +} - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { - groupA = reflect.Append(groupA, sv.Index(i)) +// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices. +// Play: https://go.dev/play/p/QVkPxzPR0iA +func GroupBy[T any](slice []T, groupFn func(index int, item T) bool) ([]T, []T) { + if len(slice) == 0 { + return make([]T, 0), make([]T, 0) + } + + groupB := make([]T, 0) + groupA := make([]T, 0) + + for i, v := range slice { + ok := groupFn(i, v) + if ok { + groupA = append(groupA, v) } else { - groupB = reflect.Append(groupB, sv.Index(i)) + groupB = append(groupB, v) } } - return groupA.Interface(), groupB.Interface() + return groupA, groupB } -// Find iterates over elements of slice, returning the first one that passes a truth test on function. -// The function signature should be func(index int, value interface{}) bool . -func Find(slice, function interface{}) (interface{}, bool) { - sv := sliceValue(slice) - fn := functionValue(function) +// GroupWith return a map composed of keys generated from the resultults of running each element of slice thru iteratee. +// Play: https://go.dev/play/p/ApCvMNTLO8a +func GroupWith[T any, U comparable](slice []T, iteratee func(item T) U) map[U][]T { + result := make(map[U][]T) - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) + for _, v := range slice { + key := iteratee(v) + if _, ok := result[key]; !ok { + result[key] = []T{} + } + result[key] = append(result[key], v) } + return result +} + +// Find iterates over elements of slice, returning the first one that passes a truth test on predicate function. +// If return T is nil then no items matched the predicate func. +// Play: https://go.dev/play/p/CBKeBoHVLgq +// Deprecated +func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) { + v, ok := FindBy(slice, predicate) + return &v, ok +} + +// FindLast iterates over elements of slice from end to begin, +// return the first one that passes a truth test on predicate function. +// If return T is nil then no items matched the predicate func. +// Play: https://go.dev/play/p/FFDPV_j7URd +// Deprecated +func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) { + v, ok := FindLastBy(slice, predicate) + return &v, ok +} + +// FindBy iterates over elements of slice, returning the first one that passes a truth test on predicate function. +// If return T is nil or zero value then no items matched the predicate func. +// In contrast to Find or FindLast, its return value no longer requires dereferencing +// Play: https://go.dev/play/p/n1lysBYl-GB +func FindBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) { index := -1 - for i := 0; i < sv.Len(); i++ { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { + + for i, v := range slice { + if predicate(i, v) { index = i break } } if index == -1 { - var none interface{} - return none, false + return v, false } - return sv.Index(index).Interface(), true + return slice[index], true } -// FindLast iterates over elements of slice from end to begin, returning the first one that passes a truth test on function. -// The function signature should be func(index int, value interface{}) bool . -func FindLast(slice, function interface{}) (interface{}, bool) { - sv := sliceValue(slice) - fn := functionValue(function) - - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) { - panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) - } - +// FindLastBy iterates over elements of slice, returning the last one that passes a truth test on predicate function. +// If return T is nil or zero value then no items matched the predicate func. +// In contrast to Find or FindLast, its return value no longer requires dereferencing +// Play: https://go.dev/play/p/8iqomzyCl_s +func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) { index := -1 - for i := sv.Len() - 1; i >= 0; i-- { - flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] - if flag.Bool() { + + for i := len(slice) - 1; i >= 0; i-- { + if predicate(i, slice[i]) { index = i break } } if index == -1 { - var none interface{} - return none, false + return v, false } - return sv.Index(index).Interface(), true + return slice[index], true } -// FlattenDeep flattens slice recursive -func FlattenDeep(slice interface{}) interface{} { +// Flatten flattens slice with one level. +// Play: https://go.dev/play/p/hYa3cBEevtm +func Flatten(slice any) any { + 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 := sv.Index(i) + if item.Kind() == reflect.Slice { + for j := 0; j < item.Len(); j++ { + result = reflect.Append(result, item.Index(j)) + } + } else { + result = reflect.Append(result, item) + } + } + + return result.Interface() +} + +// FlattenDeep flattens slice recursive. +// Play: https://go.dev/play/p/yjYNHPyCFaF +func FlattenDeep(slice any) any { sv := sliceValue(slice) st := sliceElemType(sv.Type()) + tmp := reflect.MakeSlice(reflect.SliceOf(st), 0, 0) - res := flattenRecursive(sv, tmp) - return res.Interface() + + result := flattenRecursive(sv, tmp) + + return result.Interface() } func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { @@ -419,357 +506,669 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { return result } -// ForEach iterates over elements of slice and invokes function for each element -// The function signature should be func(index int, value interface{}). -func ForEach(slice, function interface{}) { - sv := sliceValue(slice) - fn := functionValue(function) +// 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]) + } +} - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, nil) { - panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String()) +// 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 +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]) { + break + } } +} - for i := 0; i < sv.Len(); i++ { - fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)}) +// Map creates an slice of values by running each element of slice thru iteratee function. +// Play: https://go.dev/play/p/biaTefqPquw +func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U { + result := make([]U, len(slice), cap(slice)) + + for i := 0; i < len(slice); i++ { + result[i] = iteratee(i, slice[i]) } + + return result } -// Map creates an slice of values by running each element of `slice` thru `function`. -// The function signature should be func(index int, value interface{}) interface{}. -func Map(slice, function interface{}) interface{} { - sv := sliceValue(slice) - fn := functionValue(function) +// FilterMap returns a slice which apply both filtering and mapping to the given slice. +// iteratee callback function should returntwo values: +// 1, mapping result. +// 2, whether the result element should be included or not +// Play: https://go.dev/play/p/J94SZ_9MiIe +func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U { + result := []U{} + + for i, v := range slice { + if a, ok := iteratee(i, v); ok { + result = append(result, a) + } + } - elemType := sv.Type().Elem() - if checkSliceCallbackFuncSignature(fn, elemType, nil) { - panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String()) + return result +} + +// FlatMap manipulates a slice and transforms and flattens it to a slice of another type. +// Play: https://go.dev/play/p/_QARWlWs1N_F +func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U { + result := make([]U, 0, len(slice)) + + for i, v := range slice { + result = append(result, iteratee(i, v)...) } - res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len()) - for i := 0; i < sv.Len(); i++ { - res.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]) + return result +} + +// Reduce creates an slice of values by running each element of slice thru iteratee function. +// Play: https://go.dev/play/p/_RfXJJWIsIm +func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T { + accumulator := initial + + for i, v := range slice { + accumulator = iteratee(i, v, accumulator) } - return res.Interface() + + return accumulator } -// Reduce creates an slice of values by running each element of `slice` thru `function`. -// The function signature should be func(index int, value1, value2 interface{}) interface{} . -func Reduce(slice, function, zero interface{}) interface{} { - sv := sliceValue(slice) - elementType := sv.Type().Elem() +// ReduceBy produces a value from slice by accumulating the result of each element as passed through the reducer function. +// Play: https://go.dev/play/p/YKDpLi7gtee +func ReduceBy[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U { + accumulator := initial + + for i, v := range slice { + accumulator = reducer(i, v, accumulator) + } + + return accumulator +} + +// ReduceRight is like ReduceBy, but it iterates over elements of slice from right to left. +// Play: https://go.dev/play/p/qT9dZC03A1K +func ReduceRight[T any, U any](slice []T, initial U, reducer func(index int, item T, agg U) U) U { + accumulator := initial - len := sv.Len() - if len == 0 { - return zero - } else if len == 1 { - return sv.Index(0).Interface() + for i := len(slice) - 1; i >= 0; i-- { + accumulator = reducer(i, slice[i], accumulator) } - fn := functionValue(function) - if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) { - t := elementType.String() - panic("function param should be of type func(int, " + t + ", " + t + ")" + t) + return accumulator +} + +// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new. +// Play: https://go.dev/play/p/P5mZp7IhOFo +func Replace[T comparable](slice []T, old T, new T, n int) []T { + result := make([]T, len(slice)) + copy(result, slice) + + for i := range result { + if result[i] == old && n != 0 { + result[i] = new + n-- + } } - var params [3]reflect.Value - params[0] = reflect.ValueOf(0) - params[1] = sv.Index(0) - params[2] = sv.Index(1) + return result +} - res := fn.Call(params[:])[0] +// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new. +// Play: https://go.dev/play/p/CzqXMsuYUrx +func ReplaceAll[T comparable](slice []T, old T, new T) []T { + return Replace(slice, old, new, -1) +} - for i := 2; i < len; i++ { - params[0] = reflect.ValueOf(i) - params[1] = res - params[2] = sv.Index(i) - res = fn.Call(params[:])[0] +// Repeat creates a slice with length n whose elements are param `item`. +// Play: https://go.dev/play/p/1CbOmtgILUU +func Repeat[T any](item T, n int) []T { + result := make([]T, n) + + for i := range result { + result[i] = item } - return res.Interface() + return result } // InterfaceSlice convert param to slice of interface. -func InterfaceSlice(slice interface{}) []interface{} { +// deprecated: use generics feature of go1.18+ for replacement. +// Play: https://go.dev/play/p/FdQXF0Vvqs- +func InterfaceSlice(slice any) []any { sv := sliceValue(slice) if sv.IsNil() { return nil } - res := make([]interface{}, sv.Len()) + result := make([]any, sv.Len()) for i := 0; i < sv.Len(); i++ { - res[i] = sv.Index(i).Interface() + result[i] = sv.Index(i).Interface() } - return res + return result } // StringSlice convert param to slice of string. -func StringSlice(slice interface{}) []string { +// deprecated: use generics feature of go1.18+ for replacement. +// Play: https://go.dev/play/p/W0TZDWCPFcI +func StringSlice(slice any) []string { v := sliceValue(slice) - out := make([]string, v.Len()) + result := make([]string, v.Len()) for i := 0; i < v.Len(); i++ { v, ok := v.Index(i).Interface().(string) if !ok { panic("invalid element type") } - out[i] = v + result[i] = v } - return out + return result } // IntSlice convert param to slice of int. -func IntSlice(slice interface{}) []int { +// 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) - out := make([]int, sv.Len()) + result := make([]int, sv.Len()) for i := 0; i < sv.Len(); i++ { v, ok := sv.Index(i).Interface().(int) if !ok { panic("invalid element type") } - out[i] = v + result[i] = v } - return out + return result } -// DeleteByIndex delete the element of slice from start index to end index - 1. -// Delete i: s = append(s[:i], s[i+1:]...) -// Delete i to j: s = append(s[:i], s[j:]...) -func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) { - v := sliceValue(slice) - i := start - if v.Len() == 0 || i < 0 || i > v.Len() { - return nil, errors.New("InvalidStartIndex") +// 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 < 0 || index >= len(slice) { + return slice[:len(slice)-1] } - if len(end) > 0 { - j := end[0] - if j <= i || j > v.Len() { - return nil, errors.New("InvalidEndIndex") - } - v = reflect.AppendSlice(v.Slice(0, i), v.Slice(j, v.Len())) - } else { - v = reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())) + + result := append([]T(nil), slice...) + copy(result[index:], result[index+1:]) + + // Set the last element to zero value, clean up the memory. + result[len(result)-1] = zeroValue[T]() + + return result[:len(result)-1] +} + +func zeroValue[T any]() T { + var zero T + return zero +} + +// DeleteRange delete the element of slice from start index to end index(exclude). +// Play: https://go.dev/play/p/945HwiNrnle +func DeleteRange[T any](slice []T, start, end int) []T { + result := make([]T, 0, len(slice)-(end-start)) + + for i := 0; i < start; i++ { + result = append(result, slice[i]) + } + + for i := end; i < len(slice); i++ { + result = append(result, slice[i]) } - return v.Interface(), nil + return result } -// Drop creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0 -func Drop(slice interface{}, n int) interface{} { - sv := sliceValue(slice) +// Drop drop n elements from the start of a slice. +// Play: https://go.dev/play/p/jnPO2yQsT8H +func Drop[T any](slice []T, n int) []T { + size := len(slice) - if n == 0 { + if size <= n { + return []T{} + } + + if n <= 0 { return slice } - svLen := sv.Len() + result := make([]T, 0, size-n) - if math.Abs(float64(n)) >= float64(svLen) { - return reflect.MakeSlice(sv.Type(), 0, 0).Interface() - } + return append(result, slice[n:]...) +} - if n > 0 { - res := reflect.MakeSlice(sv.Type(), svLen-n, svLen-n) - for i := 0; i < res.Len(); i++ { - res.Index(i).Set(sv.Index(i + n)) - } +// DropRight drop n elements from the end of a slice. +// Play: https://go.dev/play/p/8bcXvywZezG +func DropRight[T any](slice []T, n int) []T { + size := len(slice) - return res.Interface() + if size <= n { + return []T{} } - res := reflect.MakeSlice(sv.Type(), svLen+n, svLen+n) - for i := 0; i < res.Len(); i++ { - res.Index(i).Set(sv.Index(i)) + if n <= 0 { + return slice } - return res.Interface() + result := make([]T, 0, size-n) + + return append(result, slice[:size-n]...) } -// InsertByIndex insert the element into slice at index. -// Insert value: s = append(s[:i], append([]T{x}, s[i:]...)...) -// Insert slice: a = append(a[:i], append(b, a[i:]...)...) -func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) { - v := sliceValue(slice) +// DropWhile drop n elements from the start of a slice while predicate function returns true. +// Play: https://go.dev/play/p/4rt252UV_qs +func DropWhile[T any](slice []T, predicate func(item T) bool) []T { + i := 0 - if index < 0 || index > v.Len() { - return slice, errors.New("InvalidSliceIndex") + for ; i < len(slice); i++ { + if !predicate(slice[i]) { + break + } } - // value is slice - vv := reflect.ValueOf(value) - if vv.Kind() == reflect.Slice { - if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value).Elem() { - return slice, errors.New("InvalidValueType") + result := make([]T, 0, len(slice)-i) + + return append(result, slice[i:]...) +} + +// DropRightWhile drop n elements from the end of a slice while predicate function returns true. +// Play: https://go.dev/play/p/6wyK3zMY56e +func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T { + i := len(slice) - 1 + + for ; i >= 0; i-- { + if !predicate(slice[i]) { + break } - v = reflect.AppendSlice(v.Slice(0, index), reflect.AppendSlice(vv.Slice(0, vv.Len()), v.Slice(index, v.Len()))) - return v.Interface(), nil } - // value is not slice - if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) { - return slice, errors.New("InvalidValueType") + result := make([]T, 0, i+1) + + return append(result, slice[:i+1]...) +} + +// InsertAt insert the value or other slice into slice at index. +// Play: https://go.dev/play/p/hMLNxPEGJVE +func InsertAt[T any](slice []T, index int, value any) []T { + size := len(slice) + + if index < 0 || index > size { + 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: + return slice } - if index == v.Len() { - return reflect.Append(v, reflect.ValueOf(value)).Interface(), nil +} + +// 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 { + if index < 0 || index >= len(slice) { + return slice } - v = reflect.AppendSlice(v.Slice(0, index+1), v.Slice(index, v.Len())) - v.Index(index).Set(reflect.ValueOf(value)) + result := make([]T, len(slice)) + copy(result, slice) + + result[index] = value - return v.Interface(), nil + return result } -// UpdateByIndex update the slice element at index. -func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) { - v := sliceValue(slice) +// Unique remove duplicate elements in slice. +// Play: https://go.dev/play/p/AXw0R3ZTE6a +func Unique[T comparable](slice []T) []T { + if len(slice) == 0 { + return slice + } - if index < 0 || index >= v.Len() { - return slice, errors.New("InvalidSliceIndex") + seen := make(map[T]struct{}, len(slice)) + result := slice[:0] + + for _, item := range slice { + if _, exists := seen[item]; !exists { + seen[item] = struct{}{} + result = append(result, item) + } } - if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) { - return slice, errors.New("InvalidValueType") + return result +} + +// UniqueBy removes duplicate elements from the input slice based on the values returned by the iteratee function. +// 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 { + if len(slice) == 0 { + return slice } - v.Index(index).Set(reflect.ValueOf(value)) + seen := make(map[U]struct{}, len(slice)) + result := slice[:0] + + for _, item := range slice { + key := iteratee(item) + if _, exists := seen[key]; !exists { + seen[key] = struct{}{} + result = append(result, item) + } + } - return v.Interface(), nil + return result } -// Unique remove duplicate elements in slice. -func Unique(slice interface{}) interface{} { - sv := sliceValue(slice) - if sv.Len() == 0 { +// UniqueByComparator removes duplicate elements from the input slice using the provided comparator function. +// 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 { + if len(slice) == 0 { return slice } - var temp []interface{} - - for i := 0; i < sv.Len(); i++ { - v := sv.Index(i).Interface() - skip := true - for j := range temp { - if v == temp[j] { - skip = false + result := make([]T, 0, len(slice)) + for _, item := range slice { + isDuplicate := false + for _, existing := range result { + if comparator(item, existing) { + isDuplicate = true break } } - if skip { - temp = append(temp, v) + if !isDuplicate { + result = append(result, item) + } + } + + return result +} + +// UniqueByField remove duplicate elements in struct slice by struct field. +// Play: https://go.dev/play/p/6cifcZSPIGu +func UniqueByField[T any](slice []T, field string) ([]T, error) { + seen := map[any]struct{}{} + + var result []T + for _, item := range slice { + val, err := getField(item, field) + if err != nil { + return nil, fmt.Errorf("get field %s failed: %v", field, err) + } + if _, ok := seen[val]; !ok { + seen[val] = struct{}{} + result = append(result, item) } } - res := reflect.MakeSlice(sv.Type(), len(temp), len(temp)) - for i := 0; i < len(temp); i++ { - res.Index(i).Set(reflect.ValueOf(temp[i])) + return result, nil +} + +func getField[T any](item T, field string) (interface{}, error) { + v := reflect.ValueOf(item) + t := reflect.TypeOf(item) + + if t.Kind() == reflect.Ptr { + t = t.Elem() + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", item) } - return res.Interface() - // if use map filter, the result slice element order is random, not same as origin slice - //mp := make(map[interface{}]bool) - //for i := 0; i < sv.Len(); i++ { - // v := sv.Index(i).Interface() - // mp[v] = true - //} - // - //var res []interface{} - //for k := range mp { - // res = append(res, mp[k]) - //} - //return res + f := v.FieldByName(field) + if !f.IsValid() { + return nil, fmt.Errorf("field name %s not found", field) + } + return v.FieldByName(field).Interface(), nil } -// Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons. -func Union(slices ...interface{}) interface{} { - if len(slices) == 0 { - return nil +// Union creates a slice of unique elements, in order, from all given slices. +// Play: https://go.dev/play/p/hfXV1iRIZOf +func Union[T comparable](slices ...[]T) []T { + result := []T{} + contain := map[T]struct{}{} + + for _, slice := range slices { + for _, item := range slice { + if _, ok := contain[item]; !ok { + contain[item] = struct{}{} + result = append(result, item) + } + } } - // append all slices, then unique it - var allSlices []interface{} - len := 0 - for i := range slices { - sv := sliceValue(slices[i]) - len += sv.Len() - for j := 0; j < sv.Len(); j++ { - v := sv.Index(j).Interface() - allSlices = append(allSlices, v) + + return result +} + +// UnionBy is like Union, what's more it accepts iteratee which is invoked for each element of each slice. +// Play: https://go.dev/play/p/HGKHfxKQsFi +func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T { + result := []T{} + contain := map[V]struct{}{} + + for _, slice := range slices { + for _, item := range slice { + val := predicate(item) + if _, ok := contain[val]; !ok { + contain[val] = struct{}{} + result = append(result, item) + } } } - sv := sliceValue(slices[0]) - res := reflect.MakeSlice(sv.Type(), len, len) - for i := 0; i < len; i++ { - res.Index(i).Set(reflect.ValueOf(allSlices[i])) + return result +} + +// Deprecated: Please use Concat() function instead. +// Merge all given slices into one slice. +// Play: https://go.dev/play/p/lbjFp784r9N +func Merge[T any](slices ...[]T) []T { + return Concat(slices...) +} + +// Intersection creates a slice of unique elements that included by all slices. +// Play: https://go.dev/play/p/anJXfB5wq_t +func Intersection[T comparable](slices ...[]T) []T { + result := []T{} + elementCount := make(map[T]int) + + for _, slice := range slices { + seen := make(map[T]bool) + + for _, item := range slice { + if !seen[item] { + seen[item] = true + elementCount[item]++ + } + } } - return Unique(res.Interface()) + for _, item := range slices[0] { + if elementCount[item] == len(slices) { + result = append(result, item) + elementCount[item] = 0 + } + } + + return result } -// Intersection creates a slice of unique values that included by all slices. -func Intersection(slices ...interface{}) interface{} { +// SymmetricDifference oppoiste operation of intersection function. +// Play: https://go.dev/play/p/h42nJX5xMln +func SymmetricDifference[T comparable](slices ...[]T) []T { if len(slices) == 0 { - return nil + return []T{} + } + if len(slices) == 1 { + return Unique(slices[0]) } - reduceFunc := func(index int, slice1, slice2 interface{}) interface{} { - set := make([]interface{}, 0) - hash := make(map[interface{}]bool) + result := make([]T, 0) - sv1 := reflect.ValueOf(slice1) - for i := 0; i < sv1.Len(); i++ { - v := sv1.Index(i).Interface() - hash[v] = true - } + intersectSlice := Intersection(slices...) - sv2 := reflect.ValueOf(slice2) - for i := 0; i < sv2.Len(); i++ { - el := sv2.Index(i).Interface() - if _, found := hash[el]; found { - set = append(set, el) + for i := 0; i < len(slices); i++ { + slice := slices[i] + for _, v := range slice { + if !Contain(intersectSlice, v) { + result = append(result, v) } } - res := reflect.MakeSlice(sv1.Type(), len(set), len(set)) - for i := 0; i < len(set); i++ { - res.Index(i).Set(reflect.ValueOf(set[i])) + + } + + return Unique(result) +} + +// Reverse return slice of element order is reversed to the given slice. +// 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 { + slice[i], slice[j] = slice[j], slice[i] + } +} + +// ReverseCopy return a new slice of element order is reversed to the given slice. +// Play: https://go.dev/play/p/c9arEaP7Cg- +func ReverseCopy[T any](slice []T) []T { + result := make([]T, len(slice)) + + for i, j := 0, len(slice)-1; i < len(slice); i, j = i+1, j-1 { + result[i] = slice[j] + } + + return result +} + +// Shuffle return a new slice with elements shuffled. +// Play: https://go.dev/play/p/YHvhnWGU3Ge +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] + }) + + 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 { + for i := 1; i < len(slice); i++ { + if slice[i-1] > slice[i] { + return false } - return res.Interface() } - res := Reduce(slices, reduceFunc, nil) - return Unique(res) + return true } -// ReverseSlice return slice of element order is reversed to the given slice -func ReverseSlice(slice interface{}) { - sv := sliceValue(slice) - swp := reflect.Swapper(sv.Interface()) - for i, j := 0, sv.Len()-1; i < j; i, j = i+1, j-1 { - swp(i, j) +// IsDescending checks if a slice is descending order. +// Play: https://go.dev/play/p/U_LljFXma14 +func IsDescending[T constraints.Ordered](slice []T) bool { + for i := 1; i < len(slice); i++ { + if slice[i-1] < slice[i] { + return false + } } + + return true } -// Shuffle creates an slice of shuffled values -func Shuffle(slice interface{}) interface{} { - sv := sliceValue(slice) - length := sv.Len() +// IsSorted checks if a slice is sorted(ascending or descending). +// Play: https://go.dev/play/p/nCE8wPLwSA- +func IsSorted[T constraints.Ordered](slice []T) bool { + return IsAscending(slice) || IsDescending(slice) +} - res := reflect.MakeSlice(sv.Type(), length, length) - for i, v := range rand.Perm(length) { - res.Index(i).Set(sv.Index(v)) +// IsSortedByKey checks if a slice is sorted by iteratee function. +// Play: https://go.dev/play/p/tUoGB7DOHI4 +func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool { + size := len(slice) + + isAscending := func(data []T) bool { + for i := 0; i < size-1; i++ { + if iteratee(data[i]) > iteratee(data[i+1]) { + return false + } + } + + return true } - return res.Interface() + isDescending := func(data []T) bool { + for i := 0; i < size-1; i++ { + if iteratee(data[i]) < iteratee(data[i+1]) { + return false + } + } + + return true + } + + return isAscending(slice) || isDescending(slice) +} + +// Sort sorts a slice of any ordered type(number or string), use quick sort algrithm. +// default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`. +// Play: https://go.dev/play/p/V9AVjzf_4Fk +func Sort[T constraints.Ordered](slice []T, sortOrder ...string) { + if len(sortOrder) > 0 && sortOrder[0] == "desc" { + quickSort(slice, 0, len(slice)-1, "desc") + } else { + quickSort(slice, 0, len(slice)-1, "asc") + } +} + +// SortBy sorts the slice in ascending order as determined by the less function. +// This sort is not guaranteed to be stable. +// Play: https://go.dev/play/p/DAhLQSZEumm +func SortBy[T any](slice []T, less func(a, b T) bool) { + quickSortBy(slice, 0, len(slice)-1, less) } // SortByField return sorted slice by field -// Slice element should be struct, field type should be int, uint, string, or bool +// slice element should be struct, field type should be int, uint, string, or bool // default sortType is ascending (asc), if descending order, set sortType to desc -func SortByField(slice interface{}, field string, sortType ...string) error { +// This function is deprecated, use Sort and SortBy for replacement. +// Play: https://go.dev/play/p/fU1prOBP9p1 +func SortByField[T any](slice []T, field string, sortType ...string) error { sv := sliceValue(slice) t := sv.Type().Elem() @@ -838,25 +1237,306 @@ func SortByField(slice interface{}, field string, sortType ...string) error { return nil } -// Without creates a slice excluding all given values -func Without(slice interface{}, values ...interface{}) interface{} { - sv := sliceValue(slice) - if sv.Len() == 0 { +// Without creates a slice excluding all given items. +// Play: https://go.dev/play/p/bwhEXEypThg +func Without[T comparable](slice []T, items ...T) []T { + if len(items) == 0 || len(slice) == 0 { return slice } - var indexes []int - for i := 0; i < sv.Len(); i++ { - v := sv.Index(i).Interface() - if !Contain(values, v) { - indexes = append(indexes, i) + result := make([]T, 0, len(slice)) + for _, v := range slice { + if !Contain(items, v) { + result = append(result, v) + } + } + + return result +} + +// IndexOf returns the index at which the first occurrence of an item is found in a slice or return -1 if the item cannot be found. +// Play: https://go.dev/play/p/MRN1f0FpABb +func IndexOf[T comparable](arr []T, val T) int { + limit := 10 + // gets the hash value of the array as the key of the hash table. + key := fmt.Sprintf("%p", arr) + + muForMemoryHash.RLock() + // determines whether the hash table is empty. If so, the hash table is created. + if memoryHashMap[key] == nil { + + muForMemoryHash.RUnlock() + muForMemoryHash.Lock() + + if memoryHashMap[key] == nil { + memoryHashMap[key] = make(map[any]int) + // iterate through the array, adding the value and index of each element to the hash table. + for i := len(arr) - 1; i >= 0; i-- { + memoryHashMap[key][arr[i]] = i + } + } + + muForMemoryHash.Unlock() + } else { + muForMemoryHash.RUnlock() + } + + muForMemoryHash.Lock() + // update the hash table counter. + memoryHashCounter[key]++ + muForMemoryHash.Unlock() + + // use the hash table to find the specified value. If found, the index is returned. + muForMemoryHash.RLock() + index, ok := memoryHashMap[key][val] + muForMemoryHash.RUnlock() + + if ok { + muForMemoryHash.RLock() + // calculate the memory usage of the hash table. + size := len(memoryHashMap) + muForMemoryHash.RUnlock() + + // If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared. + if size > limit { + muForMemoryHash.Lock() + var minKey string + var minVal int + for k, v := range memoryHashCounter { + if k == key { + continue + } + if minVal == 0 || v < minVal { + minKey = k + minVal = v + } + } + delete(memoryHashMap, minKey) + delete(memoryHashCounter, minKey) + muForMemoryHash.Unlock() + } + return index + } + return -1 +} + +// LastIndexOf returns the index at which the last occurrence of the item is found in a slice or return -1 if the then cannot be found. +// Play: https://go.dev/play/p/DokM4cf1IKH +func LastIndexOf[T comparable](slice []T, item T) int { + for i := len(slice) - 1; i >= 0; i-- { + if item == slice[i] { + return i + } + } + + return -1 +} + +// ToSlicePointer returns a pointer to the slices of a variable parameter transformation. +// Play: https://go.dev/play/p/gx4tr6_VXSF +func ToSlicePointer[T any](items ...T) []*T { + result := make([]*T, len(items)) + for i := range items { + result[i] = &items[i] + } + + return result +} + +// ToSlice returns a slices of a variable parameter transformation. +// Play: https://go.dev/play/p/YzbzVq5kscN +func ToSlice[T any](items ...T) []T { + result := make([]T, len(items)) + copy(result, items) + + return result +} + +// AppendIfAbsent only absent append the item. +// Play: https://go.dev/play/p/KcC1QXQ-RkL +func AppendIfAbsent[T comparable](slice []T, item T) []T { + if !Contain(slice, item) { + slice = append(slice, item) + } + return slice +} + +// SetToDefaultIf sets elements to their default value if they match the given predicate. +// It retains the positions of the elements in the slice. +// It returns slice of T and the count of modified slice items +// Play: https://go.dev/play/p/9AXGlPRC0-A +func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) { + var count int + for i := 0; i < len(slice); i++ { + if predicate(slice[i]) { + var zeroValue T + slice[i] = zeroValue + count++ + } + } + return slice, count +} + +// KeyBy converts a slice to a map based on a callback function. +// Play: https://go.dev/play/p/uXod2LWD1Kg +func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T { + result := make(map[U]T, len(slice)) + + for _, v := range slice { + k := iteratee(v) + result[k] = v + } + + return result +} + +// Join the slice item with specify separator. +// Play: https://go.dev/play/p/huKzqwNDD7V +func Join[T any](slice []T, separator string) string { + str := Map(slice, func(_ int, item T) string { + return fmt.Sprint(item) + }) + + return strings.Join(str, separator) +} + +// Partition all slice elements with the evaluation of the given predicate functions. +// Play: https://go.dev/play/p/lkQ3Ri2NQhV +func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T { + l := len(predicates) + + result := make([][]T, l+1) + + for _, item := range slice { + processed := false + + for i, f := range predicates { + if f == nil { + panic("predicate function must not be nill") + } + + if f(item) { + result[i] = append(result[i], item) + processed = true + break + } + } + + if !processed { + result[l] = append(result[l], item) + } + } + + return result +} + +// Breaks a list into two parts at the point where the predicate for the first time is true. +// Play: https://go.dev/play/p/yLYcBTyeQIz +func Break[T any](values []T, predicate func(T) bool) ([]T, []T) { + a := make([]T, 0) + b := make([]T, 0) + if len(values) == 0 { + return a, b + } + matched := false + for _, value := range values { + + if !matched && predicate(value) { + matched = true + } + + if matched { + b = append(b, value) + } else { + a = append(a, value) } } + return a, b +} - res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes)) - for i := range indexes { - res.Index(i).Set(sv.Index(indexes[i])) +// Random get a random item of slice, return idx=-1 when slice is empty +// Play: https://go.dev/play/p/UzpGQptWppw +func Random[T any](slice []T) (val T, idx int) { + if len(slice) == 0 { + return val, -1 } - return res.Interface() + idx = random.RandInt(0, len(slice)) + return slice[idx], idx +} + +// RightPadding adds padding to the right end of a 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 + } + for i := 0; i < paddingLength; i++ { + slice = append(slice, paddingValue) + } + return slice +} + +// LeftPadding adds padding to the left begin of a slice. +// Play: https://go.dev/play/p/jlQVoelLl2k +func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T { + if paddingLength == 0 { + return slice + } + + 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 +} + +// Frequency counts the frequency of each element in the slice. +// Play: https://go.dev/play/p/CW3UVNdUZOq +func Frequency[T comparable](slice []T) map[T]int { + result := make(map[T]int) + + for _, v := range slice { + result[v]++ + } + + return result +} + +// JoinFunc joins the slice elements into a single string with the given separator. +// 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 { + if i > 0 { + buf.WriteString(sep) + } + buf.WriteString(fmt.Sprint(transform(v))) + } + return buf.String() +} + +// ConcatBy concats the elements of a slice into a single value using the provided separator and connector function. +// Play: https://go.dev/play/p/6QcUpcY4UMW +func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T { + var result T + + if len(slice) == 0 { + return result + } + + for i, v := range slice { + result = connector(result, v) + if i < len(slice)-1 { + result = connector(result, sep) + } + } + + return result } diff --git a/slice/slice_concurrent.go b/slice/slice_concurrent.go new file mode 100644 index 00000000..2cd0d9b8 --- /dev/null +++ b/slice/slice_concurrent.go @@ -0,0 +1,237 @@ +// Copyright 2024 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +package slice + +import ( + "runtime" + "sync" +) + +// ForEachConcurrent applies the iteratee function to each item in the slice concurrently. +// Play: https://go.dev/play/p/kT4XW7DKVoV +func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) { + sliceLen := len(slice) + if sliceLen == 0 { + return + } + + if numThreads <= 0 { + numThreads = 1 + } + + var wg sync.WaitGroup + + chunkSize := (sliceLen + numThreads - 1) / numThreads + + for i := 0; i < numThreads; i++ { + start := i * chunkSize + end := start + chunkSize + + if start >= sliceLen { + break + } + + if end > sliceLen { + end = sliceLen + } + + wg.Add(1) + go func(start, end int) { + defer wg.Done() + + for j := start; j < end; j++ { + iteratee(j, slice[j]) + } + }(start, end) + } + + wg.Wait() +} + +// MapConcurrent applies the iteratee function to each item in the slice concurrently. +// Play: https://go.dev/play/p/H1ehfPkPen0 +func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U { + result := make([]U, len(slice)) + var wg sync.WaitGroup + + workerChan := make(chan struct{}, numThreads) + + for index, item := range slice { + wg.Add(1) + + workerChan <- struct{}{} + + go func(i int, v T) { + defer wg.Done() + + result[i] = iteratee(i, v) + + <-workerChan + }(index, item) + } + + wg.Wait() + + return result +} + +// ReduceConcurrent reduces the slice to a single value by applying the reducer function to each item in the slice concurrently. +// Play: https://go.dev/play/p/Tjwe6OtaG07 +func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T { + if numThreads <= 0 { + numThreads = 1 + } + + var wg sync.WaitGroup + var mu sync.Mutex + + sliceLen := len(slice) + chunkSize := (sliceLen + numThreads - 1) / numThreads + results := make([]T, numThreads) + + for i := 0; i < numThreads; i++ { + start := i * chunkSize + end := start + chunkSize + if end > sliceLen { + end = sliceLen + } + + wg.Add(1) + go func(i, start, end int) { + defer wg.Done() + tempResult := initial + for j := start; j < end; j++ { + tempResult = reducer(j, slice[j], tempResult) + } + mu.Lock() + results[i] = tempResult + mu.Unlock() + }(i, start, end) + } + + wg.Wait() + + result := initial + for i, r := range results { + result = reducer(i, result, r) + } + + return result +} + +// FilterConcurrent applies the provided filter function `predicate` to each element of the input slice concurrently. +// Play: https://go.dev/play/p/t_pkwerIRVx +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) + + for index, item := range slice { + wg.Add(1) + + workerChan <- struct{}{} + + go func(i int, v T) { + defer wg.Done() + + if predicate(i, v) { + mu.Lock() + result = append(result, v) + mu.Unlock() + } + + <-workerChan + }(index, item) + } + + wg.Wait() + + return result +} + +// UniqueByConcurrent removes duplicate elements from the slice by parallel +// The comparator function is used to compare the elements +// The numThreads parameter specifies the number of threads to use +// If numThreads is less than or equal to 0, it will be set to 1 +// The comparator function should return true if the two elements are equal +// Play: https://go.dev/play/p/wXZ7LcYRMGL +func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T { + if numThreads <= 0 { + numThreads = 1 + } else if numThreads > len(slice) { + numThreads = len(slice) + } + + maxThreads := runtime.NumCPU() + if numThreads > maxThreads { + numThreads = maxThreads + } + + removeDuplicate := func(items []T, comparator func(item T, other T) bool) []T { + var result []T + for _, item := range items { + seen := false + for _, r := range result { + if comparator(item, r) { + seen = true + break + } + } + if !seen { + result = append(result, item) + } + } + return result + } + + chunkSize := (len(slice) + numThreads - 1) / numThreads + + chunks := make([][]T, 0, numThreads) + for i := 0; i < len(slice); i += chunkSize { + end := i + chunkSize + if end > len(slice) { + end = len(slice) + } + chunks = append(chunks, slice[i:end]) + } + + resultCh := make(chan resultChunk[T], numThreads) + var wg sync.WaitGroup + + for i, chunk := range chunks { + wg.Add(1) + go func(index int, chunk []T) { + defer wg.Done() + resultCh <- resultChunk[T]{index, removeDuplicate(chunk, comparator)} + }(i, chunk) + } + + go func() { + wg.Wait() + close(resultCh) + }() + + results := make([][]T, len(chunks)) + for r := range resultCh { + results[r.index] = r.data + } + + result := []T{} + seen := make(map[T]bool) + + for _, chunk := range results { + for _, item := range chunk { + if !seen[item] { + seen[item] = true + result = append(result, item) + } + } + + } + + return result +} diff --git a/slice/slice_example_test.go b/slice/slice_example_test.go new file mode 100644 index 00000000..339abdfa --- /dev/null +++ b/slice/slice_example_test.go @@ -0,0 +1,1349 @@ +package slice + +import ( + "fmt" + "math" + "reflect" + "strconv" + "strings" +) + +func ExampleContain() { + result1 := Contain([]string{"a", "b", "c"}, "a") + result2 := Contain([]int{1, 2, 3}, 4) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleContainBy() { + type foo struct { + A string + B int + } + + array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}} + result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 }) + result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 }) + + array2 := []string{"a", "b", "c"} + result3 := ContainBy(array2, func(t string) bool { return t == "a" }) + result4 := ContainBy(array2, func(t string) bool { return t == "d" }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // false + // true + // false +} + +func ExampleContainSubSlice() { + result1 := ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"}) + result2 := ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleChunk() { + arr := []string{"a", "b", "c", "d", "e"} + + result1 := Chunk(arr, 1) + result2 := Chunk(arr, 2) + result3 := Chunk(arr, 3) + result4 := Chunk(arr, 4) + result5 := Chunk(arr, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [[a] [b] [c] [d] [e]] + // [[a b] [c d] [e]] + // [[a b c] [d e]] + // [[a b c d] [e]] + // [[a b c d e]] +} + +func ExampleCompact() { + result1 := Compact([]int{0}) + result2 := Compact([]int{0, 1, 2, 3}) + result3 := Compact([]string{"", "a", "b", "0"}) + result4 := Compact([]bool{false, true, true}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [] + // [1 2 3] + // [a b 0] + // [true true] +} + +func ExampleConcat() { + result1 := Concat([]int{1, 2}, []int{3, 4}) + result2 := Concat([]string{"a", "b"}, []string{"c"}, []string{"d"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [1 2 3 4] + // [a b c d] +} + +func ExampleDifference() { + slice1 := []int{1, 2, 3, 4, 5} + slice2 := []int{4, 5, 6} + + result := Difference(slice1, slice2) + + fmt.Println(result) + + // Output: + // [1 2 3] +} + +func ExampleDifferenceBy() { + slice1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6 + slice2 := []int{3, 4, 5} //after add one: 4 5 6 + + addOne := func(i int, v int) int { + return v + 1 + } + + result := DifferenceBy(slice1, slice2, addOne) + + fmt.Println(result) + + // Output: + // [1 2] +} + +func ExampleDifferenceWith() { + slice1 := []int{1, 2, 3, 4, 5} + slice2 := []int{4, 5, 6, 7, 8} + + isDouble := func(v1, v2 int) bool { + return v2 == 2*v1 + } + + result := DifferenceWith(slice1, slice2, isDouble) + + fmt.Println(result) + + // Output: + // [1 5] +} + +func ExampleEqual() { + slice1 := []int{1, 2, 3} + slice2 := []int{1, 2, 3} + slice3 := []int{1, 3, 2} + + result1 := Equal(slice1, slice2) + result2 := Equal(slice1, slice3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleEqualWith() { + slice1 := []int{1, 2, 3} + slice2 := []int{2, 4, 6} + + isDouble := func(a, b int) bool { + return b == a*2 + } + + result := EqualWith(slice1, slice2, isDouble) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleEvery() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := Every(nums, isEven) + + fmt.Println(result) + + // Output: + // false +} + +func ExampleNone() { + nums := []int{1, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := None(nums, isEven) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleSome() { + nums := []int{1, 2, 3, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := Some(nums, isEven) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleFilter() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := Filter(nums, isEven) + + fmt.Println(result) + + // Output: + // [2 4] +} + +func ExampleFilterConcurrent() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := FilterConcurrent(nums, isEven, 2) + + fmt.Println(result) + + // Output: + // [2 4] +} + +func ExampleCount() { + nums := []int{1, 2, 3, 3, 4} + + result1 := Count(nums, 1) + result2 := Count(nums, 3) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // 2 +} + +func ExampleCountBy() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result := CountBy(nums, isEven) + + fmt.Println(result) + + // Output: + // 2 +} + +func ExampleGroupBy() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + even, odd := GroupBy(nums, isEven) + + fmt.Println(even) + fmt.Println(odd) + + // Output: + // [2 4] + // [1 3 5] +} + +func ExampleGroupWith() { + nums := []float64{6.1, 4.2, 6.3} + + floor := func(num float64) float64 { + return math.Floor(num) + } + + result := GroupWith(nums, floor) //map[float64][]float64 + + fmt.Println(result) + + // Output: + // map[4:[4.2] 6:[6.1 6.3]] +} + +func ExampleFind() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := Find(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 2 + // true +} + +func ExampleFindLast() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := FindLast(nums, isEven) + + fmt.Println(*result) + fmt.Println(ok) + + // Output: + // 4 + // true +} + +func ExampleFindBy() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := FindBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 2 + // true +} + +func ExampleFindLastBy() { + nums := []int{1, 2, 3, 4, 5} + + isEven := func(i, num int) bool { + return num%2 == 0 + } + + result, ok := FindLastBy(nums, isEven) + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 4 + // true +} + +func ExampleFlatten() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := Flatten(arrs) + + fmt.Println(result) + + // Output: + // [[a b] [c d]] +} + +func ExampleFlattenDeep() { + arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + + result := FlattenDeep(arrs) + + fmt.Println(result) + + // Output: + // [a b c d] +} + +func ExampleForEach() { + nums := []int{1, 2, 3} + + var result []int + addOne := func(_ int, v int) { + result = append(result, v+1) + } + + ForEach(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} + +func ExampleForEachConcurrent() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8} + + result := make([]int, len(nums)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + ForEachConcurrent(nums, addOne, 4) + + fmt.Println(result) + + // Output: + // [2 3 4 5 6 7 8 9] +} + +func ExampleForEachWithBreak() { + numbers := []int{1, 2, 3, 4, 5} + + var sum int + + ForEachWithBreak(numbers, func(_, n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + fmt.Println(sum) + + // Output: + // 6 +} + +func ExampleMap() { + nums := []int{1, 2, 3} + + addOne := func(_ int, v int) int { + return v + 1 + } + + result := Map(nums, addOne) + + fmt.Println(result) + + // Output: + // [2 3 4] +} + +func ExampleFilterMap() { + nums := []int{1, 2, 3, 4, 5} + + getEvenNumStr := func(i, num int) (string, bool) { + if num%2 == 0 { + return strconv.FormatInt(int64(num), 10), true + } + return "", false + } + + result := FilterMap(nums, getEvenNumStr) + + fmt.Printf("%#v", result) + + // Output: + // []string{"2", "4"} +} + +func ExampleFlatMap() { + nums := []int{1, 2, 3, 4} + + result := FlatMap(nums, func(i int, num int) []string { + s := "hi-" + strconv.FormatInt(int64(num), 10) + return []string{s} + }) + + fmt.Printf("%#v", result) + + // Output: + // []string{"hi-1", "hi-2", "hi-3", "hi-4"} +} + +func ExampleReduce() { + nums := []int{1, 2, 3} + + sum := func(_ int, v1, v2 int) int { + return v1 + v2 + } + + result := Reduce(nums, sum, 0) + + fmt.Println(result) + + // Output: + // 6 +} + +func ExampleReduceConcurrent() { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 1) + + fmt.Println(result) + + // Output: + // 55 +} + +func ExampleReduceBy() { + result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int { + return agg + item + }) + + result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 10 + // 1234 +} + +func ExampleReduceRight() { + result := ReduceRight([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + + fmt.Println(result) + + // Output: + // 4321 +} + +func ExampleReplace() { + strs := []string{"a", "b", "c", "a"} + + result1 := Replace(strs, "a", "x", 0) + result2 := Replace(strs, "a", "x", 1) + result3 := Replace(strs, "a", "x", 2) + result4 := Replace(strs, "a", "x", 3) + result5 := Replace(strs, "a", "x", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c a] + // [x b c a] + // [x b c x] + // [x b c x] + // [x b c x] +} + +func ExampleReplaceAll() { + result := ReplaceAll([]string{"a", "b", "c", "a"}, "a", "x") + + fmt.Println(result) + + // Output: + // [x b c x] +} + +func ExampleRepeat() { + result := Repeat("a", 3) + + fmt.Println(result) + + // Output: + // [a a a] +} + +func ExampleInterfaceSlice() { + strs := []string{"a", "b", "c"} + + result := InterfaceSlice(strs) //[]interface{}{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} + +func ExampleStringSlice() { + strs := []interface{}{"a", "b", "c"} + + result := StringSlice(strs) //[]string{"a", "b", "c"} + fmt.Println(result) + + // Output: + // [a b c] +} + +func ExampleIntSlice() { + nums := []interface{}{1, 2, 3} + + result := IntSlice(nums) //[]int{1, 2, 3} + fmt.Println(result) + + // Output: + // [1 2 3] +} + +func ExampleDeleteAt() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := DeleteAt(chars, 0) + result2 := DeleteAt(chars, 4) + result3 := DeleteAt(chars, 5) + result4 := DeleteAt(chars, 6) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [b c d e] + // [a b c d] + // [a b c d] + // [a b c d] +} + +func ExampleDeleteRange() { + chars := []string{"a", "b", "c", "d", "e"} + + result1 := DeleteRange(chars, 0, 0) + result2 := DeleteRange(chars, 0, 1) + result3 := DeleteRange(chars, 0, 3) + result4 := DeleteRange(chars, 0, 4) + result5 := DeleteRange(chars, 0, 5) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c d e] + // [b c d e] + // [d e] + // [e] + // [] +} + +func ExampleDrop() { + result1 := Drop([]string{"a", "b", "c"}, 0) + result2 := Drop([]string{"a", "b", "c"}, 1) + result3 := Drop([]string{"a", "b", "c"}, -1) + result4 := Drop([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [b c] + // [a b c] + // [] +} + +func ExampleDropRight() { + result1 := DropRight([]string{"a", "b", "c"}, 0) + result2 := DropRight([]string{"a", "b", "c"}, 1) + result3 := DropRight([]string{"a", "b", "c"}, -1) + result4 := DropRight([]string{"a", "b", "c"}, 4) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // [a b c] + // [a b] + // [a b c] + // [] +} + +func ExampleDropWhile() { + numbers := []int{1, 2, 3, 4, 5} + + result1 := DropWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := DropWhile(numbers, func(n int) bool { + return true + }) + result3 := DropWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [2 3 4 5] + // [] + // [1 2 3 4 5] +} + +func ExampleDropRightWhile() { + numbers := []int{1, 2, 3, 4, 5} + + result1 := DropRightWhile(numbers, func(n int) bool { + return n != 2 + }) + result2 := DropRightWhile(numbers, func(n int) bool { + return true + }) + result3 := DropRightWhile(numbers, func(n int) bool { + return n == 0 + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [1 2] + // [] + // [1 2 3 4 5] +} + +func ExampleInsertAt() { + result1 := InsertAt([]string{"a", "b", "c"}, 0, "1") + result2 := InsertAt([]string{"a", "b", "c"}, 1, "1") + result3 := InsertAt([]string{"a", "b", "c"}, 2, "1") + result4 := InsertAt([]string{"a", "b", "c"}, 3, "1") + result5 := InsertAt([]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [1 a b c] + // [a 1 b c] + // [a b 1 c] + // [a b c 1] + // [1 2 3 a b c] +} + +func ExampleUpdateAt() { + result1 := UpdateAt([]string{"a", "b", "c"}, -1, "1") + result2 := UpdateAt([]string{"a", "b", "c"}, 0, "1") + result3 := UpdateAt([]string{"a", "b", "c"}, 1, "1") + result4 := UpdateAt([]string{"a", "b", "c"}, 2, "1") + result5 := UpdateAt([]string{"a", "b", "c"}, 3, "1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a b c] + // [1 b c] + // [a 1 c] + // [a b 1] + // [a b c] +} + +func ExampleUnique() { + result := Unique([]string{"a", "a", "b"}) + fmt.Println(result) + + // Output: + // [a b] +} + +func ExampleUniqueBy() { + nums := []int{1, 2, 3, 4, 5, 6} + result := UniqueBy(nums, func(val int) int { + return val % 3 + }) + + fmt.Println(result) + + // Output: + // [1 2 3] +} + +func ExampleUniqueByComparator() { + uniqueNums := UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool { + return item == other + }) + + caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool { + return strings.ToLower(item) == strings.ToLower(other) + }) + + fmt.Println(uniqueNums) + fmt.Println(caseInsensitiveStrings) + + // Output: + // [1 2 3 4 5 6] + // [apple banana cherry date] +} + +func ExampleUniqueByField() { + type User struct { + ID int `json:"id"` + Name string `json:"name"` + } + + users := []User{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "b"}, + {ID: 1, Name: "c"}, + } + + result, err := UniqueByField(users, "ID") + if err != nil { + } + + fmt.Println(result) + + // Output: + // [{1 a} {2 b}] +} + +func ExampleUnion() { + nums1 := []int{1, 3, 4, 6} + nums2 := []int{1, 2, 5, 6} + + result := Union(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 3 4 6 2 5] +} + +func ExampleUnionBy() { + nums := []int{1, 2, 3, 4} + + divideTwo := func(n int) int { + return n / 2 + } + result := UnionBy(divideTwo, nums) + + fmt.Println(result) + + // Output: + // [1 2 4] +} + +func ExampleMerge() { + nums1 := []int{1, 2, 3} + nums2 := []int{3, 4} + + result := Merge(nums1, nums2) + + fmt.Println(result) + + // Output: + // [1 2 3 3 4] +} + +func ExampleIntersection() { + nums1 := []int{1, 2, 3} + nums2 := []int{2, 3, 4} + + result := Intersection(nums1, nums2) + + fmt.Println(result) + + // Output: + // [2 3] +} + +func ExampleSymmetricDifference() { + nums1 := []int{1, 2, 3} + nums2 := []int{1, 2, 4} + + result := SymmetricDifference(nums1, nums2) + + fmt.Println(result) + + // Output: + // [3 4] +} + +func ExampleReverse() { + strs := []string{"a", "b", "c", "d"} + + Reverse(strs) + + fmt.Println(strs) + + // Output: + // [d c b a] +} + +func ExampleReverseCopy() { + strs := []string{"a", "b", "c", "d"} + + reversedStrs := ReverseCopy(strs) + + fmt.Println(reversedStrs) + fmt.Println(strs) + + // Output: + // [d c b a] + // [a b c d] +} + +func ExampleShuffle() { + strs := []string{"a", "b", "c", "d"} + Shuffle(strs) + + fmt.Println(len(strs)) + + // Output: + // 4 +} + +func ExampleShuffleCopy() { + strs := []string{"a", "b", "c", "d"} + shuffledStrs := ShuffleCopy(strs) + + fmt.Println(len(shuffledStrs)) + fmt.Println(strs) + + // Output: + // 4 + // [a b c d] +} + +func ExampleIsAscending() { + + result1 := IsAscending([]int{1, 2, 3, 4, 5}) + result2 := IsAscending([]int{5, 4, 3, 2, 1}) + result3 := IsAscending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsDescending() { + + result1 := IsDescending([]int{5, 4, 3, 2, 1}) + result2 := IsDescending([]int{1, 2, 3, 4, 5}) + result3 := IsDescending([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsSorted() { + + result1 := IsSorted([]int{1, 2, 3, 4, 5}) + result2 := IsSorted([]int{5, 4, 3, 2, 1}) + result3 := IsSorted([]int{2, 1, 3, 4, 5}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleIsSortedByKey() { + result1 := IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int { + return len(s) + }) + result2 := IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int { + return len(s) + }) + result3 := IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int { + return len(s) + }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleSort() { + nums := []int{1, 4, 3, 2, 5} + + Sort(nums) + + fmt.Println(nums) + + // Output: + // [1 2 3 4 5] +} + +func ExampleSortBy() { + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + SortBy(users, func(a, b User) bool { + return a.Age < b.Age + }) + + fmt.Println(users) + + // Output: + // [{b 15} {a 21} {c 100}] +} + +func ExampleSortByField() { + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + err := SortByField(users, "Age", "desc") + if err != nil { + return + } + + fmt.Println(users) + + // Output: + // [{c 100} {a 21} {b 15}] +} + +func ExampleWithout() { + result := Without([]int{1, 2, 3, 4}, 1, 2) + + fmt.Println(result) + + // Output: + // [3 4] +} + +func ExampleIndexOf() { + strs := []string{"a", "a", "b", "c"} + + result1 := IndexOf(strs, "a") + result2 := IndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 0 + // -1 +} + +func ExampleLastIndexOf() { + strs := []string{"a", "a", "b", "c"} + + result1 := LastIndexOf(strs, "a") + result2 := LastIndexOf(strs, "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1 + // -1 +} + +func ExampleToSlice() { + result := ToSlice("a", "b", "c") + + fmt.Println(result) + + // Output: + // [a b c] +} + +func ExampleToSlicePointer() { + str1 := "a" + str2 := "b" + + result := ToSlicePointer(str1, str2) + + expect := []*string{&str1, &str2} + + isEqual := reflect.DeepEqual(result, expect) + + fmt.Println(isEqual) + + // Output: + // true +} + +func ExampleAppendIfAbsent() { + result1 := AppendIfAbsent([]string{"a", "b"}, "b") + result2 := AppendIfAbsent([]string{"a", "b"}, "c") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b] + // [a b c] +} + +func ExampleKeyBy() { + result := KeyBy([]string{"a", "ab", "abc"}, func(str string) int { + return len(str) + }) + + fmt.Println(result) + + // Output: + // map[1:a 2:ab 3:abc] +} + +func ExampleJoin() { + nums := []int{1, 2, 3, 4, 5} + + result1 := Join(nums, ",") + result2 := Join(nums, "-") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // 1,2,3,4,5 + // 1-2-3-4-5 +} + +func ExamplePartition() { + nums := []int{1, 2, 3, 4, 5} + + result1 := Partition(nums) + result2 := Partition(nums, func(n int) bool { return n%2 == 0 }) + result3 := Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 }) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // [[1 2 3 4 5]] + // [[2 4] [1 3 5]] + // [[1 2] [3 4] [5]] +} + +func ExampleRandom() { + nums := []int{1, 2, 3, 4, 5} + + val, idx := Random(nums) + if idx >= 0 && idx < len(nums) && Contain(nums, val) { + fmt.Println("okk") + } + + // Output: + // okk +} + +func ExampleSetToDefaultIf() { + strs := []string{"a", "b", "a", "c", "d", "a"} + modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s }) + fmt.Println(modifiedStrs) + fmt.Println(count) + + // Output: + // [ b c d ] + // 3 +} + +func ExampleBreak() { + nums := []int{1, 2, 3, 4, 5} + even := func(n int) bool { return n%2 == 0 } + + resultEven, resultAfterFirstEven := Break(nums, even) + fmt.Println(resultEven) + fmt.Println(resultAfterFirstEven) + + // Output: + // [1] + // [2 3 4 5] +} + +func ExampleRightPadding() { + nums := []int{1, 2, 3, 4, 5} + padded := RightPadding(nums, 0, 3) + fmt.Println(padded) + + // Output: + // [1 2 3 4 5 0 0 0] +} + +func ExampleLeftPadding() { + nums := []int{1, 2, 3, 4, 5} + padded := LeftPadding(nums, 0, 3) + fmt.Println(padded) + + // Output: + // [0 0 0 1 2 3 4 5] +} + +func ExampleUniqueByConcurrent() { + nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7} + comparator := func(item int, other int) bool { return item == other } + + result := UniqueByConcurrent(nums, comparator, 4) + + fmt.Println(result) + + // Output: + // [1 2 3 4 5 6 7] +} + +func ExampleMapConcurrent() { + nums := []int{1, 2, 3, 4, 5, 6} + result := MapConcurrent(nums, func(_, n int) int { return n * n }, 4) + + fmt.Println(result) + + // Output: + // [1 4 9 16 25 36] +} + +func ExampleFrequency() { + strs := []string{"a", "b", "b", "c", "c", "c"} + result := Frequency(strs) + + fmt.Println(result) + + // Output: + // map[a:1 b:2 c:3] +} + +func ExampleJoinFunc() { + result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string { + return strings.ToUpper(s) + }) + + fmt.Println(result) + + // Output: + // A, B, C +} + +func ExampleConcatBy() { + type Person struct { + Name string + Age int + } + + people := []Person{ + {Name: "Alice", Age: 30}, + {Name: "Bob", Age: 25}, + {Name: "Charlie", Age: 35}, + } + + sep := Person{Name: " | ", Age: 0} + + personConnector := func(a, b Person) Person { + return Person{Name: a.Name + b.Name, Age: a.Age + b.Age} + } + + result := ConcatBy(people, sep, personConnector) + + fmt.Println(result.Name) + fmt.Println(result.Age) + + // Output: + // Alice | Bob | Charlie + // 90 +} + +func ExampleEqualUnordered() { + result1 := EqualUnordered([]int{1, 2, 3}, []int{3, 2, 1}) + result2 := EqualUnordered([]int{1, 2, 3}, []int{4, 5, 6}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} diff --git a/slice/slice_internal.go b/slice/slice_internal.go index b3113f1e..f6c275ec 100644 --- a/slice/slice_internal.go +++ b/slice/slice_internal.go @@ -3,10 +3,19 @@ package slice import ( "fmt" "reflect" + + "golang.org/x/exp/constraints" ) +// resultChunk is used to store the intermediate results of UniqueByConcurrent. +// It is defined separately to be compatible with versions of go up to 1.20. +type resultChunk[T comparable] struct { + index int + data []T +} + // sliceValue return the reflect value of a slice -func sliceValue(slice interface{}) reflect.Value { +func sliceValue(slice any) reflect.Value { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice { panic(fmt.Sprintf("Invalid slice type, value of type %T", slice)) @@ -14,52 +23,76 @@ func sliceValue(slice interface{}) reflect.Value { return v } -// functionValue return the reflect value of a function -func functionValue(function interface{}) reflect.Value { - v := reflect.ValueOf(function) - if v.Kind() != reflect.Func { - panic(fmt.Sprintf("Invalid function type, value of type %T", function)) +// sliceElemType get slice element type +func sliceElemType(reflectType reflect.Type) reflect.Type { + for { + if reflectType.Kind() != reflect.Slice { + return reflectType + } + + reflectType = reflectType.Elem() } - return v } -// checkSliceCallbackFuncSignature Check func sign : s :[]type1{} -> func(i int, data type1) type2 -// see https://coolshell.cn/articles/21164.html#%E6%B3%9B%E5%9E%8BMap-Reduce -func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bool { - //Check it is a function - if fn.Kind() != reflect.Func { - return false - } - // NumIn() - returns a function type's input parameter count. - // NumOut() - returns a function type's output parameter count. - if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) { - return false +func quickSort[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) { + if lowIndex < highIndex { + p := partitionOrderedSlice(slice, lowIndex, highIndex, order) + quickSort(slice, lowIndex, p-1, order) + quickSort(slice, p+1, highIndex, order) } - // In() - returns the type of a function type's i'th input parameter. - // first input param type should be int - if fn.Type().In(0) != reflect.TypeOf(1) { - return false - } - for i := 0; i < len(types)-1; i++ { - if fn.Type().In(i) != types[i] { - return false +} + +// partitionOrderedSlice split ordered slice into two parts for quick sort +func partitionOrderedSlice[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) int { + p := slice[highIndex] + i := lowIndex + + for j := lowIndex; j < highIndex; j++ { + if order == "desc" { + if slice[j] > p { + swap(slice, i, j) + i++ + } + } else { + if slice[j] < p { + swap(slice, i, j) + i++ + } } } - // Out() - returns the type of a function type's i'th output parameter. - outType := types[len(types)-1] - if outType != nil && fn.Type().Out(0) != outType { - return false + + swap(slice, i, highIndex) + + return i +} + +func quickSortBy[T any](slice []T, lowIndex, highIndex int, less func(a, b T) bool) { + if lowIndex < highIndex { + p := partitionAnySlice(slice, lowIndex, highIndex, less) + quickSortBy(slice, lowIndex, p-1, less) + quickSortBy(slice, p+1, highIndex, less) } - return true } -// sliceElemType get slice element type -func sliceElemType(reflectType reflect.Type) reflect.Type { - for { - if reflectType.Kind() != reflect.Slice { - return reflectType - } +// partitionAnySlice split any slice into two parts for quick sort +func partitionAnySlice[T any](slice []T, lowIndex, highIndex int, less func(a, b T) bool) int { + p := slice[highIndex] + i := lowIndex - reflectType = reflectType.Elem() + for j := lowIndex; j < highIndex; j++ { + + if less(slice[j], p) { + swap(slice, i, j) + i++ + } } + + swap(slice, i, highIndex) + + return i +} + +// swap two slice value at index i and j +func swap[T any](slice []T, i, j int) { + slice[i], slice[j] = slice[j], slice[i] } diff --git a/slice/slice_test.go b/slice/slice_test.go index b074bda6..fbbed930 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -1,58 +1,122 @@ package slice import ( + "fmt" + "math" "reflect" + "sort" + "strconv" + "strings" + "sync" "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestContain(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestContain") - assert.Equal(true, Contain([]string{"a", "b", "c"}, "a")) - assert.Equal(false, Contain([]string{"a", "b", "c"}, "d")) - assert.Equal(true, Contain([]string{""}, "")) - assert.Equal(false, Contain([]string{}, "")) + tests := []struct { + slice []string + give string + want bool + }{ + {[]string{"a", "b", "c"}, "a", true}, + {[]string{"a", "b", "c"}, "d", false}, + {[]string{""}, "", true}, + {[]string{}, "", false}, + } + + for _, tt := range tests { + assert.Equal(tt.want, Contain(tt.slice, tt.give)) + } +} + +func TestContainBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestContainBy") + + type foo struct { + a string + b int + } - var m = map[string]int{"a": 1} - assert.Equal(true, Contain(m, "a")) - assert.Equal(false, Contain(m, "b")) + tests := []struct { + slice []foo + predicateFn func(f foo) bool + want bool + }{ + { + []foo{{a: "1", b: 1}, {a: "2", b: 2}}, + func(f foo) bool { return f.a == "1" && f.b == 1 }, + true, + }, + { + []foo{{a: "1", b: 1}, {a: "2", b: 2}}, + func(f foo) bool { return f.a == "2" && f.b == 1 }, + false, + }, + } - assert.Equal(true, Contain("abc", "a")) - assert.Equal(false, Contain("abc", "d")) + for _, tt := range tests { + assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn)) + } } func TestContainSubSlice(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestContainSubSlice") - assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"})) - assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"})) - assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1})) - assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []string{"a"})) + tests := []struct { + slice []string + subSlice []string + want bool + }{ + {[]string{"a", "b", "c"}, []string{"a", "b"}, true}, + {[]string{"a", "b", "c"}, []string{"a", "d"}, false}, + {[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true}, + {[]string{"a", "b", "c"}, []string{"a", "b", "c", "d"}, false}, + {[]string{"a", "b", ""}, []string{"a", ""}, true}, + {[]string{""}, []string{""}, true}, + } + + for _, tt := range tests { + assert.Equal(tt.want, ContainSubSlice(tt.slice, tt.subSlice)) + } } func TestChunk(t *testing.T) { - assert := internal.NewAssert(t, "TestChunk") + t.Parallel() - arr := []string{"a", "b", "c", "d", "e"} - r1 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}} - assert.Equal(r1, Chunk(InterfaceSlice(arr), 1)) - - r2 := [][]interface{}{{"a", "b"}, {"c", "d"}, {"e"}} - assert.Equal(r2, Chunk(InterfaceSlice(arr), 2)) - - r3 := [][]interface{}{{"a", "b", "c"}, {"d", "e"}} - assert.Equal(r3, Chunk(InterfaceSlice(arr), 3)) + assert := internal.NewAssert(t, "TestChunk") - r4 := [][]interface{}{{"a", "b", "c", "d"}, {"e"}} - assert.Equal(r4, Chunk(InterfaceSlice(arr), 4)) + tests := []struct { + slice []string + chuanSize int + want [][]string + }{ + {[]string{"a", "b", "c", "d", "e"}, -1, [][]string{}}, + {[]string{"a", "b", "c", "d", "e"}, 0, [][]string{}}, + {[]string{"a", "b", "c", "d", "e"}, 1, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}}, + {[]string{"a", "b", "c", "d", "e"}, 2, [][]string{{"a", "b"}, {"c", "d"}, {"e"}}}, + {[]string{"a", "b", "c", "d", "e"}, 3, [][]string{{"a", "b", "c"}, {"d", "e"}}}, + {[]string{"a", "b", "c", "d", "e"}, 4, [][]string{{"a", "b", "c", "d"}, {"e"}}}, + {[]string{"a", "b", "c", "d", "e"}, 5, [][]string{{"a", "b", "c", "d", "e"}}}, + {[]string{"a", "b", "c", "d", "e"}, 6, [][]string{{"a", "b", "c", "d", "e"}}}, + } - r5 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}} - assert.Equal(r5, Chunk(InterfaceSlice(arr), 5)) + for _, tt := range tests { + assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize)) + } } func TestCompact(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TesCompact") assert.Equal([]int{}, Compact([]int{0})) @@ -64,169 +128,456 @@ func TestCompact(t *testing.T) { } func TestConcat(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "Concat") - assert.Equal([]int{0}, Concat([]int{}, 0)) - assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, 4, 5)) assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4, 5})) assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5})) - assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, 5)) +} + +func BenchmarkConcat(b *testing.B) { + slice1 := []int{1, 2, 3} + slice2 := []int{4, 5, 6} + slice3 := []int{7, 8, 9} + + for i := 0; i < b.N; i++ { + result := Concat(slice1, slice2, slice3) + if len(result) == 0 { + b.Fatal("unexpected empty result") + } + } +} + +func TestEqual(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEqual") + + slice1 := []int{1, 2, 3} + slice2 := []int{1, 2, 3} + slice3 := []int{3, 2, 1} + + assert.Equal(true, Equal(slice1, slice2)) + assert.Equal(false, Equal(slice1, slice3)) + assert.Equal(false, Equal(slice2, slice3)) +} + +// go test -fuzz=Fuzz -fuzztime=10s . +func FuzzEqual(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b []byte) { + Equal(a, b) + }) +} + +func TestEqualWith(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEqualWith") + + slice1 := []int{1, 2, 3} + slice2 := []int{2, 4, 6} + + isDouble := func(a, b int) bool { + return b == a*2 + } + + assert.Equal(true, EqualWith(slice1, slice2, isDouble)) } func TestEvery(t *testing.T) { - nums := []int{1, 2, 3, 5} + t.Parallel() + + assert := internal.NewAssert(t, "TestEvery") + isEven := func(i, num int) bool { return num%2 == 0 } + isOdd := func(i, num int) bool { + return num%2 == 1 + } - assert := internal.NewAssert(t, "TestEvery") - assert.Equal(false, Every(nums, isEven)) + tests := []struct { + slice []int + predicateFn func(i, num int) bool + want bool + }{ + {[]int{1, 3, 5, 7}, isOdd, true}, + {[]int{2, 4, 6, 8}, isEven, true}, + {[]int{1, 2, 3, 4}, isOdd, false}, + {[]int{1, 2, 3, 4}, isEven, false}, + } + + for _, tt := range tests { + assert.Equal(tt.want, Every(tt.slice, tt.predicateFn)) + } } func TestNone(t *testing.T) { - nums := []int{1, 2, 3, 5} - check := func(i, num int) bool { + t.Parallel() + + assert := internal.NewAssert(t, "TestNone") + + isEven := func(i, num int) bool { + return num%2 == 0 + } + isOdd := func(i, num int) bool { return num%2 == 1 } - assert := internal.NewAssert(t, "TestNone") - assert.Equal(false, None(nums, check)) + tests := []struct { + slice []int + predicateFn func(i, num int) bool + want bool + }{ + {[]int{1, 3, 5, 7}, isEven, true}, + {[]int{2, 4, 6, 8}, isOdd, true}, + {[]int{1, 2, 3, 4}, isOdd, false}, + {[]int{1, 2, 3, 4}, isEven, false}, + } + + for _, tt := range tests { + assert.Equal(tt.want, None(tt.slice, tt.predicateFn)) + } } func TestSome(t *testing.T) { - nums := []int{1, 2, 3, 5} - hasEven := func(i, num int) bool { - return num%2 == 0 - } + t.Parallel() assert := internal.NewAssert(t, "TestSome") - assert.Equal(true, Some(nums, hasEven)) -} -func TestFilter(t *testing.T) { - nums := []int{1, 2, 3, 4, 5} isEven := func(i, num int) bool { return num%2 == 0 } - - assert := internal.NewAssert(t, "TestFilter") - assert.Equal([]int{2, 4}, Filter(nums, isEven)) - - type student struct { - name string - age int - } - students := []student{ - {"a", 10}, - {"b", 11}, - {"c", 12}, - {"d", 13}, - {"e", 14}, + isOdd := func(i, num int) bool { + return num%2 == 1 } - studentsOfAageGreat12 := []student{ - {"d", 13}, - {"e", 14}, + + tests := []struct { + slice []int + predicateFn func(i, num int) bool + want bool + }{ + {[]int{1, 3, 5, 7}, isEven, false}, + {[]int{2, 4, 6, 8}, isOdd, false}, + {[]int{1, 2, 3, 4}, isOdd, true}, + {[]int{1, 2, 3, 4}, isEven, true}, } - filterFunc := func(i int, s student) bool { - return s.age > 12 + + for _, tt := range tests { + assert.Equal(tt.want, Some(tt.slice, tt.predicateFn)) } +} + +func TestFilter(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilter") + + t.Run("filter int slice", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + isEven := func(i, num int) bool { + return num%2 == 0 + } - assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc)) + assert.Equal([]int{2, 4}, Filter(nums, isEven)) + }) + + t.Run("filter struct slice", func(t *testing.T) { + type student struct { + name string + age int + } + students := []student{ + {"a", 10}, + {"b", 11}, + {"c", 12}, + {"d", 13}, + {"e", 14}, + } + studentsOfAgeGreat12 := []student{ + {"d", 13}, + {"e", 14}, + } + filterFunc := func(i int, s student) bool { + return s.age > 12 + } + + assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc)) + }) } func TestGroupBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGroupBy") + nums := []int{1, 2, 3, 4, 5, 6} evenFunc := func(i, num int) bool { return (num % 2) == 0 } - expectedEven := []int{2, 4, 6} - expectedOdd := []int{1, 3, 5} even, odd := GroupBy(nums, evenFunc) - assert := internal.NewAssert(t, "TestGroupBy") - assert.Equal(expectedEven, even) - assert.Equal(expectedOdd, odd) + assert.Equal([]int{2, 4, 6}, even) + assert.Equal([]int{1, 3, 5}, odd) +} + +func TestGroupWith(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGroupWith") + + nums := []float64{6.1, 4.2, 6.3} + floor := func(num float64) float64 { + return math.Floor(num) + } + expected := map[float64][]float64{ + 4: {4.2}, + 6: {6.1, 6.3}, + } + + assert.Equal(expected, GroupWith(nums, floor)) } func TestCount(t *testing.T) { + t.Parallel() + + numbers := []int{1, 2, 3, 3, 5, 6} + + assert := internal.NewAssert(t, "TestCountBy") + + assert.Equal(1, Count(numbers, 1)) + assert.Equal(2, Count(numbers, 3)) +} + +func TestCountBy(t *testing.T) { + t.Parallel() + nums := []int{1, 2, 3, 4, 5, 6} evenFunc := func(i, num int) bool { return (num % 2) == 0 } - assert := internal.NewAssert(t, "TestCount") - assert.Equal(3, Count(nums, evenFunc)) + assert := internal.NewAssert(t, "TestCountBy") + assert.Equal(3, CountBy(nums, evenFunc)) } func TestFind(t *testing.T) { + t.Parallel() + + nums := []int{1, 2, 3, 4, 5} + even := func(i, num int) bool { + return num%2 == 0 + } + result, ok := Find(nums, even) + + assert := internal.NewAssert(t, "TestFind") + + assert.Equal(true, ok) + assert.Equal(2, *result) + + _, ok = Find(nums, func(_ int, v int) bool { + return v == 6 + }) + + assert.Equal(false, ok) +} + +func TestFindBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFindBy") + + nums := []int{1, 2, 3, 4, 5} + + result, ok := FindBy(nums, func(i, num int) bool { + return num%2 == 0 + }) + assert.Equal(true, ok) + assert.Equal(2, result) + + result, ok = FindBy(nums, func(_ int, v int) bool { + return v == 6 + }) + assert.Equal(false, ok) + assert.Equal(0, result) +} + +func TestFindLastBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFindLastBy") + nums := []int{1, 2, 3, 4, 5} even := func(i, num int) bool { return num%2 == 0 } - res, ok := Find(nums, even) + result, ok := FindLastBy(nums, even) if !ok { t.Fatal("found nothing") } + assert.Equal(4, result) - assert := internal.NewAssert(t, "TestFind") - assert.Equal(2, res) + result, ok = FindLastBy(nums, func(_ int, v int) bool { + return v == 6 + }) + + assert.Equal(result == 0 && ok == false, true) } func TestFindLast(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFindLast") + nums := []int{1, 2, 3, 4, 5} even := func(i, num int) bool { return num%2 == 0 } - res, ok := FindLast(nums, even) + result, ok := FindLast(nums, even) if !ok { t.Fatal("found nothing") } - assert := internal.NewAssert(t, "TestFindLast") - assert.Equal(4, res) + assert.Equal(4, *result) } -func TestFindFoundNothing(t *testing.T) { - nums := []int{1, 1, 1, 1, 1, 1} - findFunc := func(i, num int) bool { - return num > 1 - } - _, ok := Find(nums, findFunc) - // if ok { - // t.Fatal("found something") - // } - assert := internal.NewAssert(t, "TestFindFoundNothing") - assert.Equal(false, ok) +func TestFlatten(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFlatten") + + input := [][][]string{{{"a", "b"}}, {{"c", "d"}}} + expected := [][]string{{"a", "b"}, {"c", "d"}} + + assert.Equal(expected, Flatten(input)) } func TestFlattenDeep(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFlattenDeep") + input := [][][]string{{{"a", "b"}}, {{"c", "d"}}} expected := []string{"a", "b", "c", "d"} - assert := internal.NewAssert(t, "TestFlattenDeep") assert.Equal(expected, FlattenDeep(input)) } func TestForEach(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestForEach") + numbers := []int{1, 2, 3, 4, 5} - expected := []int{3, 4, 5, 6, 7} - var numbersAddTwo []int - ForEach(numbers, func(index int, value int) { - numbersAddTwo = append(numbersAddTwo, value+2) + var result []int + addTwo := func(index int, value int) { + result = append(result, value+2) + } + + ForEach(numbers, addTwo) + + assert.Equal([]int{3, 4, 5, 6, 7}, result) +} + +func TestForEachConcurrent(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestForEachConcurrent") + + t.Run("single thread", func(t *testing.T) { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + result := make([]int, len(numbers)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + ForEachConcurrent(numbers, addOne, 1) + + expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10} + assert.Equal(expected, result) + }) + + t.Run("normal", func(t *testing.T) { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + result := make([]int, len(numbers)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + ForEachConcurrent(numbers, addOne, 4) + + expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10} + assert.Equal(expected, result) + }) + + t.Run("negative threads", func(t *testing.T) { + numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + result := make([]int, len(numbers)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + ForEachConcurrent(numbers, addOne, -4) + + expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10} + assert.Equal(expected, result) + }) + + t.Run("high number threads", func(t *testing.T) { + numbers := make([]int, 1000) + for i := range numbers { + numbers[i] = i + } + + result := make([]int, len(numbers)) + + addOne := func(index int, value int) { + result[index] = value + 1 + } + + ForEachConcurrent(numbers, addOne, 50) + + for i, item := range numbers { + assert.Equal(item+1, result[i]) + } }) +} + +func TestForEachWithBreak(t *testing.T) { + t.Parallel() assert := internal.NewAssert(t, "TestForEach") - assert.Equal(expected, numbersAddTwo) + + numbers := []int{1, 2, 3, 4, 5} + + var sum int + + ForEachWithBreak(numbers, func(_, n int) bool { + if n > 3 { + return false + } + sum += n + return true + }) + + assert.Equal(6, sum) } func TestMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMap") + nums := []int{1, 2, 3, 4} multiplyTwo := func(i, num int) int { return num * 2 } - assert := internal.NewAssert(t, "TestMap") assert.Equal([]int{2, 4, 6, 8}, Map(nums, multiplyTwo)) type student struct { @@ -251,7 +602,45 @@ func TestMap(t *testing.T) { assert.Equal(studentsOfAdd10Aage, Map(students, mapFunc)) } +func TestFilterMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilterMap") + + nums := []int{1, 2, 3, 4, 5} + + getEvenNumStr := func(i, num int) (string, bool) { + if num%2 == 0 { + return strconv.FormatInt(int64(num), 10), true + } + return "", false + } + + result := FilterMap(nums, getEvenNumStr) + + assert.Equal([]string{"2", "4"}, result) +} + +func TestFlatMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFlatMap") + + nums := []int{1, 2, 3, 4} + + result := FlatMap(nums, func(i int, num int) []string { + s := "hi-" + strconv.FormatInt(int64(num), 10) + return []string{s} + }) + + assert.Equal([]string{"hi-1", "hi-2", "hi-3", "hi-4"}, result) +} + func TestReduce(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReduce") + cases := [][]int{ {}, {1}, @@ -263,138 +652,375 @@ func TestReduce(t *testing.T) { return v1 + v2 } - assert := internal.NewAssert(t, "TestReduce") - for i := 0; i < len(cases); i++ { actual := Reduce(cases[i], f, 0) assert.Equal(expected[i], actual) } } -func TestIntSlice(t *testing.T) { - var nums []interface{} - nums = append(nums, 1, 2, 3) +func TestReduceConcurrent(t *testing.T) { + t.Parallel() - assert := internal.NewAssert(t, "TestIntSlice") - assert.Equal([]int{1, 2, 3}, IntSlice(nums)) -} + assert := internal.NewAssert(t, "TestReduceConcurrent") -func TestStringSlice(t *testing.T) { - var strs []interface{} - strs = append(strs, "a", "b", "c") + t.Run("basic", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 4) + assert.Equal(55, result) + }) - assert := internal.NewAssert(t, "TestStringSlice") - assert.Equal([]string{"a", "b", "c"}, StringSlice(strs)) -} + t.Run("empty slice", func(t *testing.T) { + nums := []int{} + result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 4) + assert.Equal(0, result) + }) -func TestInterfaceSlice(t *testing.T) { - strs := []string{"a", "b", "c"} - expect := []interface{}{"a", "b", "c"} + t.Run("single thread", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, 1) + assert.Equal(55, result) + }) - assert := internal.NewAssert(t, "TestInterfaceSlice") - assert.Equal(expect, InterfaceSlice(strs)) + t.Run("negative threads", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int { + return agg + item + }, -1) + assert.Equal(55, result) + }) } -func TestDeleteByIndex(t *testing.T) { - assert := internal.NewAssert(t, "TestDeleteByIndex") +func TestReduceBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReduceBy") - t1 := []string{"a", "b", "c", "d", "e"} - r1 := []string{"b", "c", "d", "e"} - a1, _ := DeleteByIndex(t1, 0) - assert.Equal(r1, a1) + result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int { + return agg + item + }) + assert.Equal(10, result1) - t2 := []string{"a", "b", "c", "d", "e"} - r2 := []string{"a", "b", "c", "e"} - a2, _ := DeleteByIndex(t2, 3) - assert.Equal(r2, a2) + result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) + assert.Equal("1234", result2) +} - t3 := []string{"a", "b", "c", "d", "e"} - r3 := []string{"c", "d", "e"} - a3, _ := DeleteByIndex(t3, 0, 2) - assert.Equal(r3, a3) +func TestReduceRight(t *testing.T) { + t.Parallel() - t4 := []string{"a", "b", "c", "d", "e"} - r4 := []string{} - a4, _ := DeleteByIndex(t4, 0, 5) - assert.Equal(r4, a4) + assert := internal.NewAssert(t, "TestReduceRight") - t5 := []string{"a", "b", "c", "d", "e"} - _, err := DeleteByIndex(t5, 1, 1) - assert.IsNotNil(err) + result := ReduceRight([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { + return agg + fmt.Sprintf("%v", item) + }) - _, err = DeleteByIndex(t5, 0, 6) - assert.IsNotNil(err) + assert.Equal("4321", result) } -func TestDrop(t *testing.T) { - assert := internal.NewAssert(t, "TestInterfaceSlice") +func TestIntSlice(t *testing.T) { + t.Parallel() - assert.Equal([]int{}, Drop([]int{}, 0)) - assert.Equal([]int{}, Drop([]int{}, 1)) - assert.Equal([]int{}, Drop([]int{}, -1)) + assert := internal.NewAssert(t, "TestIntSlice") - assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0)) - assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1)) - assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5)) - assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6)) + var nums []any + nums = append(nums, 1, 2, 3) - assert.Equal([]int{1, 2, 3, 4}, Drop([]int{1, 2, 3, 4, 5}, -1)) - assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6)) - assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6)) + assert.Equal([]int{1, 2, 3}, IntSlice(nums)) } -func TestInsertByIndex(t *testing.T) { - assert := internal.NewAssert(t, "TestInsertByIndex") +func TestStringSlice(t *testing.T) { + t.Parallel() - t1 := []string{"a", "b", "c"} - r1, _ := InsertByIndex(t1, 0, "1") - assert.Equal([]string{"1", "a", "b", "c"}, r1) + assert := internal.NewAssert(t, "TestStringSlice") - r2, _ := InsertByIndex(t1, 1, "1") - assert.Equal([]string{"a", "1", "b", "c"}, r2) + var strs []any + strs = append(strs, "a", "b", "c") - r3, _ := InsertByIndex(t1, 3, "1") - assert.Equal([]string{"a", "b", "c", "1"}, r3) + assert.Equal([]string{"a", "b", "c"}, StringSlice(strs)) +} - r4, _ := InsertByIndex(t1, 0, []string{"1", "2", "3"}) - assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, r4) +func TestInterfaceSlice(t *testing.T) { + t.Parallel() - r5, _ := InsertByIndex(t1, 3, []string{"1", "2", "3"}) - assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, r5) + assert := internal.NewAssert(t, "TestInterfaceSlice") - _, err := InsertByIndex(t1, 4, "1") - assert.IsNotNil(err) + strs := []string{"a", "b", "c"} + expect := []any{"a", "b", "c"} - _, err = InsertByIndex(t1, 0, 1) - assert.IsNotNil(err) + assert.Equal(expect, InterfaceSlice(strs)) } -func TestUpdateByIndex(t *testing.T) { - assert := internal.NewAssert(t, "TestUpdateByIndex") +func TestDeleteAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDeleteAt") + + tests := []struct { + slice []int + deletePos int + wang []int + }{ + {[]int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 1, []int{1, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3, 5}}, + {[]int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}}, + {[]int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4}}, + } + + for _, tt := range tests { + assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos)) + } +} - t1 := []string{"a", "b", "c"} - r1, _ := UpdateByIndex(t1, 0, "1") - assert.Equal([]string{"1", "b", "c"}, r1) +func TestDeleteRange(t *testing.T) { + t.Parallel() - t2 := []string{"a", "b", "c"} - r2, _ := UpdateByIndex(t2, 1, "1") - assert.Equal([]string{"a", "1", "c"}, r2) + assert := internal.NewAssert(t, "TestDeleteRange") - _, err := UpdateByIndex([]string{"a", "b", "c"}, 4, "1") - assert.IsNotNil(err) + arr := []int{1, 2, 3, 4, 5} - _, err = UpdateByIndex([]string{"a", "b", "c"}, 0, 1) - assert.IsNotNil(err) + assert.Equal([]int{1, 2, 3, 4, 5}, DeleteRange(arr, 0, 0)) + assert.Equal([]int{2, 3, 4, 5}, DeleteRange(arr, 0, 1)) + assert.Equal([]int{4, 5}, DeleteRange(arr, 0, 3)) } -func TestUnique(t *testing.T) { - assert := internal.NewAssert(t, "TestUnique") - +func TestDrop(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDrop") + + tests := []struct { + slice []int + dropNum int + want []int + }{ + {[]int{}, 0, []int{}}, + {[]int{}, 1, []int{}}, + {[]int{}, -1, []int{}}, + {[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 1, []int{2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 5, []int{}}, + {[]int{1, 2, 3, 4, 5}, 6, []int{}}, + } + + for _, tt := range tests { + assert.Equal(tt.want, Drop(tt.slice, tt.dropNum)) + } +} + +func TestDropRight(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDropRight") + + tests := []struct { + slice []int + dropNum int + want []int + }{ + {[]int{}, 0, []int{}}, + {[]int{}, 1, []int{}}, + {[]int{}, -1, []int{}}, + {[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, 1, []int{1, 2, 3, 4}}, + {[]int{}, 5, []int{}}, + {[]int{}, 6, []int{}}, + } + for _, tt := range tests { + assert.Equal(tt.want, DropRight(tt.slice, tt.dropNum)) + } +} + +func TestDropWhile(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDropWhile") + + tests := []struct { + slice []int + fn func(int) bool + want []int + }{ + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{2, 3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}}, + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}}, + } + + for _, tt := range tests { + assert.Equal(tt.want, DropWhile(tt.slice, tt.fn)) + } +} + +func TestDropRightWhile(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDropRightWhile") + + tests := []struct { + slice []int + fn func(int) bool + want []int + }{ + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{1, 2}}, + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}}, + {[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}}, + } + + for _, tt := range tests { + assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn)) + } +} + +func TestInsertAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestInsertAt") + + tests := []struct { + slice []string + insertPos int + insertValue any + want []string + }{ + {[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}}, + {[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}}, + {[]string{"a", "b", "c"}, 0, "1", []string{"1", "a", "b", "c"}}, + {[]string{"a", "b", "c"}, 1, "1", []string{"a", "1", "b", "c"}}, + {[]string{"a", "b", "c"}, 2, "1", []string{"a", "b", "1", "c"}}, + {[]string{"a", "b", "c"}, 3, "1", []string{"a", "b", "c", "1"}}, + {[]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}, []string{"1", "2", "3", "a", "b", "c"}}, + {[]string{"a", "b", "c"}, 3, []string{"1", "2", "3"}, []string{"a", "b", "c", "1", "2", "3"}}, + } + + for _, tt := range tests { + assert.Equal(tt.want, InsertAt(tt.slice, tt.insertPos, tt.insertValue)) + } +} + +func TestUpdateAt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUpdateAt") + + assert.Equal([]string{"a", "b", "c"}, UpdateAt([]string{"a", "b", "c"}, -1, "1")) + assert.Equal([]string{"1", "b", "c"}, UpdateAt([]string{"a", "b", "c"}, 0, "1")) + assert.Equal([]string{"a", "b", "2"}, UpdateAt([]string{"a", "b", "c"}, 2, "2")) + assert.Equal([]string{"a", "b", "c"}, UpdateAt([]string{"a", "b", "c"}, 3, "2")) +} + +func TestUnique(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnique") + assert.Equal([]int{1, 2, 3}, Unique([]int{1, 2, 2, 3})) assert.Equal([]string{"a", "b", "c"}, Unique([]string{"a", "a", "b", "c"})) } +func TestUniqueBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUniqueBy") + + actual := UniqueBy([]int{1, 2, 3, 4, 5, 6}, func(val int) int { + return val % 4 + }) + + assert.Equal([]int{1, 2, 3, 4}, actual) +} + +func TestUniqueByField(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUniqueByField") + + type User struct { + ID int `json:"id"` + Name string `json:"name"` + } + + users := []User{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "b"}, + {ID: 1, Name: "c"}, + } + + uniqueUsers, err := UniqueByField(users, "ID") + if err != nil { + t.Error(err) + } + + assert.Equal([]User{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "b"}, + }, uniqueUsers) +} + +func TestUniqueByComparator(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUniqueByComparator") + + t.Run("equal comparison", func(t *testing.T) { + nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7} + comparator := func(item int, other int) bool { + return item == other + } + result := UniqueByComparator(nums, comparator) + + assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result) + }) + + t.Run("unique struct slice by field", func(t *testing.T) { + type student struct { + Name string + Age int + } + + students := []student{ + {Name: "a", Age: 11}, + {Name: "b", Age: 12}, + {Name: "a", Age: 13}, + {Name: "c", Age: 14}, + } + + comparator := func(item, other student) bool { return item.Name == other.Name } + + result := UniqueByComparator(students, comparator) + + assert.Equal([]student{ + {Name: "a", Age: 11}, + {Name: "b", Age: 12}, + {Name: "c", Age: 14}, + }, result) + }) + + t.Run("case-insensitive string comparison", func(t *testing.T) { + stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"} + caseInsensitiveComparator := func(item, other string) bool { + return strings.ToLower(item) == strings.ToLower(other) + } + + result := UniqueByComparator(stringSlice, caseInsensitiveComparator) + + assert.Equal([]string{"apple", "banana", "cherry", "date"}, result) + }) + +} + func TestUnion(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestUnion") s1 := []int{1, 3, 4, 6} @@ -406,7 +1032,38 @@ func TestUnion(t *testing.T) { assert.Equal([]int{1, 3, 4, 6}, Union(s1)) } +func TestUnionBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUnionBy") + + testFunc := func(i int) int { + return i / 2 + } + + result := UnionBy(testFunc, []int{0, 1, 2, 3, 4, 5}, []int{0, 2, 10}) + assert.Equal(result, []int{0, 2, 4, 10}) +} + +func TestMerge(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMerge") + + s1 := []int{1, 2, 3, 4} + s2 := []int{2, 3, 4, 5} + s3 := []int{4, 5, 6} + + assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5, 4, 5, 6}, Merge(s1, s2, s3)) + assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5}, Merge(s1, s2)) + assert.Equal([]int{2, 3, 4, 5, 4, 5, 6}, Merge(s2, s3)) +} + func TestIntersection(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIntersection") + s1 := []int{1, 2, 2, 3} s2 := []int{1, 2, 3, 4} s3 := []int{0, 2, 3, 5, 6} @@ -418,53 +1075,200 @@ func TestIntersection(t *testing.T) { {1, 2, 3}, {}, } - res := []interface{}{ + result := []any{ Intersection(s1, s2, s3), Intersection(s1, s2), Intersection(s1), Intersection(s1, s4), } - assert := internal.NewAssert(t, "TestIntersection") - - for i := 0; i < len(res); i++ { - assert.Equal(res[i], expected[i]) + for i := 0; i < len(result); i++ { + assert.Equal(expected[i], result[i]) } - assert.IsNil(Intersection()) } -func TestReverseSlice(t *testing.T) { - assert := internal.NewAssert(t, "TestIntersection") +func TestSymmetricDifference(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSymmetricDifference") + + s1 := []int{1, 2, 3} + s2 := []int{1, 2, 4} + s3 := []int{1, 2, 3, 5} + + assert.Equal([]int{1, 2, 3}, SymmetricDifference(s1)) + assert.Equal([]int{3, 4}, SymmetricDifference(s1, s2)) + assert.Equal([]int{3, 4, 5}, SymmetricDifference(s1, s2, s3)) +} + +func TestReverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReverse") s1 := []int{1, 2, 3, 4, 5} - ReverseSlice(s1) + Reverse(s1) assert.Equal([]int{5, 4, 3, 2, 1}, s1) s2 := []string{"a", "b", "c", "d", "e"} - ReverseSlice(s2) + Reverse(s2) assert.Equal([]string{"e", "d", "c", "b", "a"}, s2) } +func TestReverseCopy(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestReverseCopy") + + numbers := []int{1, 2, 3, 4, 5} + reversedNumbers := ReverseCopy(numbers) + + assert.Equal([]int{5, 4, 3, 2, 1}, reversedNumbers) + assert.Equal([]int{1, 2, 3, 4, 5}, numbers) +} + func TestDifference(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestDifference") s1 := []int{1, 2, 3, 4, 5} s2 := []int{4, 5, 6} + assert.Equal([]int{1, 2, 3}, Difference(s1, s2)) } +func TestDifferenceWith(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestDifferenceWith") + + s1 := []int{1, 2, 3, 4, 5} + s2 := []int{4, 5, 6, 7, 8} + isDouble := func(v1, v2 int) bool { + return v2 == 2*v1 + } + + assert.Equal([]int{1, 5}, DifferenceWith(s1, s2, isDouble)) +} + func TestDifferenceBy(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestDifferenceBy") - s1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6 - s2 := []int{3, 4, 5} //after add one: 4 5 6 + s1 := []int{1, 2, 3, 4, 5} // after add one: 2 3 4 5 6 + s2 := []int{3, 4, 5} // after add one: 4 5 6 addOne := func(i int, v int) int { return v + 1 } + assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne)) } +func TestIsAscending(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsAscending") + + assert.Equal(true, IsAscending([]int{1, 2, 3, 4, 5})) + assert.Equal(false, IsAscending([]int{5, 4, 3, 2, 1})) + assert.Equal(false, IsAscending([]int{2, 1, 3, 4, 5})) +} + +func TestIsDescending(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsDescending") + + assert.Equal(true, IsDescending([]int{5, 4, 3, 2, 1})) + assert.Equal(false, IsDescending([]int{1, 2, 3, 4, 5})) + assert.Equal(false, IsDescending([]int{2, 1, 3, 4, 5})) +} + +func TestIsSorted(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsSorted") + + assert.Equal(true, IsSorted([]int{5, 4, 3, 2, 1})) + assert.Equal(true, IsSorted([]int{1, 2, 3, 4, 5})) + assert.Equal(false, IsSorted([]int{2, 1, 3, 4, 5})) +} + +func TestIsSortedByKey(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsSortedByKey") + + assert.Equal(true, IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int { + return len(s) + })) + + assert.Equal(true, IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int { + return len(s) + })) + + assert.Equal(false, IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int { + return len(s) + })) +} + +func TestSort(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSort") + + numbers := []int{1, 4, 3, 2, 5} + + Sort(numbers) + assert.Equal([]int{1, 2, 3, 4, 5}, numbers) + + Sort(numbers, "desc") + assert.Equal([]int{5, 4, 3, 2, 1}, numbers) + + strings := []string{"a", "d", "c", "b", "e"} + + Sort(strings) + assert.Equal([]string{"a", "b", "c", "d", "e"}, strings) + + Sort(strings, "desc") + assert.Equal([]string{"e", "d", "c", "b", "a"}, strings) +} + +func TestSortBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSortBy") + + numbers := []int{1, 4, 3, 2, 5} + + SortBy(numbers, func(a, b int) bool { + return a < b + }) + assert.Equal([]int{1, 2, 3, 4, 5}, numbers) + + type User struct { + Name string + Age uint + } + + users := []User{ + {Name: "a", Age: 21}, + {Name: "b", Age: 15}, + {Name: "c", Age: 100}} + + SortBy(users, func(a, b User) bool { + return a.Age < b.Age + }) + + assert.EqualValues(15, users[0].Age) + assert.EqualValues(21, users[1].Age) + assert.EqualValues(100, users[2].Age) +} + func TestSortByFielDesc(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSortByFielDesc") type student struct { @@ -487,10 +1291,12 @@ func TestSortByFielDesc(t *testing.T) { err := SortByField(students, "age", "desc") assert.IsNil(err) - assert.Equal(students, studentsOfSortByAge) + assert.Equal(studentsOfSortByAge, students) } func TestSortByFieldAsc(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSortByFieldAsc") type student struct { @@ -513,33 +1319,658 @@ func TestSortByFieldAsc(t *testing.T) { err := SortByField(students, "age") assert.IsNil(err) - assert.Equal(students, studentsOfSortByAge) + assert.Equal(studentsOfSortByAge, students) } func TestWithout(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestWithout") assert.Equal([]int{3, 4, 5}, Without([]int{1, 2, 3, 4, 5}, 1, 2)) assert.Equal([]int{1, 2, 3, 4, 5}, Without([]int{1, 2, 3, 4, 5})) } func TestShuffle(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestShuffle") - s := []int{1, 2, 3, 4, 5} - res := Shuffle(s) - t.Log("Shuffle result: ", res) + numbers := []int{1, 2, 3, 4, 5} + result := Shuffle(numbers) + + assert.Equal(5, len(result)) +} + +func TestShuffleCopy(t *testing.T) { + t.Parallel() - assert.Equal(reflect.TypeOf(s), reflect.TypeOf(res)) + assert := internal.NewAssert(t, "TestShuffleCopy") - rv := reflect.ValueOf(res) - assert.Equal(5, rv.Len()) + numbers := []int{1, 2, 3, 4, 5} + result := ShuffleCopy(numbers) + + assert.Equal(5, len(result)) + assert.Equal([]int{1, 2, 3, 4, 5}, numbers) +} + +func TestIndexOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOf") + + arr := []string{"a", "a", "b", "c"} + key := fmt.Sprintf("%p", arr) + assert.Equal(0, IndexOf(arr, "a")) + assert.Equal(-1, IndexOf(arr, "d")) + assert.Equal(2, memoryHashCounter[key]) + + arr1 := []int{1, 2, 3, 4, 5} + key1 := fmt.Sprintf("%p", arr1) + assert.Equal(3, IndexOf(arr1, 4)) + assert.Equal(-1, IndexOf(arr1, 6)) + assert.Equal(2, memoryHashCounter[key1]) + + arr2 := []float64{1.1, 2.2, 3.3, 4.4, 5.5} + key2 := fmt.Sprintf("%p", arr2) + assert.Equal(2, IndexOf(arr2, 3.3)) + assert.Equal(3, IndexOf(arr2, 4.4)) + assert.Equal(-1, IndexOf(arr2, 6.6)) + assert.Equal(3, memoryHashCounter[key2]) + + for i := 0; i < 6; i++ { + a := []string{"a", "b", "c"} + IndexOf(a, "a") + IndexOf(a, "b") + } + minArr := []string{"c", "b", "a"} + minKey := fmt.Sprintf("%p", minArr) + assert.Equal(0, IndexOf(minArr, "c")) + + arr3 := []string{"q", "w", "e"} + key3 := fmt.Sprintf("%p", arr3) + assert.Equal(1, IndexOf(arr3, "w")) + assert.Equal(-1, IndexOf(arr3, "r")) + assert.Equal(2, memoryHashCounter[key3]) + assert.Equal(0, memoryHashCounter[minKey]) + + arr4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + const numGoroutines = 100 + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(i int) { + defer wg.Done() + index := IndexOf(arr4, i%10+1) + assert.Equal(i%10, index) + }(i) + } + wg.Wait() +} + +func BenchmarkIndexOfDifferentSizes(b *testing.B) { + sizes := []int{10, 100, 1000, 10000, 100000} + for _, size := range sizes { + arr := make([]int, size) + for i := 0; i < len(arr); i++ { + arr[i] = i + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = IndexOf(arr, size/2) // 查找数组中间的元素 + } + }) + } +} - assert.Equal(true, rv.Kind() == reflect.Slice) - assert.Equal(true, rv.Type().Elem().Kind() == reflect.Int) +func TestLastIndexOf(t *testing.T) { + t.Parallel() - assert.Equal(true, Contain(res, 1)) - assert.Equal(true, Contain(res, 2)) - assert.Equal(true, Contain(res, 3)) - assert.Equal(true, Contain(res, 4)) - assert.Equal(true, Contain(res, 5)) + assert := internal.NewAssert(t, "TestLastIndexOf") + + arr := []string{"a", "b", "b", "c"} + assert.Equal(0, LastIndexOf(arr, "a")) + assert.Equal(2, LastIndexOf(arr, "b")) + assert.Equal(-1, LastIndexOf(arr, "d")) +} + +func TestToSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToSlice") + + str1 := "a" + str2 := "b" + assert.Equal([]string{"a"}, ToSlice(str1)) + assert.Equal([]string{"a", "b"}, ToSlice(str1, str2)) +} + +func TestToSlicePointer(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestToSlicePointer") + + str1 := "a" + str2 := "b" + assert.Equal([]*string{&str1}, ToSlicePointer(str1)) + assert.Equal([]*string{&str1, &str2}, ToSlicePointer(str1, str2)) +} + +func TestAppendIfAbsent(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestAppendIfAbsent") + + str1 := []string{"a", "b"} + assert.Equal([]string{"a", "b"}, AppendIfAbsent(str1, "a")) + assert.Equal([]string{"a", "b", "c"}, AppendIfAbsent(str1, "c")) +} + +func TestReplace(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReplace") + + strs := []string{"a", "b", "a", "c", "d", "a"} + + assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "a", "x", 0)) + assert.Equal([]string{"x", "b", "a", "c", "d", "a"}, Replace(strs, "a", "x", 1)) + assert.Equal([]string{"x", "b", "x", "c", "d", "a"}, Replace(strs, "a", "x", 2)) + assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", 3)) + assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", 4)) + + assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", -1)) + assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, Replace(strs, "a", "x", -2)) + + assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "x", "y", 1)) + assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, Replace(strs, "x", "y", -1)) +} + +func TestReplaceAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReplaceAll") + + strs := []string{"a", "b", "a", "c", "d", "a"} + + assert.Equal([]string{"x", "b", "x", "c", "d", "x"}, ReplaceAll(strs, "a", "x")) + assert.Equal([]string{"a", "b", "a", "c", "d", "a"}, ReplaceAll(strs, "e", "x")) +} + +func TestKeyBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestKeyBy") + + result := KeyBy([]string{"a", "ab", "abc"}, func(str string) int { + return len(str) + }) + + assert.Equal(map[int]string{1: "a", 2: "ab", 3: "abc"}, result) +} + +func TestRepeat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRepeat") + + assert.Equal([]string{}, Repeat("a", 0)) + assert.Equal([]string{"a", "a", "a", "a", "a", "a"}, Repeat("a", 6)) +} + +func TestJoin(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestJoin") + + nums := []int{1, 2, 3, 4, 5} + + result1 := Join(nums, ",") + result2 := Join(nums, "-") + + assert.Equal("1,2,3,4,5", result1) + assert.Equal("1-2-3-4-5", result2) +} + +func TestPartition(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPartition") + + assert.Equal([][]int{{1, 2, 3, 4, 5}}, Partition([]int{1, 2, 3, 4, 5})) + assert.Equal([][]int{{2, 4}, {1, 3, 5}}, Partition([]int{1, 2, 3, 4, 5}, func(n int) bool { return n%2 == 0 })) + assert.Equal([][]int{{1, 2}, {3, 4}, {5}}, Partition([]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })) +} + +func TestRandom(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRandom") + + var arr []int + var val, idx int + + _, idx = Random(arr) + assert.Equal(-1, idx) + + arr = []int{} + _, idx = Random(arr) + assert.Equal(-1, idx) + + arr = []int{1} + val, idx = Random(arr) + assert.Equal(0, idx) + assert.Equal(arr[idx], val) + + arr = []int{1, 2, 3} + val, idx = Random(arr) + assert.Greater(len(arr), idx) + assert.Equal(arr[idx], val) +} + +func TestSetToDefaultIf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "SetToDefaultIf") + + // Subtest for strings + t.Run("strings", func(t *testing.T) { + strs := []string{"a", "b", "a", "c", "d", "a"} + actualStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s }) + assert.Equal([]string{"", "b", "", "c", "d", ""}, actualStrs) + assert.Equal(3, count) + }) + + // Subtest for integers + t.Run("integers", func(t *testing.T) { + ints := []int{1, 2, 3, 2, 4, 2} + actualInts, count := SetToDefaultIf(ints, func(i int) bool { return i == 2 }) + assert.Equal([]int{1, 0, 3, 0, 4, 0}, actualInts) + assert.Equal(3, count) + }) + + // Subtest for floating-point numbers + t.Run("floats", func(t *testing.T) { + floats := []float64{1.1, 2.2, 3.3, 2.2, 4.4, 2.2} + actualFloats, count := SetToDefaultIf(floats, func(f float64) bool { return f == 2.2 }) + assert.Equal([]float64{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats) + assert.Equal(3, count) + }) + + // Subtest for booleans + t.Run("booleans", func(t *testing.T) { + bools := []bool{true, false, true, true, false} + actualBools, count := SetToDefaultIf(bools, func(b bool) bool { return b }) + assert.Equal([]bool{false, false, false, false, false}, actualBools) + assert.Equal(3, count) + }) + + // Subtest for a custom type + type customType struct { + field string + } + t.Run("customType", func(t *testing.T) { + customs := []customType{{"a"}, {"b"}, {"a"}, {"c"}} + actualCustoms, count := SetToDefaultIf(customs, func(c customType) bool { return c.field == "a" }) + expected := []customType{{""}, {"b"}, {""}, {"c"}} + assert.Equal(expected, actualCustoms) + assert.Equal(2, count) + }) + + // Subtest for slice of integers + t.Run("sliceOfInts", func(t *testing.T) { + sliceOfInts := [][]int{{1, 2}, {3, 4}, {5, 6}, {1, 2}} + actualSlice, count := SetToDefaultIf(sliceOfInts, func(s []int) bool { return reflect.DeepEqual(s, []int{1, 2}) }) + expected := [][]int{nil, {3, 4}, {5, 6}, nil} + assert.Equal(expected, actualSlice) + assert.Equal(2, count) + }) + + // Subtest for maps (simple use case) + t.Run("mapOfStringToInts", func(t *testing.T) { + maps := []map[string]int{{"a": 1}, {"b": 2}, {"a": 1}, {"c": 3}} + actualMaps, count := SetToDefaultIf(maps, func(m map[string]int) bool { _, ok := m["a"]; return ok }) + expected := []map[string]int{nil, {"b": 2}, nil, {"c": 3}} + assert.Equal(expected, actualMaps) + assert.Equal(2, count) + }) + + // Subtest for pointers to integers + t.Run("pointersToInts", func(t *testing.T) { + one, two, three := 1, 2, 3 + pointers := []*int{&one, &two, &one, &three} + actualPointers, count := SetToDefaultIf(pointers, func(p *int) bool { return p != nil && *p == 1 }) + expected := []*int{nil, &two, nil, &three} + assert.Equal(expected, actualPointers) + assert.Equal(2, count) + }) + + // Subtest for channels + t.Run("channels", func(t *testing.T) { + ch1, ch2 := make(chan int), make(chan int) + channels := []chan int{ch1, ch2, ch1} + actualChannels, count := SetToDefaultIf(channels, func(ch chan int) bool { return ch == ch1 }) + expected := []chan int{nil, ch2, nil} + assert.Equal(expected, actualChannels) + assert.Equal(2, count) + }) + + // Subtest for interfaces + t.Run("interfaces", func(t *testing.T) { + var i1, i2 interface{} = "hello", 42 + interfaces := []interface{}{i1, i2, i1} + actualInterfaces, count := SetToDefaultIf(interfaces, func(i interface{}) bool { _, ok := i.(string); return ok }) + expected := []interface{}{nil, 42, nil} + assert.Equal(expected, actualInterfaces) + assert.Equal(2, count) + }) + + // Subtest for complex structs + t.Run("complexStructs", func(t *testing.T) { + type ComplexStruct struct { + Name string + Value int + Data []byte + } + cs1, cs2 := ComplexStruct{Name: "Test", Value: 1, Data: []byte{1, 2, 3}}, ComplexStruct{Name: "Another", Value: 2, Data: []byte{4, 5, 6}} + complexStructs := []ComplexStruct{cs1, cs2, cs1} + actualComplexStructs, count := SetToDefaultIf(complexStructs, func(cs ComplexStruct) bool { return cs.Name == "Test" }) + expected := []ComplexStruct{{}, cs2, {}} + assert.Equal(expected, actualComplexStructs) + assert.Equal(2, count) + }) + + // Subtest for uints + t.Run("uints", func(t *testing.T) { + uints := []uint{1, 2, 3, 2, 4, 2} + actualUints, count := SetToDefaultIf(uints, func(u uint) bool { return u == 2 }) + assert.Equal([]uint{1, 0, 3, 0, 4, 0}, actualUints) + assert.Equal(3, count) + }) + + // Subtest for float32 + t.Run("float32s", func(t *testing.T) { + floats := []float32{1.1, 2.2, 3.3, 2.2, 4.4, 2.2} + actualFloats, count := SetToDefaultIf(floats, func(f float32) bool { return f == 2.2 }) + assert.Equal([]float32{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats) + assert.Equal(3, count) + }) + + // Subtest for []byte + t.Run("byteSlices", func(t *testing.T) { + bytes := [][]byte{{'a', 'b'}, {'c', 'd'}, {'a', 'b'}} + actualBytes, count := SetToDefaultIf(bytes, func(b []byte) bool { return string(b) == "ab" }) + expected := [][]byte{nil, {'c', 'd'}, nil} + assert.Equal(expected, actualBytes) + assert.Equal(2, count) + }) +} + +func TestBreak(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBreak") + + // Test with integers + nums := []int{1, 2, 3, 4, 5} + even := func(n int) bool { return n%2 == 0 } + + resultEven, resultAfterFirstEven := Break(nums, even) + assert.Equal([]int{1}, resultEven) + assert.Equal([]int{2, 3, 4, 5}, resultAfterFirstEven) + + // Test with strings + strings := []string{"apple", "banana", "cherry", "date", "elderberry"} + startsWithA := func(s string) bool { return s[0] == 'a' } + + resultStartsWithA, resultAfterFirstStartsWithA := Break(strings, startsWithA) + assert.Equal([]string{}, resultStartsWithA) + assert.Equal([]string{"apple", "banana", "cherry", "date", "elderberry"}, resultAfterFirstStartsWithA) + + // Test with empty slice + emptySlice := []int{} + resultEmpty, _ := Break(emptySlice, even) + assert.Equal([]int{}, resultEmpty) + + // Test with all elements satisfying the predicate + allEven := []int{2, 4, 6, 8, 10} + emptyResult, resultAllEven := Break(allEven, even) + assert.Equal([]int{2, 4, 6, 8, 10}, resultAllEven) + assert.Equal([]int{}, emptyResult) + + // Test with no elements satisfying the predicate + allOdd := []int{1, 3, 5, 7, 9} + resultAllOdd, emptyResult := Break(allOdd, even) + assert.Equal([]int{1, 3, 5, 7, 9}, resultAllOdd) + assert.Equal([]int{}, emptyResult) +} + +func TestRightPaddingAndLeftPadding(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "RightPaddingAndLeftPadding") + + // Test with integers + nums := []int{1, 2, 3, 4, 5} + + padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3) + assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded) +} + +func TestUniqueByConcurrent(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUniqueByConcurrent") + + nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7} + comparator := func(item int, other int) bool { return item == other } + + result := UniqueByConcurrent(nums, comparator, 4) + + assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result) +} + +func TestMapConcurrent(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestMapConcurrent") + + t.Run("empty slice", func(t *testing.T) { + actual := MapConcurrent([]int{}, func(_, n int) int { return n * n }, 4) + assert.Equal([]int{}, actual) + }) + + t.Run("single thread", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6} + expected := []int{1, 4, 9, 16, 25, 36} + actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 1) + assert.Equal(expected, actual) + }) + + t.Run("multiple threads", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6} + expected := []int{1, 4, 9, 16, 25, 36} + actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 4) + assert.Equal(expected, actual) + }) + +} + +func TestFilterConcurrent(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFilterConcurrent") + + t.Run("empty slice", func(t *testing.T) { + actual := FilterConcurrent([]int{}, func(_, n int) bool { return n != 0 }, 4) + assert.Equal([]int{}, actual) + }) + + t.Run("single thread", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6} + expected := []int{4, 5, 6} + actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 1) + assert.Equal(expected, actual) + }) + + t.Run("multiple threads", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6} + expected := []int{4, 5, 6} + actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4) + sort.Ints(actual) + sort.Ints(expected) + assert.Equal(expected, actual) + }) +} + +func TestFrequency(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFrequency") + + t.Run("empty slice", func(t *testing.T) { + result := Frequency([]int{}) + assert.Equal(map[int]int{}, result) + }) + + t.Run("int slice", func(t *testing.T) { + nums := []int{1, 2, 2, 3, 3, 3} + expected := map[int]int{1: 1, 2: 2, 3: 3} + result := Frequency(nums) + + assert.Equal(expected, result) + }) + + t.Run("string slice", func(t *testing.T) { + strs := []string{"a", "b", "b", "c", "c", "c"} + expected := map[string]int{"a": 1, "b": 2, "c": 3} + result := Frequency(strs) + + assert.Equal(expected, result) + }) + + t.Run("struct slice", func(t *testing.T) { + type student struct { + Name string + Age int + } + + students := []student{ + {Name: "a", Age: 11}, + {Name: "b", Age: 12}, + {Name: "a", Age: 13}, + {Name: "c", Age: 14}, + } + + expected := map[student]int{ + {Name: "a", Age: 11}: 1, + {Name: "a", Age: 13}: 1, + {Name: "b", Age: 12}: 1, + {Name: "c", Age: 14}: 1, + } + result := Frequency(students) + + assert.Equal(expected, result) + }) +} + +func TestJoinFunc(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestJoinFunc") + + t.Run("basic case", func(t *testing.T) { + result := JoinFunc([]int{1, 2, 3}, ", ", func(i int) int { + return i * 2 + }) + + expected := "2, 4, 6" + assert.Equal(expected, result) + }) + + t.Run("empty slice", func(t *testing.T) { + result := JoinFunc([]int{}, ", ", func(i int) int { + return i * 2 + }) + + assert.Equal("", result) + }) + + t.Run("single element slice", func(t *testing.T) { + result := JoinFunc([]int{1}, ", ", func(i int) int { + return i * 2 + }) + + assert.Equal("2", result) + }) + + t.Run("string slice", func(t *testing.T) { + result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string { + return strings.ToUpper(s) + }) + + expected := "A, B, C" + assert.Equal(expected, result) + }) +} + +func TestConcatBy(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestConcatBy") + + t.Run("Join strings", func(t *testing.T) { + result := ConcatBy([]string{"Hello", "World"}, ", ", func(a, b string) string { + return a + b + }) + + expected := "Hello, World" + assert.Equal(expected, result) + }) + + t.Run("Join Person struct", func(t *testing.T) { + type Person struct { + Name string + Age int + } + + people := []Person{ + {Name: "Alice", Age: 30}, + {Name: "Bob", Age: 25}, + {Name: "Charlie", Age: 35}, + } + sep := Person{Name: " | ", Age: 0} + + personConnector := func(a, b Person) Person { + return Person{Name: a.Name + b.Name, Age: a.Age + b.Age} + } + + result := ConcatBy(people, sep, personConnector) + + assert.Equal("Alice | Bob | Charlie", result.Name) + assert.Equal(90, result.Age) + }) +} + +func TestEqualUnordered(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestEqualUnordered") + + tests := []struct { + slice1, slice2 []int + expected bool + }{ + {[]int{}, []int{}, true}, + {[]int{1, 2, 3}, []int{1, 2, 3}, true}, + {[]int{1, 2, 3}, []int{3, 2, 1}, true}, + {[]int{1, 2, 3}, []int{1, 2, 3, 4}, false}, + {[]int{1, 2, 3}, []int{1, 2}, false}, + {[]int{1, 2, 3}, []int{1, 2, 4}, false}, + } + + for _, test := range tests { + assert.Equal(test.expected, EqualUnordered(test.slice1, test.slice2)) + } } diff --git a/stream/stream.go b/stream/stream.go new file mode 100644 index 00000000..7c2acd83 --- /dev/null +++ b/stream/stream.go @@ -0,0 +1,431 @@ +// Copyright 2023 dudaodong@gmail.com. All rights resulterved. +// Use of this source code is governed by MIT license + +// Package stream 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. +package stream + +import ( + "bytes" + "encoding/gob" + + "github.com/duke-git/lancet/v2/slice" + "golang.org/x/exp/constraints" +) + +// A stream should implements methods: +// type StreamI[T any] interface { + +// // part methods of Java Stream Specification. +// Distinct() StreamI[T] +// Filter(predicate func(item T) bool) StreamI[T] +// FlatMap(mapper func(item T) StreamI[T]) StreamI[T] +// Map(mapper func(item T) T) StreamI[T] +// Peek(consumer func(item T)) StreamI[T] + +// Sorted(less func(a, b T) bool) StreamI[T] +// Max(less func(a, b T) bool) (T, bool) +// Min(less func(a, b T) bool) (T, bool) + +// Limit(maxSize int) StreamI[T] +// Skip(n int) StreamI[T] + +// AllMatch(predicate func(item T) bool) bool +// AnyMatch(predicate func(item T) bool) bool +// NoneMatch(predicate func(item T) bool) bool +// ForEach(consumer func(item T)) +// Reduce(init T, accumulator func(a, b T) T) T +// Count() int + +// FindFirst() (T, bool) + +// ToSlice() []T + +// // part of methods custom extension +// Reverse() StreamI[T] +// Range(start, end int) StreamI[T] +// Concat(streams ...StreamI[T]) StreamI[T] +// } + +type Stream[T any] struct { + source []T +} + +// Of creates a stream whose elements are the specified values. +// Play: https://go.dev/play/p/jI6_iZZuVFE +func Of[T any](elems ...T) Stream[T] { + return FromSlice(elems) +} + +// Generate stream where each element is generated by the provided generater function +// Play: https://go.dev/play/p/rkOWL1yA3j9 +func Generate[T any](generator func() func() (item T, ok bool)) Stream[T] { + source := make([]T, 0) + + var zeroValue T + for next, item, ok := generator(), zeroValue, true; ok; { + + item, ok = next() + if ok { + source = append(source, item) + } + } + + return FromSlice(source) +} + +// FromSlice creates stream from slice. +// Play: https://go.dev/play/p/wywTO0XZtI4 +func FromSlice[T any](source []T) Stream[T] { + return Stream[T]{source: source} +} + +// FromChannel creates stream from channel. +// Play: https://go.dev/play/p/9TZYugGMhXZ +func FromChannel[T any](source <-chan T) Stream[T] { + s := make([]T, 0) + + for v := range source { + s = append(s, v) + } + + return FromSlice(s) +} + +// FromRange creates a number stream from start to end. both start and end are included. [start, end] +// Play: https://go.dev/play/p/9Ex1-zcg-B- +func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Stream[T] { + if end < start { + panic("stream.FromRange: param start should be before param end") + } else if step <= 0 { + panic("stream.FromRange: param step should be positive") + } + + l := int((end-start)/step) + 1 + source := make([]T, l) + + for i := 0; i < l; i++ { + source[i] = start + (T(i) * step) + } + + return FromSlice(source) +} + +// Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. +// Play: https://go.dev/play/p/HM4OlYk_OUC +func Concat[T any](a, b Stream[T]) Stream[T] { + source := make([]T, 0) + + source = append(source, a.source...) + source = append(source, b.source...) + + return FromSlice(source) +} + +// Distinct returns a stream that removes the duplicated items. +// Play: https://go.dev/play/p/eGkOSrm64cB +func (s Stream[T]) Distinct() Stream[T] { + source := make([]T, 0) + + distinct := map[string]bool{} + + for _, v := range s.source { + k := hashKey(v) + if _, ok := distinct[k]; !ok { + distinct[k] = true + source = append(source, v) + } + } + + return FromSlice(source) +} + +func hashKey(data any) string { + buffer := bytes.NewBuffer(nil) + encoder := gob.NewEncoder(buffer) + err := encoder.Encode(data) + if err != nil { + panic("stream.hashKey: get hashkey failed") + } + return buffer.String() +} + +// Filter returns a stream consisting of the elements of this stream that match the given predicate. +// Play: https://go.dev/play/p/MFlSANo-buc +func (s Stream[T]) Filter(predicate func(item T) bool) Stream[T] { + source := make([]T, 0) + + for _, v := range s.source { + if predicate(v) { + source = append(source, v) + } + } + + return FromSlice(source) +} + +// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream. +// Play: https://go.dev/play/p/OtNQUImdYko +func (s Stream[T]) Map(mapper func(item T) T) Stream[T] { + source := make([]T, s.Count()) + + for i, v := range s.source { + source[i] = mapper(v) + } + + return FromSlice(source) +} + +// Peek returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream. +// Play: https://go.dev/play/p/u1VNzHs6cb2 +func (s Stream[T]) Peek(consumer func(item T)) Stream[T] { + for _, v := range s.source { + consumer(v) + } + + return s +} + +// Skip returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. +// If this stream contains fewer than n elements then an empty stream will be returned. +// Play: https://go.dev/play/p/fNdHbqjahum +func (s Stream[T]) Skip(n int) Stream[T] { + if n <= 0 { + return s + } + + source := make([]T, 0) + l := len(s.source) + + if n > l { + return FromSlice(source) + } + + source = make([]T, 0, l-n) + for i := n; i < l; i++ { + source = append(source, s.source[i]) + } + + return FromSlice(source) +} + +// Limit returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length. +// Play: https://go.dev/play/p/qsO4aniDcGf +func (s Stream[T]) Limit(maxSize int) Stream[T] { + if s.source == nil { + return s + } + + if maxSize < 0 { + return FromSlice([]T{}) + } + + source := make([]T, 0, maxSize) + + for i := 0; i < len(s.source) && i < maxSize; i++ { + source = append(source, s.source[i]) + } + + return FromSlice(source) +} + +// AllMatch returns whether all elements of this stream match the provided predicate. +// Play: https://go.dev/play/p/V5TBpVRs-Cx +func (s Stream[T]) AllMatch(predicate func(item T) bool) bool { + for _, v := range s.source { + if !predicate(v) { + return false + } + } + + return true +} + +// AnyMatch returns whether any elements of this stream match the provided predicate. +// Play: https://go.dev/play/p/PTCnWn4OxSn +func (s Stream[T]) AnyMatch(predicate func(item T) bool) bool { + for _, v := range s.source { + if predicate(v) { + return true + } + } + + return false +} + +// NoneMatch returns whether no elements of this stream match the provided predicate. +// Play: https://go.dev/play/p/iWS64pL1oo3 +func (s Stream[T]) NoneMatch(predicate func(item T) bool) bool { + return !s.AnyMatch(predicate) +} + +// ForEach performs an action for each element of this stream. +// Play: https://go.dev/play/p/Dsm0fPqcidk +func (s Stream[T]) ForEach(action func(item T)) { + for _, v := range s.source { + action(v) + } +} + +// Reduce performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any. +// Play: https://go.dev/play/p/6uzZjq_DJLU +func (s Stream[T]) Reduce(initial T, accumulator func(a, b T) T) T { + for _, v := range s.source { + initial = accumulator(initial, v) + } + + return initial +} + +// Count returns the count of elements in the stream. +// Play: https://go.dev/play/p/r3koY6y_Xo- +func (s Stream[T]) Count() int { + return len(s.source) +} + +// FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty. +// Play: https://go.dev/play/p/9xEf0-6C1e3 +func (s Stream[T]) FindFirst() (T, bool) { + var result T + + if s.source == nil || len(s.source) == 0 { + return result, false + } + + return s.source[0], true +} + +// FindLast returns the last element of this stream and true, or zero value and false if the stream is empty. +// Play: https://go.dev/play/p/WZD2rDAW-2h +func (s Stream[T]) FindLast() (T, bool) { + var result T + + if s.source == nil || len(s.source) == 0 { + return result, false + } + + return s.source[len(s.source)-1], true +} + +// Reverse returns a stream whose elements are reverse order of given stream. +// Play: https://go.dev/play/p/A8_zkJnLHm4 +func (s Stream[T]) Reverse() Stream[T] { + l := len(s.source) + source := make([]T, l) + + for i := 0; i < l; i++ { + source[i] = s.source[l-1-i] + } + return FromSlice(source) +} + +// Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream. +// Play: https://go.dev/play/p/indZY5V2f4j +func (s Stream[T]) Range(start, end int) Stream[T] { + if start < 0 { + start = 0 + } + if end < 0 { + end = 0 + } + if start >= end { + return FromSlice([]T{}) + } + + source := make([]T, 0) + + if end > len(s.source) { + end = len(s.source) + } + + for i := start; i < end; i++ { + source = append(source, s.source[i]) + } + + return FromSlice(source) +} + +// Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function. +// Play: https://go.dev/play/p/XXtng5uonFj +func (s Stream[T]) Sorted(less func(a, b T) bool) Stream[T] { + source := []T{} + source = append(source, s.source...) + + slice.SortBy(source, less) + + return FromSlice(source) +} + +// Max returns the maximum element of this stream according to the provided less function. +// less: a > b +// Play: https://go.dev/play/p/fm-1KOPtGzn +func (s Stream[T]) Max(less func(a, b T) bool) (T, bool) { + var max T + + if len(s.source) == 0 { + return max, false + } + + for i, v := range s.source { + if less(v, max) || i == 0 { + max = v + } + } + return max, true +} + +// Min returns the minimum element of this stream according to the provided less function. +// less: a < b +// Play: https://go.dev/play/p/vZfIDgGNRe_0 +func (s Stream[T]) Min(less func(a, b T) bool) (T, bool) { + var min T + + if len(s.source) == 0 { + return min, false + } + + for i, v := range s.source { + if less(v, min) || i == 0 { + min = v + } + } + + return min, true +} + +// IndexOf returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element. +// Play: https://go.dev/play/p/tBV5Nc-XDX2 +func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int { + for i, v := range s.source { + if equal(v, target) { + return i + } + } + return -1 +} + +// LastIndexOf returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element. +// Play: https://go.dev/play/p/CjeoNw2eac_G +func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int { + for i := len(s.source) - 1; i >= 0; i-- { + if equal(s.source[i], target) { + return i + } + } + return -1 +} + +// ToSlice return the elements in the stream. +// Play: https://go.dev/play/p/jI6_iZZuVFE +func (s Stream[T]) ToSlice() []T { + return s.source +} + +func ToMap[T any, K comparable, V any](s Stream[T], mapper func(item T) (K, V)) map[K]V { + result := map[K]V{} + for _, v := range s.source { + key, value := mapper(v) + result[key] = value + } + return result +} diff --git a/stream/stream_example_test.go b/stream/stream_example_test.go new file mode 100644 index 00000000..706314c1 --- /dev/null +++ b/stream/stream_example_test.go @@ -0,0 +1,433 @@ +package stream + +import ( + "fmt" +) + +func ExampleOf() { + s := Of(1, 2, 3) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} + +func ExampleFromSlice() { + s := FromSlice([]int{1, 2, 3}) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} + +func ExampleFromChannel() { + ch := make(chan int) + go func() { + for i := 1; i < 4; i++ { + ch <- i + } + close(ch) + }() + + s := FromChannel(ch) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} + +func ExampleFromRange() { + s := FromRange(1, 5, 1) + + data := s.ToSlice() + fmt.Println(data) + + // Output: + // [1 2 3 4 5] +} + +func ExampleGenerate() { + n := 0 + max := 4 + + generator := func() func() (int, bool) { + return func() (int, bool) { + n++ + return n, n < max + } + } + + s := Generate(generator) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3] +} + +func ExampleConcat() { + s1 := FromSlice([]int{1, 2, 3}) + s2 := FromSlice([]int{4, 5, 6}) + + s := Concat(s1, s2) + + data := s.ToSlice() + + fmt.Println(data) + + // Output: + // [1 2 3 4 5 6] +} + +func ExampleStream_Distinct() { + original := FromSlice([]int{1, 2, 2, 3, 3, 3}) + distinct := original.Distinct() + + data1 := original.ToSlice() + data2 := distinct.ToSlice() + + fmt.Println(data1) + fmt.Println(data2) + + // Output: + // [1 2 2 3 3 3] + // [1 2 3] +} + +func ExampleStream_Filter() { + original := FromSlice([]int{1, 2, 3, 4, 5}) + + isEven := func(n int) bool { + return n%2 == 0 + } + + even := original.Filter(isEven) + + fmt.Println(even.ToSlice()) + + // Output: + // [2 4] +} + +func ExampleStream_Map() { + original := FromSlice([]int{1, 2, 3}) + + addOne := func(n int) int { + return n + 1 + } + + increament := original.Map(addOne) + + fmt.Println(increament.ToSlice()) + + // Output: + // [2 3 4] +} + +func ExampleStream_Peek() { + original := FromSlice([]int{1, 2, 3}) + + data := []string{} + peekStream := original.Peek(func(n int) { + data = append(data, fmt.Sprint("value", n)) + }) + + fmt.Println(original.ToSlice()) + fmt.Println(peekStream.ToSlice()) + fmt.Println(data) + + // Output: + // [1 2 3] + // [1 2 3] + // [value1 value2 value3] +} + +func ExampleStream_Skip() { + original := FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Skip(-1) + s2 := original.Skip(0) + s3 := original.Skip(1) + s4 := original.Skip(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [1 2 3 4] + // [1 2 3 4] + // [2 3 4] + // [] +} + +func ExampleStream_Limit() { + original := FromSlice([]int{1, 2, 3, 4}) + + s1 := original.Limit(-1) + s2 := original.Limit(0) + s3 := original.Limit(1) + s4 := original.Limit(5) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [] + // [1] + // [1 2 3 4] +} + +func ExampleStream_AllMatch() { + original := FromSlice([]int{1, 2, 3}) + + result1 := original.AllMatch(func(item int) bool { + return item > 0 + }) + + result2 := original.AllMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleStream_AnyMatch() { + original := FromSlice([]int{1, 2, 3}) + + result1 := original.AnyMatch(func(item int) bool { + return item > 1 + }) + + result2 := original.AnyMatch(func(item int) bool { + return item > 3 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleStream_NoneMatch() { + original := FromSlice([]int{1, 2, 3}) + + result1 := original.NoneMatch(func(item int) bool { + return item > 3 + }) + + result2 := original.NoneMatch(func(item int) bool { + return item > 1 + }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleStream_ForEach() { + original := FromSlice([]int{1, 2, 3}) + + result := 0 + original.ForEach(func(item int) { + result += item + }) + + fmt.Println(result) + + // Output: + // 6 +} + +func ExampleStream_Reduce() { + original := FromSlice([]int{1, 2, 3}) + + result := original.Reduce(0, func(a, b int) int { + return a + b + }) + + fmt.Println(result) + + // Output: + // 6 +} + +func ExampleStream_FindFirst() { + original := FromSlice([]int{1, 2, 3}) + + result, ok := original.FindFirst() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} + +func ExampleStream_FindLast() { + original := FromSlice([]int{3, 2, 1}) + + result, ok := original.FindLast() + + fmt.Println(result) + fmt.Println(ok) + + // Output: + // 1 + // true +} + +func ExampleStream_Reverse() { + original := FromSlice([]int{1, 2, 3}) + + reverse := original.Reverse() + + fmt.Println(reverse.ToSlice()) + + // Output: + // [3 2 1] +} + +func ExampleStream_Range() { + original := FromSlice([]int{1, 2, 3}) + + s1 := original.Range(0, 0) + s2 := original.Range(0, 1) + s3 := original.Range(0, 3) + s4 := original.Range(1, 2) + + fmt.Println(s1.ToSlice()) + fmt.Println(s2.ToSlice()) + fmt.Println(s3.ToSlice()) + fmt.Println(s4.ToSlice()) + + // Output: + // [] + // [1] + // [1 2 3] + // [2] +} + +func ExampleStream_Sorted() { + original := FromSlice([]int{4, 2, 1, 3}) + + sorted := original.Sorted(func(a, b int) bool { return a < b }) + + fmt.Println(original.ToSlice()) + fmt.Println(sorted.ToSlice()) + + // Output: + // [4 2 1 3] + // [1 2 3 4] +} + +func ExampleStream_Max() { + original := FromSlice([]int{4, 2, 1, 3}) + + max, ok := original.Max(func(a, b int) bool { return a > b }) + + fmt.Println(max) + fmt.Println(ok) + + // Output: + // 4 + // true +} + +func ExampleStream_Min() { + original := FromSlice([]int{4, 2, 1, 3}) + + min, ok := original.Min(func(a, b int) bool { return a < b }) + + fmt.Println(min) + fmt.Println(ok) + + // Output: + // 1 + // true +} + +func ExampleStream_Count() { + s1 := FromSlice([]int{1, 2, 3}) + s2 := FromSlice([]int{}) + + fmt.Println(s1.Count()) + fmt.Println(s2.Count()) + + // Output: + // 3 + // 0 +} + +func ExampleStream_IndexOf() { + s := FromSlice([]int{1, 2, 3, 2}) + + result1 := s.IndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.IndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 1 +} + +func ExampleStream_LastIndexOf() { + s := FromSlice([]int{1, 2, 3, 2}) + + result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b }) + result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b }) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // -1 + // 3 +} + +func ExampleToMap() { + type Person struct { + Name string + Age int + } + s := FromSlice([]Person{ + {Name: "Tom", Age: 10}, + {Name: "Jim", Age: 20}, + {Name: "Mike", Age: 30}, + }) + m := ToMap(s, func(p Person) (string, Person) { + return p.Name, p + }) + fmt.Println(m) + + // Output: + // map[Jim:{Jim 20} Mike:{Mike 30} Tom:{Tom 10}] +} diff --git a/stream/stream_test.go b/stream/stream_test.go new file mode 100644 index 00000000..cf6e0a2f --- /dev/null +++ b/stream/stream_test.go @@ -0,0 +1,425 @@ +package stream + +import ( + "fmt" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestOf(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromSlice") + + stream := Of(1, 2, 3) + assert.Equal([]int{1, 2, 3}, stream.ToSlice()) +} + +func TestGenerate(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromSlice") + + n := 0 + max := 4 + + generator := func() func() (int, bool) { + return func() (int, bool) { + n++ + return n, n < max + } + } + + stream := Generate(generator) + + assert.Equal([]int{1, 2, 3}, stream.ToSlice()) +} + +func TestFromSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromSlice") + + stream := FromSlice([]int{1, 2, 3}) + assert.Equal([]int{1, 2, 3}, stream.ToSlice()) +} + +func TestFromChannel(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromChannel") + + ch := make(chan int) + go func() { + for i := 1; i < 4; i++ { + ch <- i + } + close(ch) + }() + + stream := FromChannel(ch) + + assert.Equal([]int{1, 2, 3}, stream.ToSlice()) +} + +func TestFromRange(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFromRange") + + s1 := FromRange(1, 5, 1) + s2 := FromRange(1.1, 5.0, 1.0) + + assert.Equal([]int{1, 2, 3, 4, 5}, s1.ToSlice()) + assert.Equal([]float64{1.1, 2.1, 3.1, 4.1}, s2.ToSlice()) +} + +func TestStream_Distinct(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStream_Distinct") + + nums := FromSlice([]int{1, 2, 2, 3, 3, 3}) + distinctNums := nums.Distinct() + + assert.Equal([]int{1, 2, 2, 3, 3, 3}, nums.ToSlice()) + assert.Equal([]int{1, 2, 3}, distinctNums.ToSlice()) + + type Person struct { + Id string + Name string + Age uint + } + + people := []Person{ + {Id: "001", Name: "Tom", Age: 10}, + {Id: "001", Name: "Tom", Age: 10}, + {Id: "002", Name: "Jim", Age: 20}, + {Id: "003", Name: "Mike", Age: 30}, + } + + stream := FromSlice(people) + distinctStream := stream.Distinct() + + // {[{001 Tom 10} {001 Tom 10} {002 Jim 20} {003 Mike 30}]} + t.Log(stream) + + // {[{001 Tom 10} {002 Jim 20} {003 Mike 30}]} + t.Log(distinctStream) +} + +func TestStream_Filter(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStream_Filter") + + stream := FromSlice([]int{1, 2, 3, 4, 5}) + + isEven := func(n int) bool { + return n%2 == 0 + } + + even := stream.Filter(isEven) + + assert.Equal([]int{2, 4}, even.ToSlice()) +} + +func TestStream_Map(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Map") + + stream := FromSlice([]int{1, 2, 3}) + + addOne := func(n int) int { + return n + 1 + } + + s := stream.Map(addOne) + + assert.Equal([]int{2, 3, 4}, s.ToSlice()) +} + +func TestStream_Peek(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Peek") + + stream := FromSlice([]int{1, 2, 3, 4, 5, 6}) + + result := []string{} + stream = stream.Filter(func(n int) bool { + return n <= 3 + }).Peek(func(n int) { + result = append(result, fmt.Sprint("current: ", n)) + }) + + assert.Equal([]int{1, 2, 3}, stream.ToSlice()) + assert.Equal([]string{ + "current: 1", "current: 2", "current: 3", + }, result) +} + +func TestStream_Skip(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Peek") + + stream := FromSlice([]int{1, 2, 3, 4}) + + s1 := stream.Skip(-1) + s2 := stream.Skip(0) + s3 := stream.Skip(1) + s4 := stream.Skip(2) + + assert.Equal([]int{1, 2, 3, 4}, s1.ToSlice()) + assert.Equal([]int{1, 2, 3, 4}, s2.ToSlice()) + assert.Equal([]int{2, 3, 4}, s3.ToSlice()) + assert.Equal([]int{3, 4}, s4.ToSlice()) +} + +func TestStream_Limit(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Limit") + + stream := FromSlice([]int{1, 2, 3, 4, 5, 6}) + + s1 := stream.Limit(-1) + s2 := stream.Limit(0) + s3 := stream.Limit(1) + s4 := stream.Limit(6) + + assert.Equal([]int{}, s1.ToSlice()) + assert.Equal([]int{}, s2.ToSlice()) + assert.Equal([]int{1}, s3.ToSlice()) + assert.Equal([]int{1, 2, 3, 4, 5, 6}, s4.ToSlice()) +} + +func TestStream_AllMatch(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_AllMatch") + + stream := FromSlice([]int{1, 2, 3, 4, 5, 6}) + + result1 := stream.AllMatch(func(item int) bool { + return item > 0 + }) + + result2 := stream.AllMatch(func(item int) bool { + return item > 1 + }) + + assert.Equal(true, result1) + assert.Equal(false, result2) +} + +func TestStream_AnyMatch(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_AnyMatch") + + stream := FromSlice([]int{1, 2, 3}) + + result1 := stream.AnyMatch(func(item int) bool { + return item > 3 + }) + + result2 := stream.AnyMatch(func(item int) bool { + return item > 1 + }) + + assert.Equal(false, result1) + assert.Equal(true, result2) +} + +func TestStream_NoneMatch(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_NoneMatch") + + stream := FromSlice([]int{1, 2, 3}) + + result1 := stream.NoneMatch(func(item int) bool { + return item > 3 + }) + + result2 := stream.NoneMatch(func(item int) bool { + return item > 1 + }) + + assert.Equal(true, result1) + assert.Equal(false, result2) +} + +func TestStream_ForEach(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_ForEach") + + stream := FromSlice([]int{1, 2, 3}) + + result := 0 + stream.ForEach(func(item int) { + result += item + }) + + assert.Equal(6, result) +} + +func TestStream_Reduce(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Reduce") + + stream := FromSlice([]int{1, 2, 3}) + + result := stream.Reduce(0, func(a, b int) int { + return a + b + }) + + assert.Equal(6, result) +} + +func TestStream_Count(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Count") + + s1 := FromSlice([]int{1, 2, 3}) + s2 := FromSlice([]int{}) + + assert.Equal(3, s1.Count()) + assert.Equal(0, s2.Count()) +} + +func TestStream_FindFirst(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_FindFirst") + + stream := FromSlice([]int{1, 2, 3}) + + result, ok := stream.FindFirst() + + assert.Equal(1, result) + assert.Equal(true, ok) +} + +func TestStream_FindLast(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_FindLast") + + stream := FromSlice([]int{3, 2, 1}) + + result, ok := stream.FindLast() + + assert.Equal(1, result) + assert.Equal(true, ok) + + stream2 := FromSlice([]int{}) + + result, ok = stream2.FindLast() + + assert.Equal(0, result) + assert.Equal(false, ok) +} + +func TestStream_Reverse(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Reverse") + + s := FromSlice([]int{1, 2, 3}) + + rs := s.Reverse() + + assert.Equal([]int{3, 2, 1}, rs.ToSlice()) +} + +func TestStream_Range(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Range") + + s := FromSlice([]int{1, 2, 3}) + + s1 := s.Range(-1, 0) + assert.Equal([]int{}, s1.ToSlice()) + + s2 := s.Range(0, -1) + assert.Equal([]int{}, s2.ToSlice()) + + s3 := s.Range(0, 0) + assert.Equal([]int{}, s3.ToSlice()) + + s4 := s.Range(1, 1) + assert.Equal([]int{}, s4.ToSlice()) + + s5 := s.Range(0, 1) + assert.Equal([]int{1}, s5.ToSlice()) + + s6 := s.Range(0, 4) + assert.Equal([]int{1, 2, 3}, s6.ToSlice()) +} + +func TestStream_Concat(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Concat") + + s1 := FromSlice([]int{1, 2, 3}) + s2 := FromSlice([]int{4, 5, 6}) + + s := Concat(s1, s2) + + assert.Equal([]int{1, 2, 3, 4, 5, 6}, s.ToSlice()) +} + +func TestStream_Sorted(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Sorted") + + s := FromSlice([]int{4, 2, 1, 3}) + + s1 := s.Sorted(func(a, b int) bool { return a < b }) + + assert.Equal([]int{4, 2, 1, 3}, s.ToSlice()) + assert.Equal([]int{1, 2, 3, 4}, s1.ToSlice()) +} + +func TestStream_Max(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Max") + + s := FromSlice([]int{4, 2, 1, 3}) + + max, ok := s.Max(func(a, b int) bool { return a > b }) + + assert.Equal(4, max) + assert.Equal(true, ok) +} + +func TestStream_Min(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Min") + + s := FromSlice([]int{4, 2, 1, 3}) + + max, ok := s.Max(func(a, b int) bool { return a < b }) + + assert.Equal(1, max) + assert.Equal(true, ok) +} + +func TestStream_IndexOf(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_IndexOf") + + s := FromSlice([]int{4, 2, 1, 3, 4}) + + assert.Equal(-1, s.IndexOf(0, func(a, b int) bool { return a == b })) + assert.Equal(0, s.IndexOf(4, func(a, b int) bool { return a == b })) + assert.Equal(3, s.IndexOf(3, func(a, b int) bool { return a == b })) +} + +func TestStream_LastIndexOf(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_LastIndexOf") + + s := FromSlice([]int{4, 2, 1, 3, 2}) + + assert.Equal(-1, s.LastIndexOf(0, func(a, b int) bool { return a == b })) + assert.Equal(4, s.LastIndexOf(2, func(a, b int) bool { return a == b })) +} + +func TestStream_ToMap(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_ToMap") + type Person struct { + Name string + Age int + } + s := FromSlice([]Person{ + {Name: "Tom", Age: 10}, + {Name: "Jim", Age: 20}, + {Name: "Mike", Age: 30}, + }) + m := ToMap(s, func(p Person) (string, Person) { + return p.Name, p + }) + expected := map[string]Person{ + "Tom": {Name: "Tom", Age: 10}, + "Jim": {Name: "Jim", Age: 20}, + "Mike": {Name: "Mike", Age: 30}, + } + assert.EqualValues(expected, m) + +} diff --git a/structs/field.go b/structs/field.go new file mode 100644 index 00000000..cff2f433 --- /dev/null +++ b/structs/field.go @@ -0,0 +1,129 @@ +package structs + +import ( + "reflect" + + "github.com/duke-git/lancet/v2/pointer" +) + +// Field is abstract struct field for provide several high level functions +type Field struct { + Struct + field reflect.StructField + tag *Tag +} + +func newField(v reflect.Value, f reflect.StructField, tagName string) *Field { + tag := f.Tag.Get(tagName) + field := &Field{ + field: f, + tag: newTag(tag), + } + field.rvalue = v + field.rtype = v.Type() + field.TagName = tagName + return field +} + +// Tag returns the value that the key in the tag string. +func (f *Field) Tag() *Tag { + return f.tag +} + +// Value returns the underlying value of the field. +func (f *Field) Value() any { + return f.rvalue.Interface() +} + +// IsEmbedded returns true if the given field is an embedded field. +func (f *Field) IsEmbedded() bool { + return len(f.field.Index) > 1 +} + +// IsExported returns true if the given field is exported. +func (f *Field) IsExported() bool { + return f.field.IsExported() +} + +// IsZero returns true if the given field is zero value. +func (f *Field) IsZero() bool { + z := reflect.Zero(f.rvalue.Type()).Interface() + v := f.Value() + return reflect.DeepEqual(z, v) +} + +// IsNil returns true if the given field is nil value. +func (f *Field) IsNil() bool { + v := f.Value() + if v == nil || (reflect.ValueOf(v)).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil() { + return true + } + + return false +} + +// Name returns the name of the given field +func (f *Field) Name() string { + return f.field.Name +} + +// Kind returns the field's kind +func (f *Field) Kind() reflect.Kind { + return f.rvalue.Kind() +} + +// IsSlice check if a struct field type is slice or not +func (f *Field) IsSlice() bool { + k := f.rvalue.Kind() + return k == reflect.Slice +} + +// IsTargetType check if a struct field type is target type or not +func (f *Field) IsTargetType(targetType reflect.Kind) bool { + return f.rvalue.Kind() == targetType +} + +// mapValue covert field value to map +func (f *Field) mapValue(value any) any { + val := pointer.ExtractPointer(value) + v := reflect.ValueOf(val) + var ret any + + switch v.Kind() { + case reflect.Struct: + s := New(val) + s.TagName = f.TagName + m, _ := s.ToMap() + ret = m + case reflect.Map: + mapEl := v.Type().Elem() + switch mapEl.Kind() { + case reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice, reflect.Chan: + // iterate the map + m := make(map[string]any, v.Len()) + for _, key := range v.MapKeys() { + m[key.String()] = f.mapValue(v.MapIndex(key).Interface()) + } + ret = m + default: + ret = v.Interface() + } + case reflect.Slice, reflect.Array: + sEl := v.Type().Elem() + switch sEl.Kind() { + case reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice, reflect.Chan: + slices := make([]any, v.Len()) + for i := 0; i < v.Len(); i++ { + slices[i] = f.mapValue(v.Index(i).Interface()) + } + ret = slices + default: + ret = v.Interface() + } + default: + if v.Kind().String() != "invalid" { + ret = v.Interface() + } + } + return ret +} diff --git a/structs/field_test.go b/structs/field_test.go new file mode 100644 index 00000000..29fe27d6 --- /dev/null +++ b/structs/field_test.go @@ -0,0 +1,315 @@ +package structs + +import ( + "reflect" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestField_Tag(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_Tag") + + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := New(p1) + n, _ := s.Field("Name") + tag := n.Tag() + + assert.Equal("name", tag.Name) + assert.Equal(true, tag.HasOption("omitempty")) +} + +func TestField_Value(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_Value") + + type Parent struct { + Name string `json:"name,omitempty"` + } + p1 := &Parent{"111"} + + s := New(p1) + n, _ := s.Field("Name") + + assert.Equal("111", n.Value()) +} + +func TestField_IsEmbedded(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_IsEmbedded") + + type Parent struct { + Name string + } + type Child struct { + Parent + Age int + } + c1 := &Child{} + c1.Name = "111" + c1.Age = 11 + + s := New(c1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + assert.Equal(true, n.IsEmbedded()) + assert.Equal(false, a.IsEmbedded()) +} + +func TestField_IsExported(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_IsEmbedded") + + type Parent struct { + Name string + age int + } + p1 := &Parent{Name: "11", age: 11} + s := New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("age") + + assert.Equal(true, n.IsExported()) + assert.Equal(false, a.IsExported()) +} + +func TestField_IsZero(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_IsZero") + + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + assert.Equal(true, n.IsZero()) + assert.Equal(false, a.IsZero()) +} + +func TestField_Name(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_Name") + + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + assert.Equal("Name", n.Name()) + assert.Equal("Age", a.Name()) +} + +func TestField_Kind(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_Kind") + + type Parent struct { + Name string + Age int + } + p1 := &Parent{Age: 11} + s := New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("Age") + + assert.Equal(reflect.String, n.Kind()) + assert.Equal(reflect.Int, a.Kind()) +} + +func TestField_IsSlice(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_IsSlice") + + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{arr: []int{1, 2, 3}} + s := New(p1) + a, _ := s.Field("arr") + + assert.Equal(true, a.IsSlice()) +} + +func TestField_IsTargetType(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_IsTargetType") + + type Parent struct { + Name string + arr []int + } + + p1 := &Parent{Name: "test", arr: []int{1, 2, 3}} + s := New(p1) + n, _ := s.Field("Name") + a, _ := s.Field("arr") + + assert.Equal(true, n.IsTargetType(reflect.String)) + assert.Equal(true, a.IsTargetType(reflect.Slice)) +} + +func TestField_MapValue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestField_MapValue") + + t.Run("nested struct", func(t *testing.T) { + type Child struct { + Name string `json:"name"` + } + type Parent struct { + Name string `json:"name"` + Child *Child `json:"child"` + } + + c1 := &Child{"11-1"} + p1 := &Parent{ + Name: "11", + Child: c1, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + + assert.Equal(true, ok) + assert.Equal(map[string]any{"name": "11-1"}, val) + }) + + t.Run("nested ptr struct", func(t *testing.T) { + type Child struct { + Name string `json:"name"` + } + type Parent struct { + Name string `json:"name"` + Child any `json:"child"` + } + c1 := &Child{"11-1"} + c2 := &c1 + c3 := &c2 + p1 := &Parent{ + Name: "11", + Child: c3, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + + assert.Equal(true, ok) + assert.Equal(map[string]any{"name": "11-1"}, val) + }) + + t.Run("nested array", func(t *testing.T) { + type Parent struct { + Name string `json:"name"` + Child []int `json:"child"` + } + + p1 := &Parent{ + Name: "11", + Child: []int{1, 2, 3}, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + + assert.Equal(true, ok) + assert.Equal([]int{1, 2, 3}, val) + }) + + t.Run("nested array struct", func(t *testing.T) { + type Child struct { + Name string `json:"name"` + } + type Parent struct { + Name string `json:"name"` + Child []*Child `json:"child"` + } + + c1 := &Child{"11-1"} + c2 := &Child{"11-2"} + + p1 := &Parent{ + Name: "11", + Child: []*Child{c1, c2}, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + + assert.Equal(true, ok) + arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}} + assert.Equal(arr, val) + }) + + t.Run("nested ptr array struct", func(t *testing.T) { + type Child struct { + Name string `json:"name"` + } + type Parent struct { + Name string `json:"name"` + Child *[]*Child `json:"child"` + } + + c1 := &Child{"11-1"} + c2 := &Child{"11-2"} + + p1 := &Parent{ + Name: "11", + Child: &[]*Child{c1, c2}, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + + assert.Equal(true, ok) + arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}} + assert.Equal(arr, val) + }) + + t.Run("nested map in struct", func(t *testing.T) { + type Parent struct { + Name string `json:"name"` + Child map[string]any `json:"child"` + } + p1 := &Parent{ + Name: "11", + Child: map[string]any{"a": 1, "b": map[string]any{"name": "11-1"}}, + } + + s := New(p1) + f, ok := s.Field("Child") + val := f.mapValue(f.Value()) + assert.Equal(true, ok) + assert.Equal(map[string]any{"a": 1, "b": map[string]any{"name": "11-1"}}, val) + }) +} diff --git a/structs/struct.go b/structs/struct.go new file mode 100644 index 00000000..54819187 --- /dev/null +++ b/structs/struct.go @@ -0,0 +1,124 @@ +// Package structs provide several high level functions to manipulate struct, tag, and field. +package structs + +import ( + "fmt" + "reflect" + + "github.com/duke-git/lancet/v2/pointer" +) + +// defaultTagName is the default tag for struct fields to lookup. +var defaultTagName = "json" + +// Struct is abstract struct for provide several high level functions +type Struct struct { + raw any + rtype reflect.Type + rvalue reflect.Value + TagName string +} + +// New returns a new *Struct +func New(value any, tagName ...string) *Struct { + value = pointer.ExtractPointer(value) + v := reflect.ValueOf(value) + t := reflect.TypeOf(value) + + tn := defaultTagName + + if len(tagName) > 0 { + tn = tagName[0] + } + + // if need: can also set defaultTagName to tn across structs package level + // defaultTagName = tn + + return &Struct{ + raw: value, + rtype: t, + rvalue: v, + TagName: tn, + } +} + +// ToMap converts the given struct to a map[string]any, where the keys +// of the keys are the field names and the values of the map are the values +// of the fields. The default map key is the struct field name, but you can +// change it. The `json` key is the default tag key. Example: +// +// // default +// Name string `json:"name"` +// +// // ignore the field +// Name string // no tag +// Age string `json:"-"` // json ignore tag +// sex string // unexported field +// Goal int `json:"goal,omitempty"` // omitempty if the field is zero value +// +// // custom map key +// Name string `json:"myName"` +// +// ToMap convert the exported fields of a struct to map. +func (s *Struct) ToMap() (map[string]any, error) { + if !s.IsStruct() { + return nil, fmt.Errorf("invalid struct %v", s) + } + + result := make(map[string]any) + fields := s.Fields() + for _, f := range fields { + if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" { + continue + } + + if f.IsZero() && f.tag.HasOption("omitempty") { + continue + } + + if f.IsNil() { + continue + } + + result[f.tag.Name] = f.mapValue(f.Value()) + } + + return result, nil +} + +// Fields returns all the struct fields within a slice +func (s *Struct) Fields() []*Field { + fieldNum := s.rvalue.NumField() + fields := make([]*Field, 0, fieldNum) + for i := 0; i < fieldNum; i++ { + v := s.rvalue.Field(i) + sf := s.rtype.Field(i) + field := newField(v, sf, s.TagName) + fields = append(fields, field) + } + return fields +} + +// Field returns a Field if the given field name was found +func (s *Struct) Field(name string) (*Field, bool) { + f, ok := s.rtype.FieldByName(name) + if !ok { + return nil, false + } + return newField(s.rvalue.FieldByName(name), f, s.TagName), true +} + +// IsStruct returns true if the given rvalue is a struct +func (s *Struct) IsStruct() bool { + k := s.rvalue.Kind() + if k == reflect.Invalid { + return false + } + return k == reflect.Struct +} + +// ToMap convert struct to map, only convert exported struct field +// map key is specified same as struct field tag `json` value. +func ToMap(v any) (map[string]any, error) { + return New(v).ToMap() +} diff --git a/structs/struct_test.go b/structs/struct_test.go new file mode 100644 index 00000000..8040ced5 --- /dev/null +++ b/structs/struct_test.go @@ -0,0 +1,179 @@ +package structs + +import ( + "reflect" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestStruct_ToMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStruct_ToMap") + t.Run("invalid struct", func(t *testing.T) { + m, _ := ToMap(1) + var expected map[string]any + assert.Equal(expected, m) + }) + + t.Run("StructToMap", func(_ *testing.T) { + type People struct { + Name string `json:"name"` + age int + } + p := &People{ + "test", + 100, + } + pm, _ := ToMap(p) + var expected = map[string]any{"name": "test"} + assert.Equal(expected, pm) + }) + + t.Run("StructToMapWithJsonAttr", func(_ *testing.T) { + type People struct { + Name string `json:"name,omitempty"` // json tag with attribute + Phone string `json:"phone"` // json tag without attribute + Sex string `json:"-"` // ignore by "-" + Age int // ignore by no tag + email string // ignore by unexported + IsWorking bool `json:"is_working"` + } + p1 := People{ + Name: "AAA", // exist + Phone: "1111", + Sex: "male", + Age: 100, + email: "11@gmail.com", + } + p1m, _ := ToMap(p1) + var expect1 = map[string]any{"name": "AAA", "phone": "1111", "is_working": false} + assert.Equal(expect1, p1m) + + p2 := People{ + Name: "", + Phone: "2222", + Sex: "male", + Age: 0, + email: "22@gmail.com", + IsWorking: true, + } + p2m, _ := ToMap(p2) + var expect2 = map[string]any{"phone": "2222", "is_working": true} + assert.Equal(expect2, p2m) + }) +} + +func Test_ToMap2(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestStruct_ToMap") + + type M struct { + Name string `json:"name"` + } + + type S struct { + ID int `json:"id"` + M *M `json:"m"` + } + + s1 := &S{ + ID: 1, + } + var expect1 = map[string]any{"id": 1} + map1, err := ToMap(s1) + + assert.IsNil(err) + assert.Equal(expect1, map1) + + s2 := &S{ + ID: 1, + M: &M{ + Name: "test", + }, + } + var expect2 = map[string]any{ + "id": 1, + "m": map[string]any{ + "name": "test", + }} + map2, err := ToMap(s2) + + assert.IsNil(err) + assert.Equal(expect2, map2) +} + +func TestStruct_Fields(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStruct_Fields") + + type Parent struct { + A string `json:"a"` + B int `json:"b"` + C []string `json:"c"` + D map[string]any `json:"d"` + } + + p1 := &Parent{ + A: "1", + B: 11, + C: []string{"11", "22"}, + D: map[string]any{"d1": 1, "d2": 2}, + } + + s := New(p1) + fields := s.Fields() + assert.Equal(4, len(fields)) + assert.Equal(reflect.String, fields[0].Kind()) + assert.Equal(reflect.Int, fields[1].Kind()) + assert.Equal(reflect.Slice, fields[2].Kind()) + assert.Equal(reflect.Map, fields[3].Kind()) +} + +func TestStruct_Field(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStruct_Field") + + type Parent struct { + A string `json:"a"` + B int `json:"b"` + C []string `json:"c"` + D map[string]any `json:"d"` + } + + p1 := &Parent{ + A: "1", + B: 11, + C: []string{"11", "22"}, + D: map[string]any{"d1": 1, "d2": 2}, + } + + s := New(p1) + a, ok := s.Field("A") + + assert.Equal(true, ok) + assert.Equal(reflect.String, a.Kind()) + assert.Equal("1", a.Value()) + assert.Equal("a", a.tag.Name) + assert.Equal(false, a.tag.HasOption("omitempty")) + assert.Equal(false, a.tag.IsEmpty()) +} + +func TestStruct_IsStruct(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStruct_Field") + + type Test1 struct{} + t1 := &Test1{} + t2 := 1 + + s1 := New(t1) + s2 := New(t2) + + assert.Equal(true, s1.IsStruct()) + assert.Equal(false, s2.IsStruct()) +} diff --git a/structs/tag.go b/structs/tag.go new file mode 100644 index 00000000..2bf9917f --- /dev/null +++ b/structs/tag.go @@ -0,0 +1,36 @@ +package structs + +import ( + "strings" + + "github.com/duke-git/lancet/v2/validator" +) + +// Tag is abstract struct field tag +type Tag struct { + Name string + Options []string +} + +func newTag(tag string) *Tag { + res := strings.Split(tag, ",") + return &Tag{ + Name: res[0], + Options: res[1:], + } +} + +// HasOption check if a struct field tag has option setting. +func (t *Tag) HasOption(opt string) bool { + for _, o := range t.Options { + if o == opt { + return true + } + } + return false +} + +// IsEmpty check if a struct field has tag setting. +func (t *Tag) IsEmpty() bool { + return validator.IsEmptyString(t.Name) +} diff --git a/strutil/string.go b/strutil/string.go index 268099df..46b53574 100644 --- a/strutil/string.go +++ b/strutil/string.go @@ -1,60 +1,57 @@ -// Copyright 2021 dudaodong@gmail.com. All rights reserved. // Use of this source code is governed by MIT license // Package strutil implements some functions to manipulate string. package strutil import ( + "errors" + "math/rand" "regexp" "strings" + "time" "unicode" "unicode/utf8" + "unsafe" ) -// CamelCase covert string to camelCase string. +// used in `Shuffle` function +var rng = rand.New(rand.NewSource(int64(time.Now().UnixNano()))) + +// CamelCase coverts string to camelCase string. Non letters and numbers will be ignored. +// Play: https://go.dev/play/p/9eXP3tn2tUy func CamelCase(s string) string { - if len(s) == 0 { - return "" - } + var builder strings.Builder - res := "" - blankSpace := " " - regex, _ := regexp.Compile("[-_&]+") - ss := regex.ReplaceAllString(s, blankSpace) - for i, v := range strings.Split(ss, blankSpace) { - vv := []rune(v) + strs := splitIntoStrings(s, false) + for i, str := range strs { if i == 0 { - if vv[i] >= 65 && vv[i] <= 96 { - vv[0] += 32 - } - res += string(vv) + builder.WriteString(strings.ToLower(str)) } else { - res += Capitalize(v) + builder.WriteString(Capitalize(str)) } } - return res + return builder.String() } // Capitalize converts the first character of a string to upper case and the remaining to lower case. +// Play: https://go.dev/play/p/2OAjgbmAqHZ func Capitalize(s string) string { - if len(s) == 0 { - return "" + if s == "" { + return s } - out := make([]rune, len(s)) - for i, v := range s { - if i == 0 { - out[i] = unicode.ToUpper(v) - } else { - out[i] = unicode.ToLower(v) - } + runes := []rune(s) + runes[0] = unicode.ToUpper(runes[0]) + for i := 1; i < len(runes); i++ { + runes[i] = unicode.ToLower(runes[i]) } - return string(out) + return string(runes) } // UpperFirst converts the first character of string to upper case. +// Play: https://go.dev/play/p/sBbBxRbs8MM func UpperFirst(s string) string { if len(s) == 0 { return "" @@ -67,6 +64,7 @@ func UpperFirst(s string) string { } // LowerFirst converts the first character of string to lower case. +// Play: https://go.dev/play/p/CbzAyZmtJwL func LowerFirst(s string) string { if len(s) == 0 { return "" @@ -78,126 +76,114 @@ func LowerFirst(s string) string { return string(r) + s[size:] } -// PadEnd pads string on the right side if it's shorter than size. +// Pad pads string on the left and right side if it's shorter than size. // Padding characters are truncated if they exceed size. -func PadEnd(source string, size int, padStr string) string { - len1 := len(source) - len2 := len(padStr) - - if len1 >= size { - return source - } - - fill := "" - if len2 >= size-len1 { - fill = padStr[0 : size-len1] - } else { - fill = strings.Repeat(padStr, size-len1) - } - return source + fill[0:size-len1] +// Play: https://go.dev/play/p/NzImQq-VF8q +func Pad(source string, size int, padStr string) string { + return padAtPosition(source, size, padStr, 0) } // PadStart pads string on the left side if it's shorter than size. // Padding characters are truncated if they exceed size. +// Play: https://go.dev/play/p/xpTfzArDfvT func PadStart(source string, size int, padStr string) string { - len1 := len(source) - len2 := len(padStr) - - if len1 >= size { - return source - } + return padAtPosition(source, size, padStr, 1) +} - fill := "" - if len2 >= size-len1 { - fill = padStr[0 : size-len1] - } else { - fill = strings.Repeat(padStr, size-len1) - } - return fill[0:size-len1] + source +// PadEnd pads string on the right side if it's shorter than size. +// Padding characters are truncated if they exceed size. +// Play: https://go.dev/play/p/9xP8rN0vz-- +func PadEnd(source string, size int, padStr string) string { + return padAtPosition(source, size, padStr, 2) } -// KebabCase covert string to kebab-case +// KebabCase coverts string to kebab-case, non letters and numbers will be ignored. +// Play: https://go.dev/play/p/dcZM9Oahw-Y func KebabCase(s string) string { - if len(s) == 0 { - return "" - } - - regex := regexp.MustCompile(`[\W|_]+`) - blankSpace := " " - match := regex.ReplaceAllString(s, blankSpace) - rs := strings.Split(match, blankSpace) - - var res []string - for _, v := range rs { - splitWords := splitWordsToLower(v) - if len(splitWords) > 0 { - res = append(res, splitWords...) - } - } + result := splitIntoStrings(s, false) + return strings.Join(result, "-") +} - return strings.Join(res, "-") +// UpperKebabCase coverts string to upper KEBAB-CASE, non letters and numbers will be ignored +// Play: https://go.dev/play/p/zDyKNneyQXk +func UpperKebabCase(s string) string { + result := splitIntoStrings(s, true) + return strings.Join(result, "-") } -// SnakeCase covert string to snake_case +// SnakeCase coverts string to snake_case, non letters and numbers will be ignored +// Play: https://go.dev/play/p/tgzQG11qBuN func SnakeCase(s string) string { - if len(s) == 0 { - return "" - } - - regex := regexp.MustCompile(`[\W|_]+`) - blankSpace := " " - match := regex.ReplaceAllString(s, blankSpace) - rs := strings.Split(match, blankSpace) - - var res []string - for _, v := range rs { - splitWords := splitWordsToLower(v) - if len(splitWords) > 0 { - res = append(res, splitWords...) - } - } + result := splitIntoStrings(s, false) + return strings.Join(result, "_") +} - return strings.Join(res, "_") +// UpperSnakeCase coverts string to upper SNAKE_CASE, non letters and numbers will be ignored +// Play: https://go.dev/play/p/4COPHpnLx38 +func UpperSnakeCase(s string) string { + result := splitIntoStrings(s, true) + return strings.Join(result, "_") } -// Before create substring in source string before position when char first appear +// Before returns the substring of the source string up to the first occurrence of the specified string. +// Play: https://go.dev/play/p/JAWTZDS4F5w func Before(s, char string) string { - if s == "" || char == "" { + if char == "" { return s } - i := strings.Index(s, char) - return s[0:i] + + if i := strings.Index(s, char); i >= 0 { + return s[:i] + } + + return s } -// BeforeLast create substring in source string before position when char last appear +// BeforeLast returns the substring of the source string up to the last occurrence of the specified string. +// Play: https://go.dev/play/p/pJfXXAoG_Te func BeforeLast(s, char string) string { - if s == "" || char == "" { + if char == "" { return s } - i := strings.LastIndex(s, char) - return s[0:i] + + if i := strings.LastIndex(s, char); i >= 0 { + return s[:i] + } + + return s } -// After create substring in source string after position when char first appear +// After returns the substring after the first occurrence of a specified string in the source string. +// Play: https://go.dev/play/p/RbCOQqCDA7m func After(s, char string) string { - if s == "" || char == "" { + if char == "" { return s } - i := strings.Index(s, char) - return s[i+len(char):] + + if i := strings.Index(s, char); i >= 0 { + return s[i+len(char):] + } + + return s } -// AfterLast create substring in source string after position when char last appear +// AfterLast returns the substring after the last occurrence of a specified string in the source string. +// Play: https://go.dev/play/p/1TegARrb8Yn func AfterLast(s, char string) string { - if s == "" || char == "" { + if char == "" { return s } - i := strings.LastIndex(s, char) - return s[i+len(char):] + + if i := strings.LastIndex(s, char); i >= 0 { + return s[i+len(char):] + } + + return s } // IsString check if the value data type is string or not. -func IsString(v interface{}) bool { +// Play: https://go.dev/play/p/IOgq7oF9ERm +func IsString(v any) bool { if v == nil { return false } @@ -209,8 +195,9 @@ func IsString(v interface{}) bool { } } -// ReverseStr return string whose char order is reversed to the given string -func ReverseStr(s string) string { +// Reverse returns string whose char order is reversed to the given string. +// Play: https://go.dev/play/p/adfwalJiecD +func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] @@ -218,7 +205,8 @@ func ReverseStr(s string) string { return string(r) } -// Wrap a string with another string. +// Wrap a string with given string. +// Play: https://go.dev/play/p/KoZOlZDDt9y func Wrap(str string, wrapWith string) string { if str == "" || wrapWith == "" { return str @@ -231,20 +219,563 @@ func Wrap(str string, wrapWith string) string { return sb.String() } -// Unwrap a given string from anther string. will change str value +// Unwrap a given string from anther string. will change source string. +// Play: https://go.dev/play/p/Ec2q4BzCpG- func Unwrap(str string, wrapToken string) string { - if str == "" || wrapToken == "" { + if wrapToken == "" || !strings.HasPrefix(str, wrapToken) || !strings.HasSuffix(str, wrapToken) { + return str + } + + if len(str) < 2*len(wrapToken) { return str } - firstIndex := strings.Index(str, wrapToken) - lastIndex := strings.LastIndex(str, wrapToken) + return str[len(wrapToken) : len(str)-len(wrapToken)] +} + +// SplitEx split a given string which can control the result slice contains empty string or not. +// Play: https://go.dev/play/p/Us-ySSbWh-3 +func SplitEx(s, sep string, removeEmptyString bool) []string { + if sep == "" { + return []string{} + } - if firstIndex == 0 && lastIndex > 0 && lastIndex <= len(str)-1 { - if len(wrapToken) <= lastIndex { - str = str[len(wrapToken):lastIndex] + n := strings.Count(s, sep) + 1 + a := make([]string, n) + n-- + i := 0 + sepSave := 0 + ignore := false + + for i < n { + m := strings.Index(s, sep) + if m < 0 { + break + } + ignore = false + if removeEmptyString { + if s[:m+sepSave] == "" { + ignore = true + } + } + if !ignore { + a[i] = s[:m+sepSave] + s = s[m+len(sep):] + i++ + } else { + s = s[m+len(sep):] } } + var ret []string + if removeEmptyString { + if s != "" { + a[i] = s + ret = a[:i+1] + } else { + ret = a[:i] + } + } else { + a[i] = s + ret = a[:i+1] + } + + return ret +} + +// Substring returns a substring of the specified length starting at the specified offset position. +// Play: https://go.dev/play/p/q3sM6ehnPDp +func Substring(s string, offset int, length uint) string { + rs := []rune(s) + size := len(rs) + + if offset < 0 { + offset += size + } + if offset < 0 { + offset = 0 + } + if offset > size { + return "" + } + + end := offset + int(length) + if end > size { + end = size + } + + return strings.ReplaceAll(string(rs[offset:end]), "\x00", "") +} + +// SplitWords splits a string into words, word only contains alphabetic characters. +// Play: https://go.dev/play/p/KLiX4WiysMM +func SplitWords(s string) []string { + var word string + var words []string + var r rune + var size, pos int + + isWord := false + + for len(s) > 0 { + r, size = utf8.DecodeRuneInString(s) + + switch { + case isLetter(r): + if !isWord { + isWord = true + word = s + pos = 0 + } + + case isWord && (r == '\'' || r == '-'): + // is word + + default: + if isWord { + isWord = false + words = append(words, word[:pos]) + } + } + + pos += size + s = s[size:] + } + + if isWord { + words = append(words, word[:pos]) + } + + return words +} + +// WordCount return the number of meaningful word, word only contains alphabetic characters. +// Play: https://go.dev/play/p/bj7_odx3vRf +func WordCount(s string) int { + var r rune + var size, count int + + isWord := false + + for len(s) > 0 { + r, size = utf8.DecodeRuneInString(s) + + switch { + case isLetter(r): + if !isWord { + isWord = true + count++ + } + + case isWord && (r == '\'' || r == '-'): + // is word + + default: + isWord = false + } + + s = s[size:] + } + + return count +} + +// RemoveNonPrintable remove non-printable characters from a string. +// Play: https://go.dev/play/p/og47F5x_jTZ +func RemoveNonPrintable(str string) string { + result := strings.Map(func(r rune) rune { + if unicode.IsPrint(r) { + return r + } + return -1 + }, str) + + return result +} + +// StringToBytes converts a string to byte slice without a memory allocation. +// Play: https://go.dev/play/p/7OyFBrf9AxA +func StringToBytes(str string) (b []byte) { + return *(*[]byte)(unsafe.Pointer(&struct { + string + Cap int + }{str, len(str)})) +} + +// BytesToString converts a byte slice to string without a memory allocation. +// Play: https://go.dev/play/p/6c68HRvJecH +func BytesToString(bytes []byte) string { + return *(*string)(unsafe.Pointer(&bytes)) +} + +// IsBlank checks if a string is whitespace, empty. +// Play: https://go.dev/play/p/6zXRH_c0Qd3 +func IsBlank(str string) bool { + if len(str) == 0 { + return true + } + // memory copies will occur here, but UTF8 will be compatible + runes := []rune(str) + for _, r := range runes { + if !unicode.IsSpace(r) { + return false + } + } + return true +} + +// IsNotBlank checks if a string is not whitespace, not empty. +// Play: https://go.dev/play/p/e_oJW0RAquA +func IsNotBlank(str string) bool { + return !IsBlank(str) +} + +// HasPrefixAny check if a string starts with any of a slice of specified strings. +// Play: https://go.dev/play/p/8UUTl2C5slo +func HasPrefixAny(str string, prefixes []string) bool { + if len(str) == 0 || len(prefixes) == 0 { + return false + } + for _, prefix := range prefixes { + if strings.HasPrefix(str, prefix) { + return true + } + } + return false +} + +// HasSuffixAny check if a string ends with any of a slice of specified strings. +// Play: https://go.dev/play/p/sKWpCQdOVkx +func HasSuffixAny(str string, suffixes []string) bool { + if len(str) == 0 || len(suffixes) == 0 { + return false + } + for _, suffix := range suffixes { + if strings.HasSuffix(str, suffix) { + return true + } + } + return false +} + +// IndexOffset returns the index of the first instance of substr in string after offsetting the string by `idxFrom`, +// or -1 if substr is not present in string. +// Play: https://go.dev/play/p/qZo4lV2fomB +func IndexOffset(str string, substr string, idxFrom int) int { + if idxFrom > len(str)-1 || idxFrom < 0 { + return -1 + } + + return strings.Index(str[idxFrom:], substr) + idxFrom +} + +// ReplaceWithMap returns a copy of `str`, +// which is replaced by a map in unordered way, case-sensitively. +// Play: https://go.dev/play/p/h3t7CNj2Vvu +func ReplaceWithMap(str string, replaces map[string]string) string { + for k, v := range replaces { + str = strings.ReplaceAll(str, k, v) + } + return str } + +// SplitAndTrim splits string `str` by a string `delimiter` to a slice, +// and calls Trim to every element of this slice. It ignores the elements +// which are empty after Trim. +// Play: https://go.dev/play/p/ZNL6o4SkYQ7 +func SplitAndTrim(str, delimiter string, characterMask ...string) []string { + result := make([]string, 0) + + for _, v := range strings.Split(str, delimiter) { + v = Trim(v, characterMask...) + if v != "" { + result = append(result, v) + } + } + + return result +} + +var ( + // DefaultTrimChars are the characters which are stripped by Trim* functions in default. + DefaultTrimChars = string([]byte{ + '\t', // Tab. + '\v', // Vertical tab. + '\n', // New line (line feed). + '\r', // Carriage return. + '\f', // New page. + ' ', // Ordinary space. + 0x00, // NUL-byte. + 0x85, // Delete. + 0xA0, // Non-breaking space. + }) +) + +// Trim strips whitespace (or other characters) from the beginning and end of a string. +// The optional parameter `characterMask` specifies the additional stripped characters. +// Play: https://go.dev/play/p/Y0ilP0NRV3j +func Trim(str string, characterMask ...string) string { + trimChars := DefaultTrimChars + + if len(characterMask) > 0 { + trimChars += characterMask[0] + } + + return strings.Trim(str, trimChars) +} + +// HideString hide some chars in source string with param `replaceChar`. +// replace range is origin[start : end]. [start, end) +// Play: https://go.dev/play/p/pzbaIVCTreZ) +func HideString(origin string, start, end int, replaceChar string) string { + size := len(origin) + + if start > size-1 || start < 0 || end < 0 || start > end { + return origin + } + + if end > size { + end = size + } + + if replaceChar == "" { + return origin + } + + startStr := origin[0:start] + endStr := origin[end:size] + + replaceSize := end - start + replaceStr := strings.Repeat(replaceChar, replaceSize) + + return startStr + replaceStr + endStr +} + +// ContainsAll return true if target string contains all the substrs. +// Play: https://go.dev/play/p/KECtK2Os4zq +func ContainsAll(str string, substrs []string) bool { + for _, v := range substrs { + if !strings.Contains(str, v) { + return false + } + } + + return true +} + +// ContainsAny return true if target string contains any one of the substrs. +// Play: https://go.dev/play/p/dZGSSMB3LXE +func ContainsAny(str string, substrs []string) bool { + for _, v := range substrs { + if strings.Contains(str, v) { + return true + } + } + + return false +} + +var ( + whitespaceRegexMatcher *regexp.Regexp = regexp.MustCompile(`\s`) + mutiWhitespaceRegexMatcher *regexp.Regexp = regexp.MustCompile(`[[:space:]]{2,}|[\s\p{Zs}]{2,}`) +) + +// RemoveWhiteSpace remove whitespace characters from a string. +// when set repalceAll is true removes all whitespace, false only replaces consecutive whitespace characters with one space. +// Play: https://go.dev/play/p/HzLC9vsTwkf +func RemoveWhiteSpace(str string, repalceAll bool) string { + if repalceAll && str != "" { + return strings.Join(strings.Fields(str), "") + } else if str != "" { + str = mutiWhitespaceRegexMatcher.ReplaceAllString(str, " ") + str = whitespaceRegexMatcher.ReplaceAllString(str, " ") + } + + return strings.TrimSpace(str) +} + +// SubInBetween return substring between the start and end position(excluded) of source string. +// Play: https://go.dev/play/p/EDbaRvjeNsv +func SubInBetween(str string, start string, end string) string { + if _, after, ok := strings.Cut(str, start); ok { + if before, _, ok := strings.Cut(after, end); ok { + return before + } + } + + return "" +} + +// HammingDistance calculates the Hamming distance between two strings. +// The Hamming distance is the number of positions at which the corresponding symbols are different. +// This func returns an error if the input strings are of unequal lengths. +// Play: https://go.dev/play/p/glNdQEA9HUi +func HammingDistance(a, b string) (int, error) { + if len(a) != len(b) { + return -1, errors.New("a length and b length are unequal") + } + + ar := []rune(a) + br := []rune(b) + + var distance int + for i, codepoint := range ar { + if codepoint != br[i] { + distance++ + } + } + + return distance, nil +} + +// Concat uses the strings.Builder to concatenate the input strings. +// - `length` is the expected length of the concatenated string. +// - if you are unsure about the length of the string to be concatenated, please pass 0 or a negative number. +// +// Play: https://go.dev/play/p/gD52SZHr4Kp +func Concat(length int, str ...string) string { + if len(str) == 0 { + return "" + } + + sb := strings.Builder{} + if length <= 0 { + sb.Grow(len(str[0]) * len(str)) + } else { + sb.Grow(length) + } + + for _, s := range str { + sb.WriteString(s) + } + return sb.String() +} + +// Ellipsis truncates a string to a specified length and appends an ellipsis. +// Play: https://go.dev/play/p/i1vbdQiQVRR +func Ellipsis(str string, length int) string { + str = strings.TrimSpace(str) + + if length <= 0 { + return "" + } + + runes := []rune(str) + + if len(runes) <= length { + return str + } + + return string(runes[:length]) + "..." +} + +// Shuffle the order of characters of given string. +// Play: https://go.dev/play/p/iStFwBwyGY7 +func Shuffle(str string) string { + runes := []rune(str) + + for i := len(runes) - 1; i > 0; i-- { + j := rng.Intn(i + 1) + runes[i], runes[j] = runes[j], runes[i] + } + + return string(runes) +} + +// Rotate rotates the string by the specified number of characters. +// Play: https://go.dev/play/p/Kf03iOeT5bd +func Rotate(str string, shift int) string { + if shift == 0 { + return str + } + + runes := []rune(str) + length := len(runes) + if length == 0 { + return str + } + + shift = shift % length + + if shift < 0 { + shift = length + shift + } + + var sb strings.Builder + sb.Grow(length) + + sb.WriteString(string(runes[length-shift:])) + sb.WriteString(string(runes[:length-shift])) + + return sb.String() +} + +// TemplateReplace replaces the placeholders in the template string with the corresponding values in the data map. +// The placeholders are enclosed in curly braces, e.g. {key}. +// for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, +// the result will be "Hello, world!". +// Play: https://go.dev/play/p/cXSuFvyZqv9 +func TemplateReplace(template string, data map[string]string) string { + re := regexp.MustCompile(`\{(\w+)\}`) + + result := re.ReplaceAllStringFunc(template, func(s string) string { + key := strings.Trim(s, "{}") + if val, ok := data[key]; ok { + return val + } + + return s + }) + + result = strings.ReplaceAll(result, "{{", "{") + result = strings.ReplaceAll(result, "}}", "}") + + return result +} + +// RegexMatchAllGroups Matches all subgroups in a string using a regular expression and returns the result. +// Play: https://go.dev/play/p/JZiu0RXpgN- +func RegexMatchAllGroups(pattern, str string) [][]string { + re := regexp.MustCompile(pattern) + matches := re.FindAllStringSubmatch(str, -1) + return matches +} + +// ExtractContent extracts the content between the start and end strings in the source string. +// Play: https://go.dev/play/p/Ay9UIk7Rum9 +func ExtractContent(str, start, end string) []string { + result := []string{} + + for { + if _, after, ok := strings.Cut(str, start); ok { + if before, _, ok := strings.Cut(after, end); ok { + result = append(result, before) + str = after + } else { + break + } + } else { + break + } + } + + return result +} + +// FindAllOccurrences returns the positions of all occurrences of a substring in a string. +// Play: https://go.dev/play/p/uvyA6azGLB1 +func FindAllOccurrences(str, substr string) []int { + var positions []int + + for i := 0; i < len(str); { + index := strings.Index(str[i:], substr) + if index == -1 { + break + } + positions = append(positions, i+index) + i += index + 1 + } + + return positions +} diff --git a/strutil/string_example_test.go b/strutil/string_example_test.go new file mode 100644 index 00000000..a8bf402b --- /dev/null +++ b/strutil/string_example_test.go @@ -0,0 +1,775 @@ +package strutil + +import ( + "fmt" + "reflect" +) + +func ExampleAfter() { + result1 := After("foo", "") + result2 := After("foo", "foo") + result3 := After("foo/bar", "foo") + result4 := After("foo/bar", "/") + result5 := After("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // foo + // + // /bar + // bar + // bar/baz +} + +func ExampleAfterLast() { + result1 := AfterLast("foo", "") + result2 := AfterLast("foo", "foo") + result3 := AfterLast("foo/bar", "/") + result4 := AfterLast("foo/bar/baz", "/") + result5 := AfterLast("foo/bar/foo/baz", "foo") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // foo + // + // bar + // baz + // /baz +} + +func ExampleBefore() { + result1 := Before("foo", "") + result2 := Before("foo", "foo") + result3 := Before("foo/bar", "/") + result4 := Before("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + // Output: + // foo + // + // foo + // foo +} + +func ExampleBeforeLast() { + result1 := BeforeLast("foo", "") + result2 := BeforeLast("foo", "foo") + result3 := BeforeLast("foo/bar", "/") + result4 := BeforeLast("foo/bar/baz", "/") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + // Output: + // foo + // + // foo + // foo/bar +} + +func ExampleCamelCase() { + strings := []string{"", "foobar", "&FOO:BAR$BAZ", "$foo%", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := CamelCase(v) + fmt.Println(s) + } + // Output: + // + // foobar + // fooBarBaz + // foo + // foo11Bar +} + +func ExampleCapitalize() { + strings := []string{"", "Foo", "_foo", "fooBar", "foo-bar"} + + for _, v := range strings { + s := Capitalize(v) + fmt.Println(s) + } + // Output: + // + // Foo + // _foo + // Foobar + // Foo-bar +} + +func ExampleIsString() { + result1 := IsString("") + result2 := IsString("a") + result3 := IsString(1) + result4 := IsString(true) + result5 := IsString([]string{"a"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // true + // true + // false + // false + // false +} + +func ExampleKebabCase() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := KebabCase(v) + fmt.Println(s) + } + // Output: + // + // foo-bar + // foo-bar + // foobar + // foo-1-1-bar +} + +func ExampleUpperKebabCase() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := UpperKebabCase(v) + fmt.Println(s) + } + // Output: + // + // FOO-BAR + // FOO-BAR + // FOO-BAR + // FOO-1-1-BAR +} + +func ExampleLowerFirst() { + strings := []string{"", "bar", "BAr", "Bar大"} + + for _, v := range strings { + s := LowerFirst(v) + fmt.Println(s) + } + // Output: + // + // bar + // bAr + // bar大 +} + +func ExampleUpperFirst() { + strings := []string{"", "bar", "BAr", "bar大"} + + for _, v := range strings { + s := UpperFirst(v) + fmt.Println(s) + } + // Output: + // + // Bar + // BAr + // Bar大 +} + +func ExamplePad() { + result1 := Pad("foo", 1, "bar") + result2 := Pad("foo", 2, "bar") + result3 := Pad("foo", 3, "bar") + result4 := Pad("foo", 4, "bar") + result5 := Pad("foo", 5, "bar") + result6 := Pad("foo", 6, "bar") + result7 := Pad("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + + // Output: + // foo + // foo + // foo + // foob + // bfoob + // bfooba + // bafooba +} + +func ExamplePadEnd() { + result1 := PadEnd("foo", 1, "bar") + result2 := PadEnd("foo", 2, "bar") + result3 := PadEnd("foo", 3, "bar") + result4 := PadEnd("foo", 4, "bar") + result5 := PadEnd("foo", 5, "bar") + result6 := PadEnd("foo", 6, "bar") + result7 := PadEnd("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + // Output: + // foo + // foo + // foo + // foob + // fooba + // foobar + // foobarb +} + +func ExamplePadStart() { + result1 := PadStart("foo", 1, "bar") + result2 := PadStart("foo", 2, "bar") + result3 := PadStart("foo", 3, "bar") + result4 := PadStart("foo", 4, "bar") + result5 := PadStart("foo", 5, "bar") + result6 := PadStart("foo", 6, "bar") + result7 := PadStart("foo", 7, "bar") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + fmt.Println(result7) + // Output: + // foo + // foo + // foo + // bfoo + // bafoo + // barfoo + // barbfoo +} + +func ExampleReverse() { + s := "foo" + rs := Reverse(s) + + fmt.Println(s) + fmt.Println(rs) + // Output: + // foo + // oof +} + +func ExampleSnakeCase() { + strings := []string{"", "foo-bar", "Foo Bar-", "FOOBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := SnakeCase(v) + fmt.Println(s) + } + // Output: + // + // foo_bar + // foo_bar + // foobar + // foo_1_1_bar +} + +func ExampleUpperSnakeCase() { + strings := []string{"", "foo-bar", "Foo Bar-", "FooBAR", "Foo-#1😄$_%^&*(1bar"} + + for _, v := range strings { + s := UpperSnakeCase(v) + fmt.Println(s) + } + // Output: + // + // FOO_BAR + // FOO_BAR + // FOO_BAR + // FOO_1_1_BAR +} + +func ExampleSplitEx() { + result1 := SplitEx(" a b c ", "", true) + + result2 := SplitEx(" a b c ", " ", false) + result3 := SplitEx(" a b c ", " ", true) + + result4 := SplitEx("a = b = c = ", " = ", false) + result5 := SplitEx("a = b = c = ", " = ", true) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // [] + // [ a b c ] + // [a b c] + // [a b c ] + // [a b c] +} + +func ExampleWrap() { + result1 := Wrap("foo", "") + result2 := Wrap("foo", "*") + result3 := Wrap("'foo'", "'") + result4 := Wrap("", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + // Output: + // foo + // *foo* + // ''foo'' + // +} + +func ExampleUnwrap() { + result1 := Unwrap("foo", "") + result2 := Unwrap("*foo*", "*") + result3 := Unwrap("*foo", "*") + result4 := Unwrap("foo*", "*") + result5 := Unwrap("**foo**", "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // foo + // foo + // *foo + // foo* + // *foo* +} + +func ExampleSubstring() { + + result1 := Substring("abcde", 1, 3) + result2 := Substring("abcde", 1, 5) + result3 := Substring("abcde", -1, 3) + result4 := Substring("abcde", -2, 2) + result5 := Substring("abcde", -2, 3) + result6 := Substring("你好,欢迎你", 0, 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + fmt.Println(result6) + // Output: + // bcd + // bcde + // e + // de + // de + // 你好 +} + +func ExampleSplitWords() { + + result1 := SplitWords("a word") + result2 := SplitWords("I'am a programmer") + result3 := SplitWords("a -b-c' 'd'e") + result4 := SplitWords("你好,我是一名码农") + result5 := SplitWords("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // [a word] + // [I'am a programmer] + // [a b-c' d'e] + // [] + // [] +} + +func ExampleWordCount() { + + result1 := WordCount("a word") + result2 := WordCount("I'am a programmer") + result3 := WordCount("a -b-c' 'd'e") + result4 := WordCount("你好,我是一名码农") + result5 := WordCount("こんにちは,私はプログラマーです") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // 2 + // 3 + // 3 + // 0 + // 0 +} + +func ExampleRemoveNonPrintable() { + result1 := RemoveNonPrintable("hello\u00a0 \u200bworld\n") + result2 := RemoveNonPrintable("你好😄") + + fmt.Println(result1) + fmt.Println(result2) + // Output: + // hello world + // 你好😄 +} + +func ExampleStringToBytes() { + result1 := StringToBytes("abc") + result2 := reflect.DeepEqual(result1, []byte{'a', 'b', 'c'}) + + fmt.Println(result1) + fmt.Println(result2) + // Output: + // [97 98 99] + // true +} + +func ExampleBytesToString() { + bytes := []byte{'a', 'b', 'c'} + result := BytesToString(bytes) + + fmt.Println(result) + // Output: + // abc +} + +func ExampleIsBlank() { + result1 := IsBlank("") + result2 := IsBlank("\t\v\f\n") + result3 := IsBlank(" 中文") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + // Output: + // true + // true + // false +} + +func ExampleIsNotBlank() { + result1 := IsNotBlank("") + result2 := IsNotBlank(" ") + result3 := IsNotBlank("\t\v\f\n") + result4 := IsNotBlank(" 中文") + result5 := IsNotBlank(" world ") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // false + // false + // false + // true + // true +} + +func ExampleHasPrefixAny() { + result1 := HasPrefixAny("foo bar", []string{"fo", "xyz", "hello"}) + result2 := HasPrefixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + // Output: + // true + // false +} + +func ExampleHasSuffixAny() { + result1 := HasSuffixAny("foo bar", []string{"bar", "xyz", "hello"}) + result2 := HasSuffixAny("foo bar", []string{"oom", "world"}) + + fmt.Println(result1) + fmt.Println(result2) + // Output: + // true + // false +} + +func ExampleIndexOffset() { + str := "foo bar hello world" + + result1 := IndexOffset(str, "o", 5) + result2 := IndexOffset(str, "o", 0) + result3 := IndexOffset(str, "d", len(str)-1) + result4 := IndexOffset(str, "d", len(str)) + result5 := IndexOffset(str, "f", -1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + // Output: + // 12 + // 1 + // 18 + // -1 + // -1 +} + +func ExampleReplaceWithMap() { + str := "ac ab ab ac" + replaces := map[string]string{ + "a": "1", + "b": "2", + } + + result := ReplaceWithMap(str, replaces) + + fmt.Println(result) + // Output: + // 1c 12 12 1c +} + +func ExampleTrim() { + result1 := Trim("\nabcd") + + str := "$ ab cd $ " + + result2 := Trim(str) + result3 := Trim(str, "$") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // abcd + // $ ab cd $ + // ab cd +} + +func ExampleSplitAndTrim() { + str := " a,b, c,d,$1 " + + result1 := SplitAndTrim(str, ",") + result2 := SplitAndTrim(str, ",", "$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // [a b c d $1] + // [a b c d 1] +} + +func ExampleHideString() { + str := "13242658976" + + result1 := HideString(str, 3, 3, "*") + result2 := HideString(str, 3, 4, "*") + result3 := HideString(str, 3, 7, "*") + result4 := HideString(str, 7, 11, "*") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // 13242658976 + // 132*2658976 + // 132****8976 + // 1324265**** +} + +func ExampleContainsAll() { + str := "hello world" + + result1 := ContainsAll(str, []string{"hello", "world"}) + result2 := ContainsAll(str, []string{"hello", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleContainsAny() { + str := "hello world" + + result1 := ContainsAny(str, []string{"hello", "world"}) + result2 := ContainsAny(str, []string{"hello", "abc"}) + result3 := ContainsAny(str, []string{"123", "abc"}) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleRemoveWhiteSpace() { + str := " hello \r\n \t world" + + result1 := RemoveWhiteSpace(str, true) + result2 := RemoveWhiteSpace(str, false) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // helloworld + // hello world +} + +func ExampleSubInBetween() { + str := "abcde" + + result1 := SubInBetween(str, "", "de") + result2 := SubInBetween(str, "a", "d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // abc + // bc +} + +func ExampleHammingDistance() { + + result, _ := HammingDistance("abc", "def") + fmt.Println(result) + + result, _ = HammingDistance("name", "namf") + fmt.Println(result) + + // Output: + // 3 + // 1 +} + +func ExampleConcat() { + result1 := Concat(12, "Hello", " ", "World", "!") + result2 := Concat(11, "Go", " ", "Language") + result3 := Concat(0, "An apple a ", "day,", "keeps the", " doctor away") + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello World! + // Go Language + // An apple a day,keeps the doctor away +} + +func ExampleEllipsis() { + result1 := Ellipsis("hello world", 5) + result2 := Ellipsis("你好,世界!", 2) + result3 := Ellipsis("😀😃😄😁😆", 3) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // hello... + // 你好... + // 😀😃😄... +} + +func ExampleRotate() { + result1 := Rotate("Hello", 0) + result2 := Rotate("Hello", 1) + result3 := Rotate("Hello", 2) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // Hello + // oHell + // loHel +} + +func ExampleTemplateReplace() { + template := `Hello, my name is {name}, I'm {age} years old.` + data := map[string]string{ + "name": "Bob", + "age": "20", + } + + result := TemplateReplace(template, data) + + fmt.Println(result) + + // Output: + // Hello, my name is Bob, I'm 20 years old. +} + +func ExampleRegexMatchAllGroups() { + pattern := `(\w+\.+\w+)@(\w+)\.(\w+)` + str := "Emails: john.doe@example.com and jane.doe@example.com" + + result := RegexMatchAllGroups(pattern, str) + + fmt.Println(result[0]) + fmt.Println(result[1]) + + // Output: + // [john.doe@example.com john.doe example com] + // [jane.doe@example.com jane.doe example com] +} + +func ExampleExtractContent() { + html := `content1aacontent2bbcontent1` + + result := ExtractContent(html, "", "") + + fmt.Println(result) + + // Output: + // [content1 content2 content1] +} + +func ExampleFindAllOccurrences() { + result := FindAllOccurrences("ababab", "ab") + + fmt.Println(result) + + // Output: + // [0 2 4] +} diff --git a/strutil/string_internal.go b/strutil/string_internal.go index 6776d432..dd5578d0 100644 --- a/strutil/string_internal.go +++ b/strutil/string_internal.go @@ -1,40 +1,169 @@ package strutil -import "strings" +import ( + "strings" + "unicode" +) -// splitWordsToLower split a string into worlds by uppercase char -func splitWordsToLower(s string) []string { - var res []string +func splitIntoStrings(s string, upperCase bool) []string { + var runes [][]rune + lastCharType := 0 + charType := 0 - upperIndexes := upperIndex(s) - l := len(upperIndexes) - if upperIndexes == nil || l == 0 { - if s != "" { - res = append(res, s) + // split into fields based on type of unicode character + for _, r := range s { + switch true { + case isLower(r): + charType = 1 + case isUpper(r): + charType = 2 + case isDigit(r): + charType = 3 + default: + charType = 4 } - return res - } - for i := 0; i < l; i++ { - if i < l-1 { - res = append(res, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]])) + + if charType == lastCharType { + runes[len(runes)-1] = append(runes[len(runes)-1], r) } else { - res = append(res, strings.ToLower(s[upperIndexes[i]:])) + runes = append(runes, []rune{r}) + } + lastCharType = charType + } + + for i := 0; i < len(runes)-1; i++ { + if isUpper(runes[i][0]) && isLower(runes[i+1][0]) { + length := len(runes[i]) - 1 + temp := runes[i][length] + runes[i+1] = append([]rune{temp}, runes[i+1]...) + runes[i] = runes[i][:length] } } - return res -} -// upperIndex get a int slice which elements are all the uppercase char index of a string -func upperIndex(s string) []int { - var res []int - for i := 0; i < len(s); i++ { - if 64 < s[i] && s[i] < 91 { - res = append(res, i) + // filter all none letters and none digit + var result []string + for _, rs := range runes { + if len(rs) > 0 && (unicode.IsLetter(rs[0]) || isDigit(rs[0])) { + if upperCase { + result = append(result, string(toUpperAll(rs))) + } else { + result = append(result, string(toLowerAll(rs))) + } } } - if len(s) > 0 && res != nil && res[0] != 0 { - res = append([]int{0}, res...) + + return result +} + +// isDigit checks if a character is digit ('0' to '9') +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +// isLower checks if a character is lower case ('a' to 'z') +func isLower(r rune) bool { + return r >= 'a' && r <= 'z' +} + +// isUpper checks if a character is upper case ('A' to 'Z') +func isUpper(r rune) bool { + return r >= 'A' && r <= 'Z' +} + +// toLower converts a character 'A' to 'Z' to its lower case +func toLower(r rune) rune { + if r >= 'A' && r <= 'Z' { + return r + 32 + } + return r +} + +// toLowerAll converts a character 'A' to 'Z' to its lower case +func toLowerAll(rs []rune) []rune { + for i := range rs { + rs[i] = toLower(rs[i]) + } + return rs +} + +// toUpper converts a character 'a' to 'z' to its upper case +func toUpper(r rune) rune { + if r >= 'a' && r <= 'z' { + return r - 32 + } + return r +} + +// toUpperAll converts a character 'a' to 'z' to its upper case +func toUpperAll(rs []rune) []rune { + for i := range rs { + rs[i] = toUpper(rs[i]) + } + return rs +} + +// padWithPosition pads string +func padAtPosition(str string, length int, padStr string, position int) string { + if len(str) >= length { + return str + } + + if padStr == "" { + padStr = " " + } + + totalPad := length - len(str) + startPad := 0 + + if position == 0 { + startPad = totalPad / 2 + } else if position == 1 { + startPad = totalPad + } else if position == 2 { + startPad = 0 + } + endPad := totalPad - startPad + + repeatPad := func(n int) string { + repeated := strings.Repeat(padStr, (n+len(padStr)-1)/len(padStr)) + return repeated[:n] + } + + left := repeatPad(startPad) + right := repeatPad(endPad) + + return left + str + right +} + +// isLetter checks r is a letter but not CJK character. +func isLetter(r rune) bool { + if !unicode.IsLetter(r) { + return false + } + + switch { + // cjk char: /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/ + + // hiragana and katakana (Japanese only) + case r >= '\u3034' && r < '\u30ff': + return false + + // CJK unified ideographs extension A (Chinese, Japanese, and Korean) + case r >= '\u3400' && r < '\u4dbf': + return false + + // CJK unified ideographs (Chinese, Japanese, and Korean) + case r >= '\u4e00' && r < '\u9fff': + return false + + // CJK compatibility ideographs (Chinese, Japanese, and Korean) + case r >= '\uf900' && r < '\ufaff': + return false + + // half-width katakana (Japanese only) + case r >= '\uff66' && r < '\uff9f': + return false } - return res + return true } diff --git a/strutil/string_test.go b/strutil/string_test.go index 16432b70..b47443bd 100644 --- a/strutil/string_test.go +++ b/strutil/string_test.go @@ -3,164 +3,426 @@ package strutil import ( "testing" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestCamelCase(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCamelCase") - assert.Equal("fooBar", CamelCase("foo_bar")) - assert.Equal("fooBar", CamelCase("Foo-Bar")) - assert.Equal("fooBar", CamelCase("Foo&bar")) - assert.Equal("fooBar", CamelCase("foo bar")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foobar", "foobar"}, + {"&FOO:BAR$BAZ", "fooBarBaz"}, + {"fooBar", "fooBar"}, + {"FOObar", "foObar"}, + {"$foo%", "foo"}, + {" $#$Foo 22 bar ", "foo22Bar"}, + {"Foo-#1😄$_%^&*(1bar", "foo11Bar"}, + } - assert.NotEqual("FooBar", CamelCase("foo_bar")) + for _, tt := range tests { + assert.Equal(tt.expected, CamelCase(tt.input)) + } } func TestCapitalize(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestCapitalize") - assert.Equal("Foo", Capitalize("foo")) - assert.Equal("Foo", Capitalize("Foo")) - assert.Equal("Foo", Capitalize("Foo")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"Foo", "Foo"}, + {"_foo", "_foo"}, + {"foobar", "Foobar"}, + {"fooBar", "Foobar"}, + {"foo Bar", "Foo bar"}, + {"foo-bar", "Foo-bar"}, + {"$foo%", "$foo%"}, + } - assert.NotEqual("foo", Capitalize("Foo")) + for _, tt := range tests { + assert.Equal(tt.expected, Capitalize(tt.input)) + } } func TestKebabCase(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestKebabCase") - assert.Equal("foo-bar", KebabCase("Foo Bar-")) - assert.Equal("foo-bar", KebabCase("foo_Bar")) - assert.Equal("foo-bar", KebabCase("fooBar")) - assert.Equal("f-o-o-b-a-r", KebabCase("__FOO_BAR__")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo-bar", "foo-bar"}, + {"--Foo---Bar-", "foo-bar"}, + {"Foo Bar-", "foo-bar"}, + {"foo_Bar", "foo-bar"}, + {"fooBar", "foo-bar"}, + {"FOOBAR", "foobar"}, + {"FOO_BAR", "foo-bar"}, + {"__FOO_BAR__", "foo-bar"}, + {"$foo@Bar", "foo-bar"}, + {" $#$Foo 22 bar ", "foo-22-bar"}, + {"Foo-#1😄$_%^&*(1bar", "foo-1-1-bar"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, KebabCase(tt.input)) + } +} + +func TestUpperKebabCase(t *testing.T) { + t.Parallel() - assert.NotEqual("foo_bar", KebabCase("fooBar")) + assert := internal.NewAssert(t, "TestUpperKebabCase") + + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo-bar", "FOO-BAR"}, + {"--Foo---Bar-", "FOO-BAR"}, + {"Foo Bar-", "FOO-BAR"}, + {"foo_Bar", "FOO-BAR"}, + {"fooBar", "FOO-BAR"}, + {"FOOBAR", "FOOBAR"}, + {"FOO_BAR", "FOO-BAR"}, + {"__FOO_BAR__", "FOO-BAR"}, + {"$foo@Bar", "FOO-BAR"}, + {" $#$Foo 22 bar ", "FOO-22-BAR"}, + {"Foo-#1😄$_%^&*(1bar", "FOO-1-1-BAR"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, UpperKebabCase(tt.input)) + } } func TestSnakeCase(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSnakeCase") - assert.Equal("foo_bar", SnakeCase("Foo Bar-")) - assert.Equal("foo_bar", SnakeCase("foo_Bar")) - assert.Equal("foo_bar", SnakeCase("fooBar")) - assert.Equal("f_o_o_b_a_r", SnakeCase("__FOO_BAR__")) - assert.Equal("a_bbc_s_a_b_b_c", SnakeCase("aBbc-s$@a&%_B.B^C")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo-bar", "foo_bar"}, + {"--Foo---Bar-", "foo_bar"}, + {"Foo Bar-", "foo_bar"}, + {"foo_Bar", "foo_bar"}, + {"fooBar", "foo_bar"}, + {"FOOBAR", "foobar"}, + {"FOO_BAR", "foo_bar"}, + {"__FOO_BAR__", "foo_bar"}, + {"$foo@Bar", "foo_bar"}, + {" $#$Foo 22 bar ", "foo_22_bar"}, + {"Foo-#1😄$_%^&*(1bar", "foo_1_1_bar"}, + } - assert.NotEqual("foo-bar", SnakeCase("foo_Bar")) + for _, tt := range tests { + assert.Equal(tt.expected, SnakeCase(tt.input)) + } +} + +func TestUpperSnakeCase(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestUpperSnakeCase") + + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo-bar", "FOO_BAR"}, + {"--Foo---Bar-", "FOO_BAR"}, + {"Foo Bar-", "FOO_BAR"}, + {"foo_Bar", "FOO_BAR"}, + {"fooBar", "FOO_BAR"}, + {"FOOBAR", "FOOBAR"}, + {"FOO_BAR", "FOO_BAR"}, + {"__FOO_BAR__", "FOO_BAR"}, + {"$foo@Bar", "FOO_BAR"}, + {" $#$Foo 22 bar ", "FOO_22_BAR"}, + {"Foo-#1😄$_%^&*(1bar", "FOO_1_1_BAR"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, UpperSnakeCase(tt.input)) + } } func TestUpperFirst(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestLowerFirst") - assert.Equal("Foo", UpperFirst("foo")) - assert.Equal("BAR", UpperFirst("bAR")) - assert.Equal("FOo", UpperFirst("FOo")) - assert.Equal("FOo大", UpperFirst("fOo大")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo", "Foo"}, + {"bAR", "BAR"}, + {"FOo", "FOo"}, + {"fOo大", "FOo大"}, + } - assert.NotEqual("Bar", UpperFirst("BAR")) + for _, tt := range tests { + assert.Equal(tt.expected, UpperFirst(tt.input)) + } } func TestLowerFirst(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestLowerFirst") - assert.Equal("foo", LowerFirst("foo")) - assert.Equal("bAR", LowerFirst("BAR")) - assert.Equal("fOo", LowerFirst("FOo")) - assert.Equal("fOo大", LowerFirst("FOo大")) + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"foo", "foo"}, + {"bAR", "bAR"}, + {"FOo", "fOo"}, + {"fOo大", "fOo大"}, + } - assert.NotEqual("Bar", LowerFirst("BAR")) + for _, tt := range tests { + assert.Equal(tt.expected, LowerFirst(tt.input)) + } +} + +func TestPad(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestPad") + + tests := []struct { + input string + padSize int + padChar string + expected string + }{ + {"", 0, "", ""}, + {"a", 0, "", "a"}, + {"a", 1, "", "a"}, + {"a", 2, "", "a "}, + {"a", 1, "b", "a"}, + {"a", 2, "b", "ab"}, + {"abcd", 6, "m", "mabcdm"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Pad(tt.input, tt.padSize, tt.padChar)) + } } func TestPadEnd(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestPadEnd") - assert.Equal("a", PadEnd("a", 1, "b")) - assert.Equal("ab", PadEnd("a", 2, "b")) - assert.Equal("abcdmn", PadEnd("abcd", 6, "mno")) - assert.Equal("abcdmm", PadEnd("abcd", 6, "m")) - assert.Equal("abcaba", PadEnd("abc", 6, "ab")) + tests := []struct { + input string + padSize int + padChar string + expected string + }{ + {"a", 2, " ", "a "}, + {"a", 1, "b", "a"}, + {"a", 2, "b", "ab"}, + {"abcd", 6, "mno", "abcdmn"}, + {"abcd", 6, "m", "abcdmm"}, + {"abcd", 6, "ab", "abcdab"}, + } - assert.NotEqual("ba", PadEnd("a", 2, "b")) + for _, tt := range tests { + assert.Equal(tt.expected, PadEnd(tt.input, tt.padSize, tt.padChar)) + } } func TestPadStart(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestPadStart") - assert.Equal("a", PadStart("a", 1, "b")) - assert.Equal("ba", PadStart("a", 2, "b")) - assert.Equal("mnabcd", PadStart("abcd", 6, "mno")) - assert.Equal("mmabcd", PadStart("abcd", 6, "m")) - assert.Equal("abaabc", PadStart("abc", 6, "ab")) + tests := []struct { + input string + padSize int + padChar string + expected string + }{ + {"a", 1, "b", "a"}, + {"a", 2, "b", "ba"}, + {"abcd", 6, "mno", "mnabcd"}, + {"abcd", 6, "m", "mmabcd"}, + {"abc", 6, "ab", "abaabc"}, + } - assert.NotEqual("ab", PadStart("a", 2, "b")) + for _, tt := range tests { + assert.Equal(tt.expected, PadStart(tt.input, tt.padSize, tt.padChar)) + } } func TestBefore(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBefore") - assert.Equal("lancet", Before("lancet", "")) - assert.Equal("github.com", Before("github.com/test/lancet", "/")) - assert.Equal("github.com/", Before("github.com/test/lancet", "test")) + tests := []struct { + input string + char string + expected string + }{ + {"lancet", "", "lancet"}, + {"lancet", "lancet", ""}, + {"lancet", "abcdef", "lancet"}, + {"github.com/test/lancet", "/", "github.com"}, + {"github.com/test/lancet", "test", "github.com/"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Before(tt.input, tt.char)) + } } func TestBeforeLast(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestBeforeLast") - assert.Equal("lancet", BeforeLast("lancet", "")) - assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/")) - assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test")) + tests := []struct { + input string + char string + expected string + }{ + {"lancet", "", "lancet"}, + {"lancet", "lancet", ""}, + {"lancet", "abcdef", "lancet"}, + {"github.com/test/lancet", "/", "github.com/test"}, + {"github.com/test/test/lancet", "test", "github.com/test/"}, + } - assert.NotEqual("github.com/", BeforeLast("github.com/test/test/lancet", "test")) + for _, tt := range tests { + assert.Equal(tt.expected, BeforeLast(tt.input, tt.char)) + } } func TestAfter(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestAfter") - assert.Equal("lancet", After("lancet", "")) - assert.Equal("test/lancet", After("github.com/test/lancet", "/")) - assert.Equal("/lancet", After("github.com/test/lancet", "test")) + tests := []struct { + input string + char string + expected string + }{ + {"lancet", "", "lancet"}, + {"lancet", "lancet", ""}, + {"lancet", "abcdef", "lancet"}, + {"github.com/test/lancet", "/", "test/lancet"}, + {"github.com/test/lancet", "test", "/lancet"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, After(tt.input, tt.char)) + } } func TestAfterLast(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestAfterLast") - assert.Equal("lancet", AfterLast("lancet", "")) - assert.Equal("lancet", AfterLast("github.com/test/lancet", "/")) - assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test")) - assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test")) + tests := []struct { + input string + char string + expected string + }{ + {"lancet", "", "lancet"}, + {"lancet", "lancet", ""}, + {"lancet", "abcdef", "lancet"}, + {"github.com/test/lancet", "/", "lancet"}, + {"github.com/test/test/lancet", "test", "/lancet"}, + } - assert.NotEqual("/test/lancet", AfterLast("github.com/test/test/lancet", "test")) + for _, tt := range tests { + assert.Equal(tt.expected, AfterLast(tt.input, tt.char)) + } } func TestIsString(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsString") - assert.Equal(true, IsString("lancet")) - assert.Equal(true, IsString("")) - assert.Equal(false, IsString(1)) - assert.Equal(false, IsString(true)) - assert.Equal(false, IsString([]string{})) + tests := []struct { + input interface{} + expected bool + }{ + {"lancet", true}, + {"", true}, + {1, false}, + {true, false}, + {[]string{}, false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsString(tt.input)) + } } -func TestReverseStr(t *testing.T) { - assert := internal.NewAssert(t, "TestReverseStr") +func TestReverse(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReverse") - assert.Equal("cba", ReverseStr("abc")) - assert.Equal("54321", ReverseStr("12345")) + assert.Equal("cba", Reverse("abc")) + assert.Equal("54321", Reverse("12345")) } func TestWrap(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestWrap") - assert.Equal("ab", Wrap("ab", "")) - assert.Equal("", Wrap("", "*")) - assert.Equal("*ab*", Wrap("ab", "*")) - assert.Equal("\"ab\"", Wrap("ab", "\"")) - assert.Equal("'ab'", Wrap("ab", "'")) + tests := []struct { + input string + wrapper string + expected string + }{ + {"", "", ""}, + {"ab", "", "ab"}, + {"ab", "*", "*ab*"}, + {"ab", "\"", "\"ab\""}, + {"ab", "'", "'ab'"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Wrap(tt.input, tt.wrapper)) + } } func TestUnwrap(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestUnwrap") assert.Equal("", Unwrap("", "*")) @@ -177,3 +439,647 @@ func TestUnwrap(t *testing.T) { assert.Equal("***", Unwrap("***", "**")) assert.Equal("**", Unwrap("**", "**")) } + +func TestSplitEx(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSplitEx") + + assert.Equal([]string{}, SplitEx(" a b c ", "", true)) + + assert.Equal([]string{"", "a", "b", "c", ""}, SplitEx(" a b c ", " ", false)) + assert.Equal([]string{"a", "b", "c"}, SplitEx(" a b c ", " ", true)) + + assert.Equal([]string{"a", "b", "c", ""}, SplitEx("a = b = c = ", " = ", false)) + assert.Equal([]string{"a", "b", "c"}, SplitEx("a = b = c = ", " = ", true)) +} + +func TestSubstring(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSubstring") + + assert.Equal("bcd", Substring("abcde", 1, 3)) + assert.Equal("bcde", Substring("abcde", 1, 5)) + assert.Equal("e", Substring("abcde", -1, 3)) + assert.Equal("de", Substring("abcde", -2, 2)) + assert.Equal("de", Substring("abcde", -2, 3)) + assert.Equal("你好", Substring("你好,欢迎你", 0, 2)) +} + +func TestSplitWords(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSplitWords") + + cases := map[string][]string{ + "a word": {"a", "word"}, + "I'am a programmer": {"I'am", "a", "programmer"}, + "a -b-c' 'd'e": {"a", "b-c'", "d'e"}, + "你好,我是一名码农": nil, + "こんにちは,私はプログラマーです": nil, + } + + for k, v := range cases { + assert.Equal(v, SplitWords(k)) + } +} + +func TestWordCount(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestSplitWords") + + cases := map[string]int{ + "a word": 2, // {"a", "word"}, + "I'am a programmer": 3, // {"I'am", "a", "programmer"}, + "a -b-c' 'd'e": 3, // {"a", "b-c'", "d'e"}, + "你好,我是一名码农": 0, // nil, + "こんにちは,私はプログラマーです": 0, // nil, + } + + for k, v := range cases { + assert.Equal(v, WordCount(k)) + } +} + +func TestRemoveNonPrintable(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRemoveNonPrintable") + + assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n")) + assert.Equal("你好😄", RemoveNonPrintable("你好😄")) +} + +func TestStringToBytes(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStringToBytes") + + bytes := StringToBytes("abc") + assert.Equal([]byte{'a', 'b', 'c'}, bytes) + assert.Equal(3, cap(bytes)) +} + +func TestBytesToString(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestBytesToString") + + str := BytesToString([]byte{'a', 'b', 'c'}) + assert.Equal("abc", str) +} + +func TestIsBlank(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsBlank") + + tests := []struct { + input string + expected bool + }{ + {"", true}, + {" ", true}, + {"\t\v\f\n", true}, + {"\t\v\f\nabc", false}, + {"abc", false}, + {" 中文", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsBlank(tt.input)) + } +} + +func TestIsNotBlank(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsBlank") + + tests := []struct { + input string + expected bool + }{ + {"", false}, + {" ", false}, + {"\t\v\f\n", false}, + {"\t\v\f\nabc", true}, + {"abc", true}, + {" 中文", true}, + {" world ", true}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsNotBlank(tt.input)) + } +} + +func TestHasPrefixAny(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHasPrefixAny") + + tests := []struct { + str string + prefixes []string + expected bool + }{ + {"foo bar", []string{"fo", "xyz", "hello"}, true}, + {"foo bar", []string{"oom", "world"}, false}, + {"foo bar", []string{}, false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, HasPrefixAny(tt.str, tt.prefixes)) + } +} + +func TestHasSuffixAny(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestHasSuffixAny") + + tests := []struct { + str string + suffixes []string + expected bool + }{ + {"foo bar", []string{"bar", "xyz", "hello"}, true}, + {"foo bar", []string{"oom", "world"}, false}, + {"foo bar", []string{}, false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, HasSuffixAny(tt.str, tt.suffixes)) + } +} + +func TestIndexOffset(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIndexOffset") + + str := "foo bar hello world" + + tests := []struct { + str string + substr string + offset int + expected int + }{ + {str, "o", 5, 12}, + {str, "o", 0, 1}, + {str, "d", len(str) - 1, len(str) - 1}, + {str, "d", len(str), -1}, + {str, "f", -1, -1}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IndexOffset(tt.str, tt.substr, tt.offset)) + } +} + +func TestReplaceWithMap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestReplaceWithMap") + + str := "ac ab ab ac" + replaces := map[string]string{ + "a": "1", + "b": "2", + } + + assert.Equal(str, "ac ab ab ac") + assert.Equal(ReplaceWithMap(str, replaces), "1c 12 12 1c") +} + +func TestTrim(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTrim") + + str1 := "$ ab cd $ " + + assert.Equal("$ ab cd $", Trim(str1)) + assert.Equal("ab cd", Trim(str1, "$")) + + assert.Equal("abcd", Trim("\nabcd")) +} + +func TestSplitAndTrim(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTrim") + + str := " a,b, c,d,$1 " + + result1 := SplitAndTrim(str, ",") + result2 := SplitAndTrim(str, ",", "$") + + assert.Equal([]string{"a", "b", "c", "d", "$1"}, result1) + assert.Equal([]string{"a", "b", "c", "d", "1"}, result2) +} + +func TestHideString(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTrim") + + tests := []struct { + input string + start int + end int + replacedChar string + expected string + }{ + {"13242658976", 0, -1, "*", "13242658976"}, + {"13242658976", 0, 0, "*", "13242658976"}, + {"13242658976", 0, 4, "*", "****2658976"}, + {"13242658976", 3, 3, "*", "13242658976"}, + {"13242658976", 3, 4, "*", "132*2658976"}, + {"13242658976", 3, 7, "*", "132****8976"}, + {"13242658976", 3, 11, "*", "132********"}, + {"13242658976", 7, 100, "*", "1324265****"}, + {"13242658976", 100, 100, "*", "13242658976"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, HideString(tt.input, tt.start, tt.end, tt.replacedChar)) + } +} + +func TestContainsAll(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestContainsAll") + + assert.Equal(true, ContainsAll("hello world", []string{"hello", "world"})) + assert.Equal(true, ContainsAll("hello world", []string{""})) + assert.Equal(false, ContainsAll("hello world", []string{"hello", "abc"})) +} + +func TestContainsAny(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestContainsAny") + + assert.Equal(true, ContainsAny("hello world", []string{"hello", "world"})) + assert.Equal(true, ContainsAny("hello world", []string{"hello", "abc"})) + assert.Equal(false, ContainsAny("hello world", []string{"123", "abc"})) +} + +func TestRemoveWhiteSpace(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestRemoveWhiteSpace") + + str := " hello \r\n \t world" + + assert.Equal("", RemoveWhiteSpace("", true)) + assert.Equal("helloworld", RemoveWhiteSpace(str, true)) + assert.Equal("hello world", RemoveWhiteSpace(str, false)) +} + +func TestSubInBetween(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestSubInBetween") + + tests := []struct { + input string + start string + end string + expected string + }{ + {"abcde", "", "", ""}, + {"abcde", "a", "d", "bc"}, + {"abcde", "a", "e", "bcd"}, + {"abcde", "a", "f", ""}, + {"abcde", "a", "", ""}, + {"abcde", "", "e", "abcd"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, SubInBetween(tt.input, tt.start, tt.end)) + } +} + +func TestHammingDistance(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "HammingDistance") + + hd := func(a, b string) int { + c, _ := HammingDistance(a, b) + return c + } + + tests := []struct { + strA string + strB string + hammingDistance int + }{ + {" ", " ", 0}, + {" ", "c", 1}, + {"a", "d", 1}, + {"a", " ", 1}, + {"a", "f", 1}, + + {"", "", 0}, + {"abc", "ab", -1}, + {"abc", "def", 3}, + {"kitten", "sitting", -1}, + {"ö", "ü", 1}, + {"日本語", "日本語", 0}, + {"日本語", "語日本", 3}, + } + + for _, tt := range tests { + assert.Equal(tt.hammingDistance, hd(tt.strA, tt.strB)) + } +} + +func TestConcat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestConcat") + + tests := []struct { + args []string + expected string + }{ + {[]string{}, ""}, + {[]string{"a"}, "a"}, + {[]string{"a", "b"}, "ab"}, + {[]string{"a", "b", "c"}, "abc"}, + {[]string{"a", "", "b", "c", ""}, "abc"}, + {[]string{"你好", ",", "", "世界!", ""}, "你好,世界!"}, + {[]string{"Hello", " Wo", "r", "ld!", ""}, "Hello World!"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Concat(0, tt.args...)) + } +} + +func TestEllipsis(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestEllipsis") + + tests := []struct { + input string + length int + expected string + }{ + {"", 0, ""}, + {"hello world", 0, ""}, + {"hello world", -1, ""}, + {"hello world", 5, "hello..."}, + {"hello world", 11, "hello world"}, + {"你好,世界!", 2, "你好..."}, + {"😀😃😄😁😆", 3, "😀😃😄..."}, + {"This is a test.", 10, "This is a ..."}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Ellipsis(tt.input, tt.length)) + } +} + +func TestShuffle(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestShuffle") + + assert.Equal("", Shuffle("")) + assert.Equal("a", Shuffle("a")) + + str := "hello" + shuffledStr := Shuffle(str) + assert.Equal(5, len(shuffledStr)) +} + +func TestRotate(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRotate") + + tests := []struct { + input string + shift int + expected string + }{ + {"", 1, ""}, + {"a", 0, "a"}, + {"a", 1, "a"}, + {"a", -1, "a"}, + + {"Hello", -2, "lloHe"}, + {"Hello", 1, "oHell"}, + {"Hello, world!", 3, "ld!Hello, wor"}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, Rotate(tt.input, tt.shift)) + } +} + +func TestTemplateReplace(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTemplateReplace") + + t.Run("basic", func(t *testing.T) { + template := `Hello, my name is {name}, I'm {age} years old.` + data := map[string]string{ + "name": "Bob", + "age": "20", + } + + expected := `Hello, my name is Bob, I'm 20 years old.` + result := TemplateReplace(template, data) + + assert.Equal(expected, result) + }) + + t.Run("not found", func(t *testing.T) { + template := `Hello, my name is {name}, I'm {age} years old.` + data := map[string]string{ + "name": "Bob", + } + + expected := `Hello, my name is Bob, I'm {age} years old.` + result := TemplateReplace(template, data) + + assert.Equal(expected, result) + }) + + t.Run("brackets", func(t *testing.T) { + template := `Hello, my name is {name}, I'm {{age}} years old.` + data := map[string]string{ + "name": "Bob", + "age": "20", + } + + expected := `Hello, my name is Bob, I'm {20} years old.` + result := TemplateReplace(template, data) + + assert.Equal(expected, result) + }) +} + +func TestRegexMatchAllGroups(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestRegexMatchAllGroups") + + tests := []struct { + pattern string + str string + expected [][]string + }{ + { + pattern: `(\w+\.+\w+)@(\w+)\.(\w+)`, + str: "Emails: john.doe@example.com and jane.doe@example.com", + expected: [][]string{{"john.doe@example.com", "john.doe", "example", "com"}, {"jane.doe@example.com", "jane.doe", "example", "com"}}, + }, + { + pattern: `(\d+)`, + str: "No numbers here!", + expected: nil, + }, + { + pattern: `(\d{3})-(\d{2})-(\d{4})`, + str: "My number is 123-45-6789", + expected: [][]string{{"123-45-6789", "123", "45", "6789"}}, + }, + { + pattern: `(\w+)\s(\d+)`, + str: "Item A 123, Item B 456", + expected: [][]string{{"A 123", "A", "123"}, {"B 456", "B", "456"}}, + }, + { + pattern: `(\d{2})-(\d{2})-(\d{4})`, + str: "Dates: 01-01-2020, 12-31-1999", + expected: [][]string{{"01-01-2020", "01", "01", "2020"}, {"12-31-1999", "12", "31", "1999"}}, + }, + } + + for _, tt := range tests { + result := RegexMatchAllGroups(tt.pattern, tt.str) + assert.Equal(tt.expected, result) + } +} + +func TestExtractContent(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestExtractContent") + + tests := []struct { + name string + input string + start string + end string + expected []string + }{ + { + name: "Extract content between and ", + input: "This is content1 and content2 and content3", + start: "", + end: "", + expected: []string{"content1", "content2", "content3"}, + }, + { + name: "No tags in the string", + input: "This string has no tags", + start: "", + end: "", + expected: []string{}, + }, + { + name: "Single tag pair", + input: "onlyContent", + start: "", + end: "", + expected: []string{"onlyContent"}, + }, + { + name: "Tags without end tag", + input: "This content without end tag", + start: "", + end: "", + expected: []string{}, + }, + { + name: "Tags with nested content", + input: "content inner end", + start: "", + end: "", + expected: []string{"content inner end"}, + }, + { + name: "Edge case with empty string", + input: "", + start: "", + end: "", + expected: []string{}, + }, + { + name: "Edge case with no start tag", + input: "content without start tag", + start: "", + end: "", + expected: []string{}, + }, + { + name: "Edge case with no end tag", + input: "content without end tag", + start: "", + end: "", + expected: []string{}, + }, + { + name: "Multiple consecutive tags", + input: "content1content2content3", + start: "", + end: "", + expected: []string{"content1", "content2", "content3"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ExtractContent(tt.input, tt.start, tt.end) + assert.Equal(tt.expected, result) + }) + } +} + +func TestFindAllOccurrences(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestFindAllOccurrences") + + var empty []int + tests := []struct { + input string + substr string + expected []int + }{ + {"", "", empty}, + {"", "a", empty}, + {"a", "", []int{0}}, + {"a", "a", []int{0}}, + {"aa", "a", []int{0, 1}}, + {"ababab", "ab", []int{0, 2, 4}}, + {"ababab", "ba", []int{1, 3}}, + {"ababab", "c", empty}, + {"ababab", "ababab", []int{0}}, + {"ababab", "abababc", empty}, + {"ababab", "abababa", empty}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, FindAllOccurrences(tt.input, tt.substr)) + } +} diff --git a/system/os.go b/system/os.go index 91a1cb9d..641e43fe 100644 --- a/system/os.go +++ b/system/os.go @@ -6,42 +6,60 @@ package system import ( "bytes" + "fmt" "os" "os/exec" "runtime" + "strconv" + "strings" + "unicode/utf8" + + "github.com/duke-git/lancet/v2/validator" + "golang.org/x/text/encoding/simplifiedchinese" +) + +type ( + Option func(*exec.Cmd) ) -// IsWindows check if current os is windows +// IsWindows check if current os is windows. +// Play: https://go.dev/play/p/XzJULbzmf9m func IsWindows() bool { return runtime.GOOS == "windows" } -// IsLinux check if current os is linux +// IsLinux check if current os is linux. +// Play: https://go.dev/play/p/zIflQgZNuxD func IsLinux() bool { return runtime.GOOS == "linux" } -// IsMac check if current os is macos +// IsMac check if current os is macos. +// Play: https://go.dev/play/p/Mg4Hjtyq7Zc func IsMac() bool { return runtime.GOOS == "darwin" } // GetOsEnv gets the value of the environment variable named by the key. +// Play: https://go.dev/play/p/D88OYVCyjO- func GetOsEnv(key string) string { return os.Getenv(key) } // SetOsEnv sets the value of the environment variable named by the key. +// Play: https://go.dev/play/p/D88OYVCyjO- func SetOsEnv(key, value string) error { return os.Setenv(key, value) } // RemoveOsEnv remove a single environment variable. +// Play: https://go.dev/play/p/fqyq4b3xUFQ func RemoveOsEnv(key string) error { return os.Unsetenv(key) } -// CompareOsEnv gets env named by the key and compare it with comparedEnv +// CompareOsEnv gets env named by the key and compare it with comparedEnv. +// Play: https://go.dev/play/p/BciHrKYOHbp func CompareOsEnv(key, comparedEnv string) bool { env := GetOsEnv(key) if env == "" { @@ -50,23 +68,257 @@ func CompareOsEnv(key, comparedEnv string) bool { return env == comparedEnv } -// ExecCommand use shell /bin/bash -c to execute command -func ExecCommand(command string) (stdout, stderr string, err error) { +// ExecCommand execute command, return the stdout and stderr string of command, and error if error occur +// param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1 +// in linux, use /bin/bash -c to execute command +// in windows, use powershell.exe to execute command +// Play: https://go.dev/play/p/n-2fLyZef-4 +func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error) { var out bytes.Buffer - var errout bytes.Buffer + var errOut bytes.Buffer cmd := exec.Command("/bin/bash", "-c", command) if IsWindows() { - cmd = exec.Command("cmd") + cmd = exec.Command("powershell.exe", command) + } + + for _, opt := range opts { + if opt != nil { + opt(cmd) + } } cmd.Stdout = &out - cmd.Stderr = &errout + cmd.Stderr = &errOut + err = cmd.Run() if err != nil { - stderr = string(errout.Bytes()) + if utf8.Valid(errOut.Bytes()) { + stderr = byteToString(errOut.Bytes(), "UTF8") + } else if validator.IsGBK(errOut.Bytes()) { + stderr = byteToString(errOut.Bytes(), "GBK") + } + return + } + + data := out.Bytes() + if utf8.Valid(data) { + stdout = byteToString(data, "UTF8") + } else if validator.IsGBK(data) { + stdout = byteToString(data, "GBK") } - stdout = string(out.Bytes()) return } + +func byteToString(data []byte, charset string) string { + var result string + + switch charset { + case "GBK": + decodeBytes, _ := simplifiedchinese.GBK.NewDecoder().Bytes(data) + result = string(decodeBytes) + case "GB18030": + decodeBytes, _ := simplifiedchinese.GB18030.NewDecoder().Bytes(data) + result = string(decodeBytes) + case "UTF8": + fallthrough + default: + result = string(data) + } + + return result +} + +// GetOsBits return current os bits (32 or 64). +// Play: https://go.dev/play/p/ml-_XH3gJbW +func GetOsBits() int { + return 32 << (^uint(0) >> 63) +} + +// StartProcess start a new process with the specified name and arguments. +// Play: https://go.dev/play/p/5GVol6ryS_X +func StartProcess(command string, args ...string) (int, error) { + cmd := exec.Command(command, args...) + + if err := cmd.Start(); err != nil { + return 0, err + } + + return cmd.Process.Pid, nil +} + +// StopProcess stop a process by pid. +// Play: https://go.dev/play/p/jJZhRYGGcmD +func StopProcess(pid int) error { + process, err := os.FindProcess(pid) + if err != nil { + return err + } + + return process.Signal(os.Kill) +} + +// KillProcess kill a process by pid. +// Play: https://go.dev/play/p/XKmvV-ExBWa +func KillProcess(pid int) error { + process, err := os.FindProcess(pid) + if err != nil { + return err + } + + return process.Kill() +} + +// ProcessInfo contains detailed information about a process. +type ProcessInfo struct { + PID int + CPU string + Memory string + State string + User string + Cmd string + Threads []string + IOStats string + StartTime string + ParentPID int + NetworkConnections string +} + +// GetProcessInfo retrieves detailed process information by pid. +// Play: https://go.dev/play/p/NQDVywEYYx7 +func GetProcessInfo(pid int) (*ProcessInfo, error) { + var cmd *exec.Cmd + + if runtime.GOOS == "windows" { + cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/FO", "CSV", "/V") + } else { + cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "pid,%cpu,%mem,state,user,comm") + } + + output, err := cmd.Output() + if err != nil { + return nil, err + } + + processInfo, err := parseProcessInfo(output, pid) + if err != nil { + return nil, err + } + + if runtime.GOOS != "windows" { + processInfo.Threads, _ = getThreadsInfo(pid) + processInfo.IOStats, _ = getIOStats(pid) + processInfo.StartTime, _ = getProcessStartTime(pid) + processInfo.ParentPID, _ = getParentProcess(pid) + processInfo.NetworkConnections, _ = getNetworkConnections(pid) + } + + return processInfo, nil +} + +// parseProcessInfo parses the output of `ps` or `tasklist` to fill the ProcessInfo structure. +func parseProcessInfo(output []byte, pid int) (*ProcessInfo, error) { + lines := strings.Split(string(output), "\n") + + if len(lines) < 2 { + return nil, fmt.Errorf("no process found with PID %d", pid) + } + + var processInfo ProcessInfo + if runtime.GOOS == "windows" { + fields := strings.Split(lines[1], "\",\"") + if len(fields) < 9 { + return nil, fmt.Errorf("unexpected tasklist output format") + } + + processInfo = ProcessInfo{ + PID: pid, + CPU: "N/A", + Memory: fields[4], // Memory usage in K + State: fields[5], + User: "N/A", + Cmd: fields[8], + } + } else { + fields := strings.Fields(lines[1]) + if len(fields) < 6 { + return nil, fmt.Errorf("unexpected ps output format") + } + + processInfo = ProcessInfo{ + PID: pid, + CPU: fields[1], + Memory: fields[2], + State: fields[3], + User: fields[4], + Cmd: fields[5], + } + } + + return &processInfo, nil +} + +func getThreadsInfo(pid int) ([]string, error) { + cmd := exec.Command("ps", "-T", "-p", strconv.Itoa(pid)) + output, err := cmd.Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(output), "\n") + + var threads []string + for _, line := range lines[1:] { + if strings.TrimSpace(line) != "" { + threads = append(threads, line) + } + } + + return threads, nil +} + +func getIOStats(pid int) (string, error) { + filePath := fmt.Sprintf("/proc/%d/io", pid) + data, err := os.ReadFile(filePath) + if err != nil { + return "", err + } + + return string(data), nil +} + +func getProcessStartTime(pid int) (string, error) { + cmd := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "lstart=") + output, err := cmd.Output() + if err != nil { + return "", err + } + + return strings.TrimSpace(string(output)), nil +} + +func getParentProcess(pid int) (int, error) { + cmd := exec.Command("ps", "-o", "ppid=", "-p", strconv.Itoa(pid)) + output, err := cmd.Output() + if err != nil { + return 0, err + } + + ppid, err := strconv.Atoi(strings.TrimSpace(string(output))) + if err != nil { + return 0, err + } + + return ppid, nil +} + +func getNetworkConnections(pid int) (string, error) { + cmd := exec.Command("lsof", "-p", strconv.Itoa(pid), "-i") + output, err := cmd.Output() + if err != nil { + return "", err + } + + return string(output), nil +} diff --git a/system/os_darwin.go b/system/os_darwin.go new file mode 100644 index 00000000..617fa477 --- /dev/null +++ b/system/os_darwin.go @@ -0,0 +1,19 @@ +//go:build darwin + +package system + +import ( + "os/exec" +) + +func WithForeground() Option { + return func(c *exec.Cmd) { + + } +} + +func WithWinHide() Option { + return func(c *exec.Cmd) { + + } +} diff --git a/system/os_example_test.go b/system/os_example_test.go new file mode 100644 index 00000000..bc7cf31f --- /dev/null +++ b/system/os_example_test.go @@ -0,0 +1,133 @@ +package system + +import ( + "fmt" + "time" +) + +func ExampleSetOsEnv() { + err := SetOsEnv("foo", "abc") + result := GetOsEnv("foo") + + fmt.Println(err) + fmt.Println(result) + // Output: + // + // abc +} + +func ExampleGetOsEnv() { + ok := SetOsEnv("foo", "abc") + result := GetOsEnv("foo") + + fmt.Println(ok) + fmt.Println(result) + // Output: + // + // abc +} + +func ExampleRemoveOsEnv() { + err1 := SetOsEnv("foo", "abc") + result1 := GetOsEnv("foo") + + err2 := RemoveOsEnv("foo") + result2 := GetOsEnv("foo") + + fmt.Println(err1) + fmt.Println(err2) + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // + // + // abc + // +} + +func ExampleCompareOsEnv() { + err := SetOsEnv("foo", "abc") + if err != nil { + return + } + + result := CompareOsEnv("foo", "abc") + + fmt.Println(result) + + // Output: + // true +} + +func ExampleExecCommand() { + _, stderr, err := ExecCommand("ls") + // fmt.Println(stdout) + fmt.Println(stderr) + fmt.Println(err) + + // Output: + // + // +} + +func ExampleGetOsBits() { + osBits := GetOsBits() + + fmt.Println(osBits) + // Output: + // 64 +} + +func ExampleStartProcess() { + pid, err := StartProcess("sleep", "2") + if err != nil { + return + } + + fmt.Println(pid) +} + +func ExampleStopProcess() { + pid, err := StartProcess("sleep", "10") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = StopProcess(pid) + + fmt.Println(err) + + // Output: + // +} + +func ExampleKillProcess() { + pid, err := StartProcess("sleep", "3") + if err != nil { + return + } + time.Sleep(1 * time.Second) + + err = KillProcess(pid) + + fmt.Println(err) + + // Output: + // +} + +func ExampleGetProcessInfo() { + pid, err := StartProcess("ls", "-a") + if err != nil { + return + } + + processInfo, err := GetProcessInfo(pid) + if err != nil { + return + } + + fmt.Println(processInfo) +} diff --git a/system/os_linux.go b/system/os_linux.go new file mode 100644 index 00000000..24c63e61 --- /dev/null +++ b/system/os_linux.go @@ -0,0 +1,26 @@ +//go:build linux + +package system + +import ( + "os/exec" + "syscall" +) + +func WithForeground() Option { + return func(c *exec.Cmd) { + if c.SysProcAttr == nil { + c.SysProcAttr = &syscall.SysProcAttr{ + Foreground: true, + } + } else { + c.SysProcAttr.Foreground = true + } + } +} + +func WithWinHide() Option { + return func(c *exec.Cmd) { + + } +} diff --git a/system/os_test.go b/system/os_test.go index d70813f8..31e19b10 100644 --- a/system/os_test.go +++ b/system/os_test.go @@ -1,31 +1,39 @@ package system import ( + "os/exec" "strings" "testing" + "time" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" ) func TestOsDetection(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestOsJudgment") osType, _, _ := ExecCommand("echo $OSTYPE") - if strings.Index(osType, "linux") != -1 { + if strings.Contains(osType, "linux") { assert.Equal(true, IsLinux()) } - if strings.Index(osType, "darwin") != -1 { + if strings.Contains(osType, "darwin") { assert.Equal(true, IsMac()) } } func TestOsEnvOperation(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestOsEnvOperation") envNotExist := GetOsEnv("foo") assert.Equal("", envNotExist) - SetOsEnv("foo", "foo_value") + err := SetOsEnv("foo", "foo_value") + assert.IsNil(err) + envExist := GetOsEnv("foo") assert.Equal("foo_value", envExist) @@ -34,7 +42,7 @@ func TestOsEnvOperation(t *testing.T) { assert.Equal(false, CompareOsEnv("abc", "abc")) assert.Equal(false, CompareOsEnv("abc", "abc")) - err := RemoveOsEnv("foo") + err = RemoveOsEnv("foo") if err != nil { t.Fail() } @@ -42,21 +50,95 @@ func TestOsEnvOperation(t *testing.T) { } func TestExecCommand(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestExecCommand") - out, errout, err := ExecCommand("ls") - t.Log("std out: ", out) - t.Log("std err: ", errout) + // linux or mac + stdout, stderr, err := ExecCommand("ls", func(cmd *exec.Cmd) { + cmd.Dir = "/" + }) + t.Log("std out: ", stdout) + t.Log("std err: ", stderr) + assert.Equal("", stderr) assert.IsNil(err) - out, errout, err = ExecCommand("abc") - t.Log("std out: ", out) - t.Log("std err: ", errout) - if err != nil { - t.Logf("error: %v\n", err) + // windows + stdout, stderr, err = ExecCommand("dir") + t.Log("std out: ", stdout) + t.Log("std err: ", stderr) + if IsWindows() { + assert.IsNil(err) } - if !IsWindows() { - assert.IsNotNil(err) + // error command + stdout, stderr, err = ExecCommand("abc") + t.Log("std out: ", stdout) + t.Log("std err: ", stderr) + assert.IsNotNil(err) +} + +func TestGetOsBits(t *testing.T) { + t.Parallel() + + osBits := GetOsBits() + switch osBits { + case 32, 64: + t.Logf("os is %d", osBits) + default: + t.Error("os is not 32 or 64 bits") } } + +func TestStartProcess(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStartProcess") + + pid, err := StartProcess("ls", "-a") + + assert.IsNil(err) + assert.Equal(true, pid > 0) +} + +func TestKillProcess(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestKillProcess") + + pid, err := StartProcess("ls") + assert.IsNil(err) + assert.Equal(true, pid > 0) + + err = KillProcess(pid) + assert.IsNil(err) +} + +func TestStopProcess(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestStopProcess") + + pid, err := StartProcess("ls") + assert.IsNil(err) + assert.Equal(true, pid > 0) + + err = StopProcess(pid) + assert.IsNil(err) +} + +func TestGetProcessInfo(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestGetProcessInfo") + + pid, err := StartProcess("ls", "-a") + assert.IsNil(err) + + time.Sleep(1 * time.Second) + + processInfo, err := GetProcessInfo(pid) + assert.IsNil(err) + + t.Log(processInfo) +} diff --git a/system/os_windows.go b/system/os_windows.go new file mode 100644 index 00000000..4286fddf --- /dev/null +++ b/system/os_windows.go @@ -0,0 +1,26 @@ +//go:build windows + +package system + +import ( + "os/exec" + "syscall" +) + +func WithWinHide() Option { + return func(c *exec.Cmd) { + if c.SysProcAttr == nil { + c.SysProcAttr = &syscall.SysProcAttr{ + HideWindow: true, + } + } else { + c.SysProcAttr.HideWindow = true + } + } +} + +func WithForeground() Option { + return func(c *exec.Cmd) { + + } +} diff --git a/tuple/tuple.go b/tuple/tuple.go new file mode 100644 index 00000000..4554e262 --- /dev/null +++ b/tuple/tuple.go @@ -0,0 +1,641 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package tuple implements tuple data type and some operations on it. +package tuple + +import "github.com/duke-git/lancet/v2/mathutil" + +// Tuple2 represents a 2 elemnets tuple +type Tuple2[A any, B any] struct { + FieldA A + FieldB B +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/0fD1qfCVwjm +func (t Tuple2[A, B]) Unbox() (A, B) { + return t.FieldA, t.FieldB +} + +// NewTuple2 creates a 2 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/3sHVqBQpLYN +func NewTuple2[A any, B any](a A, b B) Tuple2[A, B] { + return Tuple2[A, B]{FieldA: a, FieldB: b} +} + +// Zip2 create a slice of Tuple2, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/4ncWJJ77Xio +func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] { + size := mathutil.Max(len(a), len(b)) + + tuples := make([]Tuple2[A, B], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + + tuples[i] = Tuple2[A, B]{FieldA: v1, FieldB: v2} + } + + return tuples +} + +// Unzip2 creates a group of slice from a slice of Tuple2. +// Play: https://go.dev/play/p/KBecr60feXb +func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + } + + return r1, r2 +} + +// Tuple3 represents a 3 elemnets tuple +type Tuple3[A any, B any, C any] struct { + FieldA A + FieldB B + FieldC C +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/YojLy-id1BS +func (t Tuple3[A, B, C]) Unbox() (A, B, C) { + return t.FieldA, t.FieldB, t.FieldC +} + +// NewTuple3 creates a 3 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/FtH2sdCLlCf +func NewTuple3[A any, B any, C any](a A, b B, c C) Tuple3[A, B, C] { + return Tuple3[A, B, C]{FieldA: a, FieldB: b, FieldC: c} +} + +// Zip3 create a slice of Tuple3, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/97NgmsTILfu +func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] { + size := mathutil.Max(len(a), len(b), len(c)) + + tuples := make([]Tuple3[A, B, C], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + + tuples[i] = Tuple3[A, B, C]{FieldA: v1, FieldB: v2, FieldC: v3} + } + + return tuples +} + +// Unzip3 creates a group of slice from a slice of Tuple3. +// Play: https://go.dev/play/p/bba4cpAa7KO +func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + } + + return r1, r2, r3 +} + +// Tuple4 represents a 4 elemnets tuple +type Tuple4[A any, B any, C any, D any] struct { + FieldA A + FieldB B + FieldC C + FieldD D +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/ACj9YuACGgW +func (t Tuple4[A, B, C, D]) Unbox() (A, B, C, D) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD +} + +// NewTuple4 creates a 4 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/D2EqDz096tk +func NewTuple4[A any, B any, C any, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] { + return Tuple4[A, B, C, D]{FieldA: a, FieldB: b, FieldC: c, FieldD: d} +} + +// Zip4 create a slice of Tuple4, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/PEmTYVK5hL4 +func Zip4[A any, B any, C any, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] { + size := mathutil.Max(len(a), len(b), len(c), len(d)) + + tuples := make([]Tuple4[A, B, C, D], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + v4, _ := getByIndex(d, i) + + tuples[i] = Tuple4[A, B, C, D]{FieldA: v1, FieldB: v2, FieldC: v3, FieldD: v4} + } + + return tuples +} + +// Unzip4 creates a group of slice from a slice of Tuple4. +// Play: https://go.dev/play/p/rb8z4gyYSRN +func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + } + + return r1, r2, r3, r4 +} + +// Tuple5 represents a 5 elemnets tuple +type Tuple5[A any, B any, C any, D any, E any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/GyIyZHjCvoS +func (t Tuple5[A, B, C, D, E]) Unbox() (A, B, C, D, E) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE +} + +// NewTuple5 creates a 5 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/2WndmVxPg-r +func NewTuple5[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] { + return Tuple5[A, B, C, D, E]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e} +} + +// Zip5 create a slice of Tuple5, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/fCAAJLMfBIP +func Zip5[A any, B any, C any, D any, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e)) + + tuples := make([]Tuple5[A, B, C, D, E], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + v4, _ := getByIndex(d, i) + v5, _ := getByIndex(e, i) + + tuples[i] = Tuple5[A, B, C, D, E]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5} + } + + return tuples +} + +// Unzip5 creates a group of slice from a slice of Tuple5. +// Play: https://go.dev/play/p/gyl6vKfhqPb +func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + } + + return r1, r2, r3, r4, r5 +} + +// Tuple6 represents a 6 elemnets tuple +type Tuple6[A any, B any, C any, D any, E any, F any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/FjIHV7lpxmW +func (t Tuple6[A, B, C, D, E, F]) Unbox() (A, B, C, D, E, F) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF +} + +// NewTuple6 creates a 6 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/VjqcCwEJZbs +func NewTuple6[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] { + return Tuple6[A, B, C, D, E, F]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e, FieldF: f} +} + +// Zip6 create a slice of Tuple6, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/oWPrnUYuFHo +func Zip6[A any, B any, C any, D any, E any, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e), len(f)) + + tuples := make([]Tuple6[A, B, C, D, E, F], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + v4, _ := getByIndex(d, i) + v5, _ := getByIndex(e, i) + v6, _ := getByIndex(f, i) + + tuples[i] = Tuple6[A, B, C, D, E, F]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5, FieldF: v6} + } + + return tuples +} + +// Unzip6 creates a group of slice from a slice of Tuple6. +// Play: https://go.dev/play/p/l41XFqCyh5E +func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + r6 := make([]F, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + r6[i] = t.FieldF + } + + return r1, r2, r3, r4, r5, r6 +} + +// Tuple7 represents a 7 elemnets tuple +type Tuple7[A any, B any, C any, D any, E any, F any, G any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/R9I8qeDk0zs +func (t Tuple7[A, B, C, D, E, F, G]) Unbox() (A, B, C, D, E, F, G) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG +} + +// NewTuple7 creates a 7 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/dzAgv_Ezub9 +func NewTuple7[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] { + return Tuple7[A, B, C, D, E, F, G]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e, FieldF: f, FieldG: g} +} + +// Zip7 create a slice of Tuple7, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/WUJuo897Egf +func Zip7[A any, B any, C any, D any, E any, F any, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e), len(f), len(g)) + + tuples := make([]Tuple7[A, B, C, D, E, F, G], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + v4, _ := getByIndex(d, i) + v5, _ := getByIndex(e, i) + v6, _ := getByIndex(f, i) + v7, _ := getByIndex(g, i) + + tuples[i] = Tuple7[A, B, C, D, E, F, G]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5, FieldF: v6, + FieldG: v7} + } + + return tuples +} + +// Unzip7 creates a group of slice from a slice of Tuple7. +// Play: https://go.dev/play/p/hws_P1Fr2j3 +func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + r6 := make([]F, size) + r7 := make([]G, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + r6[i] = t.FieldF + r7[i] = t.FieldG + } + + return r1, r2, r3, r4, r5, r6, r7 +} + +// Tuple8 represents a 8 elemnets tuple +type Tuple8[A any, B any, C any, D any, E any, F any, G any, H any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/PRxLBBb4SMl +func (t Tuple8[A, B, C, D, E, F, G, H]) Unbox() (A, B, C, D, E, F, G, H) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH +} + +// NewTuple8 creates a 8 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/YA9S0rz3dRz +func NewTuple8[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H) Tuple8[A, B, C, D, E, F, G, H] { + return Tuple8[A, B, C, D, E, F, G, H]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e, FieldF: f, FieldG: g, FieldH: h} +} + +// Zip8 create a slice of Tuple8, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/8V9jWkuJfaQ +func Zip8[A any, B any, C any, D any, E any, F any, G any, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)) + + tuples := make([]Tuple8[A, B, C, D, E, F, G, H], size) + + for i := 0; i < size; i++ { + v1, _ := getByIndex(a, i) + v2, _ := getByIndex(b, i) + v3, _ := getByIndex(c, i) + v4, _ := getByIndex(d, i) + v5, _ := getByIndex(e, i) + v6, _ := getByIndex(f, i) + v7, _ := getByIndex(g, i) + v8, _ := getByIndex(h, i) + + tuples[i] = Tuple8[A, B, C, D, E, F, G, H]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5, FieldF: v6, + FieldG: v7, FieldH: v8} + } + + return tuples +} + +// Unzip8 creates a group of slice from a slice of Tuple8. +// Play: https://go.dev/play/p/1SndOwGsZB4 +func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + r6 := make([]F, size) + r7 := make([]G, size) + r8 := make([]H, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + r6[i] = t.FieldF + r7[i] = t.FieldG + r8[i] = t.FieldH + } + + return r1, r2, r3, r4, r5, r6, r7, r8 +} + +// Tuple9 represents a 9 elemnets tuple +type Tuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/oFJFGTAuOa8 +func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unbox() (A, B, C, D, E, F, G, H, I) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI +} + +// NewTuple9 creates a 9 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/yS2NGGtZpQr +func NewTuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I) Tuple9[A, B, C, D, E, F, G, H, I] { + return Tuple9[A, B, C, D, E, F, G, H, I]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e, FieldF: f, FieldG: g, FieldH: h, FieldI: i} +} + +// Zip9 create a slice of Tuple9, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/cgsL15QYnfz +func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)) + + tuples := make([]Tuple9[A, B, C, D, E, F, G, H, I], size) + + for idx := 0; idx < size; idx++ { + v1, _ := getByIndex(a, idx) + v2, _ := getByIndex(b, idx) + v3, _ := getByIndex(c, idx) + v4, _ := getByIndex(d, idx) + v5, _ := getByIndex(e, idx) + v6, _ := getByIndex(f, idx) + v7, _ := getByIndex(g, idx) + v8, _ := getByIndex(h, idx) + v9, _ := getByIndex(i, idx) + + tuples[idx] = Tuple9[A, B, C, D, E, F, G, H, I]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5, FieldF: v6, + FieldG: v7, FieldH: v8, FieldI: v9} + } + + return tuples +} + +// Unzip9 creates a group of slice from a slice of Tuple9. +// Play: https://go.dev/play/p/91-BU_KURSA +func Unzip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + r6 := make([]F, size) + r7 := make([]G, size) + r8 := make([]H, size) + r9 := make([]I, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + r6[i] = t.FieldF + r7[i] = t.FieldG + r8[i] = t.FieldH + r9[i] = t.FieldI + } + + return r1, r2, r3, r4, r5, r6, r7, r8, r9 +} + +// Tuple10 represents a 10 elemnets tuple +type Tuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] struct { + FieldA A + FieldB B + FieldC C + FieldD D + FieldE E + FieldF F + FieldG G + FieldH H + FieldI I + FieldJ J +} + +// Unbox returns values in tuple. +// Play: https://go.dev/play/p/qfyx3x_X0Cu +func (t Tuple10[A, B, C, D, E, F, G, H, I, J]) Unbox() (A, B, C, D, E, F, G, H, I, J) { + return t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI, t.FieldJ +} + +// NewTuple10 creates a 10 elemnets tuple from a list of values. +// Play: https://go.dev/play/p/799qqZg0hUv +func NewTuple10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a A, b B, c C, d D, e E, f F, g G, h H, i I, j J) Tuple10[A, B, C, D, E, F, G, H, I, J] { + return Tuple10[A, B, C, D, E, F, G, H, I, J]{FieldA: a, FieldB: b, FieldC: c, FieldD: d, FieldE: e, FieldF: f, FieldG: g, FieldH: h, FieldI: i, FieldJ: j} +} + +// Zip10 create a slice of Tuple10, whose elements are correspond to the given slice elements. +// Play: https://go.dev/play/p/YSR-2cXnrY4 +func Zip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, j []J) []Tuple10[A, B, C, D, E, F, G, H, I, J] { + size := mathutil.Max(len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i), len(j)) + + tuples := make([]Tuple10[A, B, C, D, E, F, G, H, I, J], size) + + for idx := 0; idx < size; idx++ { + v1, _ := getByIndex(a, idx) + v2, _ := getByIndex(b, idx) + v3, _ := getByIndex(c, idx) + v4, _ := getByIndex(d, idx) + v5, _ := getByIndex(e, idx) + v6, _ := getByIndex(f, idx) + v7, _ := getByIndex(g, idx) + v8, _ := getByIndex(h, idx) + v9, _ := getByIndex(i, idx) + v10, _ := getByIndex(j, idx) + + tuples[idx] = Tuple10[A, B, C, D, E, F, G, H, I, J]{ + FieldA: v1, FieldB: v2, FieldC: v3, + FieldD: v4, FieldE: v5, FieldF: v6, + FieldG: v7, FieldH: v8, FieldI: v9, + FieldJ: v10} + } + + return tuples +} + +// Unzip10 creates a group of slice from a slice of Tuple10. +// Play: https://go.dev/play/p/-taQB6Wfre_z +func Unzip10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](tuples []Tuple10[A, B, C, D, E, F, G, H, I, J]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I, []J) { + size := len(tuples) + + r1 := make([]A, size) + r2 := make([]B, size) + r3 := make([]C, size) + r4 := make([]D, size) + r5 := make([]E, size) + r6 := make([]F, size) + r7 := make([]G, size) + r8 := make([]H, size) + r9 := make([]I, size) + r10 := make([]J, size) + + for i, t := range tuples { + r1[i] = t.FieldA + r2[i] = t.FieldB + r3[i] = t.FieldC + r4[i] = t.FieldD + r5[i] = t.FieldE + r6[i] = t.FieldF + r7[i] = t.FieldG + r8[i] = t.FieldH + r9[i] = t.FieldI + r10[i] = t.FieldJ + } + + return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 +} + +func getByIndex[T any](slice []T, index int) (T, bool) { + l := len(slice) + if index >= l || -index > l { + var zeroVal T + return zeroVal, false + } + + if index >= 0 { + return slice[index], true + } + + return slice[l+index], true +} diff --git a/tuple/tuple_example_test.go b/tuple/tuple_example_test.go new file mode 100644 index 00000000..24f71bbb --- /dev/null +++ b/tuple/tuple_example_test.go @@ -0,0 +1,301 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package tuple implements tuple data type and some operations on it. +package tuple + +import ( + "fmt" +) + +func ExampleNewTuple2() { + t := NewTuple2(1, 0.1) + fmt.Printf("%v %v", t.FieldA, t.FieldB) + + // Output: 1 0.1 +} + +func ExampleTuple2_Unbox() { + t := NewTuple2(1, 0.1) + v1, v2 := t.Unbox() + fmt.Printf("%v %v", v1, v2) + + // Output: 1 0.1 +} + +func ExampleZip2() { + result := Zip2([]int{1}, []float64{0.1}) + fmt.Println(result) + + // Output: [{1 0.1}] +} + +func ExampleUnzip2() { + v1, v2 := Unzip2([]Tuple2[int, float64]{{FieldA: 1, FieldB: 0.1}}) + + fmt.Printf("%v %v", v1, v2) + + // Output: [1] [0.1] +} + +func ExampleNewTuple3() { + t := NewTuple3(1, 0.1, "a") + fmt.Printf("%v %v %v", t.FieldA, t.FieldB, t.FieldC) + + // Output: 1 0.1 a +} + +func ExampleTuple3_Unbox() { + t := NewTuple3(1, 0.1, "a") + v1, v2, v3 := t.Unbox() + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: 1 0.1 a +} + +func ExampleZip3() { + result := Zip3([]int{1}, []float64{0.1}, []string{"a"}) + fmt.Println(result) + + // Output: [{1 0.1 a}] +} + +func ExampleUnzip3() { + v1, v2, v3 := Unzip3([]Tuple3[int, float64, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a"}, + }) + + fmt.Printf("%v %v %v", v1, v2, v3) + + // Output: [1] [0.1] [a] +} + +func ExampleNewTuple4() { + t := NewTuple4(1, 0.1, "a", true) + fmt.Printf("%v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD) + + // Output: 1 0.1 a true +} + +func ExampleTuple4_Unbox() { + t := NewTuple4(1, 0.1, "a", true) + v1, v2, v3, v4 := t.Unbox() + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: 1 0.1 a true +} + +func ExampleZip4() { + result := Zip4([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}) + fmt.Println(result) + + // Output: [{1 0.1 a true}] +} + +func ExampleUnzip4() { + v1, v2, v3, v4 := Unzip4([]Tuple4[int, float64, string, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true}, + }) + + fmt.Printf("%v %v %v %v", v1, v2, v3, v4) + + // Output: [1] [0.1] [a] [true] +} + +func ExampleNewTuple5() { + t := NewTuple5(1, 0.1, "a", true, 2) + fmt.Printf("%v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE) + + // Output: 1 0.1 a true 2 +} + +func ExampleTuple5_Unbox() { + t := NewTuple5(1, 0.1, "a", true, 2) + v1, v2, v3, v4, v5 := t.Unbox() + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: 1 0.1 a true 2 +} + +func ExampleZip5() { + result := Zip5([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2}] +} + +func ExampleUnzip5() { + v1, v2, v3, v4, v5 := Unzip5([]Tuple5[int, float64, string, bool, int]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2}, + }) + + fmt.Printf("%v %v %v %v %v", v1, v2, v3, v4, v5) + + // Output: [1] [0.1] [a] [true] [2] +} + +func ExampleNewTuple6() { + t := NewTuple6(1, 0.1, "a", true, 2, 2.2) + fmt.Printf("%v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF) + + // Output: 1 0.1 a true 2 2.2 +} + +func ExampleTuple6_Unbox() { + t := NewTuple6(1, 0.1, "a", true, 2, 2.2) + v1, v2, v3, v4, v5, v6 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: 1 0.1 a true 2 2.2 +} + +func ExampleZip6() { + result := Zip6([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2}] +} + +func ExampleUnzip6() { + v1, v2, v3, v4, v5, v6 := Unzip6([]Tuple6[int, float64, string, bool, int, float32]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2}, + }) + + fmt.Printf("%v %v %v %v %v %v", v1, v2, v3, v4, v5, v6) + + // Output: [1] [0.1] [a] [true] [2] [2.2] +} + +func ExampleNewTuple7() { + t := NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + fmt.Printf("%v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG) + + // Output: 1 0.1 a true 2 2.2 b +} + +func ExampleTuple7_Unbox() { + t := NewTuple7(1, 0.1, "a", true, 2, 2.2, "b") + v1, v2, v3, v4, v5, v6, v7 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: 1 0.1 a true 2 2.2 b +} + +func ExampleZip7() { + result := Zip7([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b}] +} + +func ExampleUnzip7() { + v1, v2, v3, v4, v5, v6, v7 := Unzip7([]Tuple7[int, float64, string, bool, int, float32, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] +} + +func ExampleNewTuple8() { + t := NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + fmt.Printf("%v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH) + + // Output: 1 0.1 a true 2 2.2 b c +} + +func ExampleTuple8_Unbox() { + t := NewTuple8(1, 0.1, "a", true, 2, 2.2, "b", "c") + v1, v2, v3, v4, v5, v6, v7, v8 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: 1 0.1 a true 2 2.2 b c +} + +func ExampleZip8() { + result := Zip8([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c}] +} + +func ExampleUnzip8() { + v1, v2, v3, v4, v5, v6, v7, v8 := Unzip8([]Tuple8[int, float64, string, bool, int, float32, string, string]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c"}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] +} + +func ExampleNewTuple9() { + t := NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + fmt.Printf("%v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} + +func ExampleTuple9_Unbox() { + t := NewTuple9(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + v1, v2, v3, v4, v5, v6, v7, v8, v9 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] +} + +func ExampleZip9() { + result := Zip9([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3}] +} + +func ExampleUnzip9() { + v1, v2, v3, v4, v5, v6, v7, v8, v9 := Unzip9([]Tuple9[int, float64, string, bool, int, float32, string, string, int64]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] +} + +func ExampleNewTuple10() { + type foo struct { + A string + } + t := NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", t.FieldA, t.FieldB, t.FieldC, t.FieldD, t.FieldE, t.FieldF, t.FieldG, t.FieldH, t.FieldI, t.FieldJ) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} + +func ExampleTuple10_Unbox() { + type foo struct { + A string + } + t := NewTuple10(1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := t.Unbox() + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: 1 0.1 a true 2 2.2 b c map[a:1] {a} +} + +func ExampleZip10() { + result := Zip10([]int{1}, []float64{0.1}, []string{"a"}, []bool{true}, []int{2}, []float32{2.2}, []string{"b"}, []string{"c"}, []int64{3}, []bool{false}) + fmt.Println(result) + + // Output: [{1 0.1 a true 2 2.2 b c 3 false}] +} + +func ExampleUnzip10() { + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := Unzip10([]Tuple10[int, float64, string, bool, int, float32, string, string, int64, bool]{ + {FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: 3, FieldJ: false}, + }) + + fmt.Printf("%v %v %v %v %v %v %v %v %v %v", v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + + // Output: [1] [0.1] [a] [true] [2] [2.2] [b] [c] [3] [false] +} diff --git a/tuple/tuple_test.go b/tuple/tuple_test.go new file mode 100644 index 00000000..9ddfe5fb --- /dev/null +++ b/tuple/tuple_test.go @@ -0,0 +1,195 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package tuple implements tuple data type and some operations on it. +package tuple + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestTuples(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestTuples") + + type foo struct { + A string + } + + t2 := NewTuple2[int, float64](1, 0.1) + t3 := NewTuple3[int, float64, string](1, 0.1, "a") + t4 := NewTuple4[int, float64, string, bool](1, 0.1, "a", true) + t5 := NewTuple5[int, float64, string, bool, int64](1, 0.1, "a", true, 2) + t6 := NewTuple6[int, float64, string, bool, int64, float32](1, 0.1, "a", true, 2, 2.2) + t7 := NewTuple7[int, float64, string, bool, int64, float32, string](1, 0.1, "a", true, 2, 2.2, "b") + t8 := NewTuple8[int, float64, string, bool, int64, float32, string, string](1, 0.1, "a", true, 2, 2.2, "b", "c") + t9 := NewTuple9[int, float64, string, bool, int64, float32, string, string, map[string]int](1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + t10 := NewTuple10[int, float64, string, bool, int64, float32, string, string, map[string]int, foo](1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + + assert.Equal(t2, Tuple2[int, float64]{FieldA: 1, FieldB: 0.1}) + assert.Equal(t3, Tuple3[int, float64, string]{FieldA: 1, FieldB: 0.1, FieldC: "a"}) + assert.Equal(t4, Tuple4[int, float64, string, bool]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true}) + assert.Equal(t5, Tuple5[int, float64, string, bool, int64]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2}) + assert.Equal(t6, Tuple6[int, float64, string, bool, int64, float32]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2}) + assert.Equal(t7, Tuple7[int, float64, string, bool, int64, float32, string]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b"}) + assert.Equal(t8, Tuple8[int, float64, string, bool, int64, float32, string, string]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c"}) + assert.Equal(t9, Tuple9[int, float64, string, bool, int64, float32, string, string, map[string]int]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: map[string]int{"a": 1}}) + assert.Equal(t10, Tuple10[int, float64, string, bool, int64, float32, string, string, map[string]int, foo]{FieldA: 1, FieldB: 0.1, FieldC: "a", FieldD: true, FieldE: 2, FieldF: 2.2, FieldG: "b", FieldH: "c", FieldI: map[string]int{"a": 1}, FieldJ: foo{A: "a"}}) +} + +func TestTuple_Unbox(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestTuple_Unbox") + + type foo struct { + A string + } + + t2 := NewTuple2[int, float64](1, 0.1) + v1, v2 := t2.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + + t3 := NewTuple3[int, float64, string](1, 0.1, "a") + v1, v2, v3 := t3.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + + t4 := NewTuple4[int, float64, string, bool](1, 0.1, "a", true) + v1, v2, v3, v4 := t4.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + + t5 := NewTuple5[int, float64, string, bool, int64](1, 0.1, "a", true, 2) + v1, v2, v3, v4, v5 := t5.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + + t6 := NewTuple6[int, float64, string, bool, int64, float32](1, 0.1, "a", true, 2, 2.2) + v1, v2, v3, v4, v5, v6 := t6.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + assert.Equal(float32(2.2), v6) + + t7 := NewTuple7[int, float64, string, bool, int64, float32, string](1, 0.1, "a", true, 2, 2.2, "b") + v1, v2, v3, v4, v5, v6, v7 := t7.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + assert.Equal(float32(2.2), v6) + assert.Equal("b", v7) + + t8 := NewTuple8[int, float64, string, bool, int64, float32, string, string](1, 0.1, "a", true, 2, 2.2, "b", "c") + v1, v2, v3, v4, v5, v6, v7, v8 := t8.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + assert.Equal(float32(2.2), v6) + assert.Equal("b", v7) + assert.Equal("c", v8) + + t9 := NewTuple9[int, float64, string, bool, int64, float32, string, string, map[string]int](1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}) + v1, v2, v3, v4, v5, v6, v7, v8, v9 := t9.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + assert.Equal(float32(2.2), v6) + assert.Equal("b", v7) + assert.Equal("c", v8) + assert.Equal(map[string]int{"a": 1}, v9) + + t10 := NewTuple10[int, float64, string, bool, int64, float32, string, string, map[string]int, foo](1, 0.1, "a", true, 2, 2.2, "b", "c", map[string]int{"a": 1}, foo{A: "a"}) + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 := t10.Unbox() + assert.Equal(1, v1) + assert.Equal(0.1, v2) + assert.Equal("a", v3) + assert.Equal(true, v4) + assert.Equal(int64(2), v5) + assert.Equal(float32(2.2), v6) + assert.Equal("b", v7) + assert.Equal("c", v8) + assert.Equal(map[string]int{"a": 1}, v9) + assert.Equal(foo{A: "a"}, v10) +} + +func TestZip(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestZip") + + r2 := Zip2( + []int{1, 2}, + []string{"a", "b"}, + ) + assert.Equal(r2, []Tuple2[int, string]{ + {FieldA: 1, FieldB: "a"}, + {FieldA: 2, FieldB: "b"}, + }) + + r3 := Zip3( + []int{1, 2, 3}, + []string{"a", "b", "c"}, + []float64{0.1, 0.2, 0.3}, + ) + assert.Equal(r3, []Tuple3[int, string, float64]{ + {FieldA: 1, FieldB: "a", FieldC: 0.1}, + {FieldA: 2, FieldB: "b", FieldC: 0.2}, + {FieldA: 3, FieldB: "c", FieldC: 0.3}, + }) + + r4 := Zip4( + []int{1, 2, 3, 4}, + []string{"a", "b", "c", "d"}, + []float64{0.1, 0.2, 0.3, 0.4}, + []bool{true, true, true, true}, + ) + assert.Equal(r4, []Tuple4[int, string, float64, bool]{ + {FieldA: 1, FieldB: "a", FieldC: 0.1, FieldD: true}, + {FieldA: 2, FieldB: "b", FieldC: 0.2, FieldD: true}, + {FieldA: 3, FieldB: "c", FieldC: 0.3, FieldD: true}, + {FieldA: 4, FieldB: "d", FieldC: 0.4, FieldD: true}, + }) + + r5 := Zip5( + []int{1, 2, 3, 4, 5}, + []string{"a", "b", "c", "d", "e"}, + []float64{0.1, 0.2, 0.3, 0.4, 0.5}, + []bool{true, true, true, true, true}, + []int{6, 7, 8, 9, 10}, + ) + assert.Equal(r5, []Tuple5[int, string, float64, bool, int]{ + {FieldA: 1, FieldB: "a", FieldC: 0.1, FieldD: true, FieldE: 6}, + {FieldA: 2, FieldB: "b", FieldC: 0.2, FieldD: true, FieldE: 7}, + {FieldA: 3, FieldB: "c", FieldC: 0.3, FieldD: true, FieldE: 8}, + {FieldA: 4, FieldB: "d", FieldC: 0.4, FieldD: true, FieldE: 9}, + {FieldA: 5, FieldB: "e", FieldC: 0.5, FieldD: true, FieldE: 10}, + }) + +} + +func TestUnzip(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestUnzip") + + r1, r2, r3 := Unzip3([]Tuple3[string, int, float64]{{FieldA: "a", FieldB: 1, FieldC: 0.1}, {FieldA: "b", FieldB: 2, FieldC: 0.2}}) + + assert.Equal(r1, []string{"a", "b"}) + assert.Equal(r2, []int{1, 2}) + assert.Equal(r3, []float64{0.1, 0.2}) +} diff --git a/validator/validator.go b/validator/validator.go index 362d8eca..1362eb81 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -6,22 +6,97 @@ package validator import ( "encoding/json" + "fmt" "net" + "net/mail" "net/url" + "reflect" "regexp" "strconv" "strings" + "time" "unicode" ) -var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) +var ( + alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) + letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`) + alphaNumericMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`) + numberRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d`) + intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`) + urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`) + dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`) + emailMatcher *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`) + chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`) + chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`) + chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]") + chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`) + creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`) + base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`) + base64URLMatcher *regexp.Regexp = regexp.MustCompile(`^([A-Za-z0-9_-]{4})*([A-Za-z0-9_-]{2}(==)?|[A-Za-z0-9_-]{3}=?)?$`) + binMatcher *regexp.Regexp = regexp.MustCompile(`^(0b)?[01]+$`) + hexMatcher *regexp.Regexp = regexp.MustCompile(`^(#|0x|0X)?[0-9a-fA-F]+$`) + visaMatcher *regexp.Regexp = regexp.MustCompile(`^4[0-9]{12}(?:[0-9]{3})?$`) + masterCardMatcher *regexp.Regexp = regexp.MustCompile(`^5[1-5][0-9]{14}$`) + americanExpressMatcher *regexp.Regexp = regexp.MustCompile(`^3[47][0-9]{13}$`) + unionPay *regexp.Regexp = regexp.MustCompile("^62[0-5]\\d{13,16}$") + chinaUnionPay *regexp.Regexp = regexp.MustCompile(`^62[0-9]{14,17}$`) +) + +var ( + // Identity card formula + factor = [17]int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} + // ID verification bit + verifyStr = [11]string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"} + // Starting year of ID card + birthStartYear = 1900 + // Province code + provinceKv = map[string]struct{}{ + "11": {}, + "12": {}, + "13": {}, + "14": {}, + "15": {}, + "21": {}, + "22": {}, + "23": {}, + "31": {}, + "32": {}, + "33": {}, + "34": {}, + "35": {}, + "36": {}, + "37": {}, + "41": {}, + "42": {}, + "43": {}, + "44": {}, + "45": {}, + "46": {}, + "50": {}, + "51": {}, + "52": {}, + "53": {}, + "54": {}, + "61": {}, + "62": {}, + "63": {}, + "64": {}, + "65": {}, + //"71": {}, + //"81": {}, + //"82": {}, + } +) -// IsAlpha checks if the string contains only letters (a-zA-Z) +// IsAlpha checks if the string contains only letters (a-zA-Z). +// Play: https://go.dev/play/p/7Q5sGOz2izQ func IsAlpha(str string) bool { - return isAlphaRegexMatcher.MatchString(str) + return alphaMatcher.MatchString(str) } -// IsAllUpper check if the string is all upper case letters A-Z +// IsAllUpper check if the string is all upper case letters A-Z. +// Play: https://go.dev/play/p/ZHctgeK1n4Z func IsAllUpper(str string) bool { for _, r := range str { if !unicode.IsUpper(r) { @@ -31,7 +106,8 @@ func IsAllUpper(str string) bool { return str != "" } -// IsAllLower check if the string is all lower case letters a-z +// IsAllLower check if the string is all lower case letters a-z. +// Play: https://go.dev/play/p/GjqCnOfV6cM func IsAllLower(str string) bool { for _, r := range str { if !unicode.IsLower(r) { @@ -41,7 +117,33 @@ func IsAllLower(str string) bool { return str != "" } -// ContainUpper check if the string contain at least one upper case letter A-Z +// IsASCII checks if string is all ASCII char. +// Play: https://go.dev/play/p/hfQNPLX0jNa +func IsASCII(str string) bool { + for i := 0; i < len(str); i++ { + if str[i] > unicode.MaxASCII { + return false + } + } + return true +} + +// IsPrintable checks if string is all printable chars. +// Play: https://go.dev/play/p/Pe1FE2gdtTP +func IsPrintable(str string) bool { + for _, r := range str { + if !unicode.IsPrint(r) { + if r == '\n' || r == '\r' || r == '\t' || r == '`' { + continue + } + return false + } + } + return true +} + +// ContainUpper check if the string contain at least one upper case letter A-Z. +// Play: https://go.dev/play/p/CmWeBEk27-z func ContainUpper(str string) bool { for _, r := range str { if unicode.IsUpper(r) && unicode.IsLetter(r) { @@ -51,7 +153,8 @@ func ContainUpper(str string) bool { return false } -// ContainLower check if the string contain at least one lower case letter A-Z +// ContainLower check if the string contain at least one lower case letter a-z. +// Play: https://go.dev/play/p/Srqi1ItvnAA func ContainLower(str string) bool { for _, r := range str { if unicode.IsLower(r) && unicode.IsLetter(r) { @@ -61,62 +164,90 @@ func ContainLower(str string) bool { return false } -var containLetterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`) - -// ContainLetter check if the string contain at least one letter +// ContainLetter check if the string contain at least one letter. +// Play: https://go.dev/play/p/lqFD04Yyewp func ContainLetter(str string) bool { - return containLetterRegexMatcher.MatchString(str) + return letterRegexMatcher.MatchString(str) } -// IsJSON checks if the string is valid JSON +// ContainNumber check if the string contain at least one number. +func ContainNumber(input string) bool { + return numberRegexMatcher.MatchString(input) +} + +// IsJSON checks if the string is valid JSON. +// Play: https://go.dev/play/p/8Kip1Itjiil func IsJSON(str string) bool { var js json.RawMessage return json.Unmarshal([]byte(str), &js) == nil } +// IsAlphaNumeric check if the string is alphanumeric. +// Play: https://go.dev/play/p/RHeESLrLg9c +func IsAlphaNumeric(s string) bool { + return alphaNumericMatcher.MatchString(s) +} + // IsNumberStr check if the string can convert to a number. +// Play: https://go.dev/play/p/LzaKocSV79u func IsNumberStr(s string) bool { return IsIntStr(s) || IsFloatStr(s) } // IsFloatStr check if the string can convert to a float. +// Play: https://go.dev/play/p/LOYwS_Oyl7U func IsFloatStr(str string) bool { _, e := strconv.ParseFloat(str, 64) return e == nil } -var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`) - // IsIntStr check if the string can convert to a integer. +// Play: https://go.dev/play/p/jQRtFv-a0Rk func IsIntStr(str string) bool { - return isIntStrRegexMatcher.MatchString(str) + return intStrMatcher.MatchString(str) } // IsIp check if the string is a ip address. +// Play: https://go.dev/play/p/FgcplDvmxoD func IsIp(ipstr string) bool { ip := net.ParseIP(ipstr) return ip != nil } +// IsIpPort check if the string is ip:port. +// Play: https://go.dev/play/p/xUmls_b9L29 +func IsIpPort(str string) bool { + host, port, err := net.SplitHostPort(str) + if err != nil { + return false + } + + ip := net.ParseIP(host) + return ip != nil && IsPort(port) +} + // IsIpV4 check if the string is a ipv4 address. +// Play: https://go.dev/play/p/zBGT99EjaIu func IsIpV4(ipstr string) bool { ip := net.ParseIP(ipstr) if ip == nil { return false } - return strings.Contains(ipstr, ".") + return ip.To4() != nil } // IsIpV6 check if the string is a ipv6 address. +// Play: https://go.dev/play/p/AHA0r0AzIdC func IsIpV6(ipstr string) bool { ip := net.ParseIP(ipstr) if ip == nil { return false } - return strings.Contains(ipstr, ":") + return ip.To4() == nil && len(ip) == net.IPv6len } // IsPort check if the string is a valid net port. +// Play: func IsPort(str string) bool { if i, err := strconv.ParseInt(str, 10, 64); err == nil && i > 0 && i < 65536 { return true @@ -124,9 +255,8 @@ func IsPort(str string) bool { return false } -var isUrlRegexMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`) - // IsUrl check if the string is url. +// Play: https://go.dev/play/p/pbJGa7F98Ka func IsUrl(str string) bool { if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { return false @@ -142,79 +272,102 @@ func IsUrl(str string) bool { return false } - return isUrlRegexMatcher.MatchString(str) + return urlMatcher.MatchString(str) } -var isDnsRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`) - // IsDns check if the string is dns. +// Play: https://go.dev/play/p/jlYApVLLGTZ func IsDns(dns string) bool { - return isDnsRegexMatcher.MatchString(dns) + return dnsMatcher.MatchString(dns) } -var isEmailRegexMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`) - // IsEmail check if the string is a email address. +// Play: https://go.dev/play/p/Os9VaFlT33G func IsEmail(email string) bool { - return isEmailRegexMatcher.MatchString(email) -} + _, err := mail.ParseAddress(email) + return err == nil -var isChineseMobileRegexMatcher *regexp.Regexp = regexp.MustCompile("^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$") + // return emailMatcher.MatchString(email) +} // IsChineseMobile check if the string is chinese mobile number. +// Play: https://go.dev/play/p/GPYUlGTOqe3 func IsChineseMobile(mobileNum string) bool { - return isChineseMobileRegexMatcher.MatchString(mobileNum) + return chineseMobileMatcher.MatchString(mobileNum) } -var isChineseIdNumRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`) - -// IsChineseIdNum check if the string is chinese id number. +// IsChineseIdNum check if the string is chinese id card. +// Play: https://go.dev/play/p/d8EWhl2UGDF func IsChineseIdNum(id string) bool { - return isChineseIdNumRegexMatcher.MatchString(id) -} + // All characters should be numbers, and the last digit can be either x or X + if !chineseIdMatcher.MatchString(id) { + return false + } + + // Verify province codes and complete all province codes according to GB/T2260 + _, ok := provinceKv[id[0:2]] + if !ok { + return false + } -var containChineseRegexMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]") + // Verify birthday, must be greater than birthStartYear and less than the current year + birthStr := fmt.Sprintf("%s-%s-%s", id[6:10], id[10:12], id[12:14]) + birthday, err := time.Parse("2006-01-02", birthStr) + if err != nil || birthday.After(time.Now()) || birthday.Year() < birthStartYear { + return false + } + + // Verification code + sum := 0 + for i, c := range id[:17] { + v, _ := strconv.Atoi(string(c)) + sum += v * factor[i] + } + + return verifyStr[sum%11] == strings.ToUpper(id[17:18]) +} // ContainChinese check if the string contain mandarin chinese. +// Play: https://go.dev/play/p/7DpU0uElYeM func ContainChinese(s string) bool { - return containChineseRegexMatcher.MatchString(s) + return chineseMatcher.MatchString(s) } -var isChinesePhoneRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`) - // IsChinesePhone check if the string is chinese phone number. -// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx +// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx. +// Play: https://go.dev/play/p/RUD_-7YZJ3I func IsChinesePhone(phone string) bool { - return isChinesePhoneRegexMatcher.MatchString(phone) + return chinesePhoneMatcher.MatchString(phone) } -var isCreditCardRegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`) - // IsCreditCard check if the string is credit card. +// Play: https://go.dev/play/p/sNwwL6B0-v4 func IsCreditCard(creditCart string) bool { - return isCreditCardRegexMatcher.MatchString(creditCart) + return creditCardMatcher.MatchString(creditCart) } -var isBase64RegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`) - // IsBase64 check if the string is base64 string. +// Play: https://go.dev/play/p/sWHEySAt6hl func IsBase64(base64 string) bool { - return isBase64RegexMatcher.MatchString(base64) + return base64Matcher.MatchString(base64) } // IsEmptyString check if the string is empty. +// Play: https://go.dev/play/p/dpzgUjFnBCX func IsEmptyString(str string) bool { return len(str) == 0 } -// IsRegexMatch check if the string match the regexp +// IsRegexMatch check if the string match the regexp. +// Play: https://go.dev/play/p/z_XeZo_litG func IsRegexMatch(str, regex string) bool { reg := regexp.MustCompile(regex) return reg.MatchString(str) } // IsStrongPassword check if the string is strong password, if len(password) is less than the length param, return false -// Strong password: alpha(lower+upper) + number + special chars(!@#$%^&*()?><) +// Strong password: alpha(lower+upper) + number + special chars(!@#$%^&*()?><). +// Play: https://go.dev/play/p/QHdVcSQ3uDg func IsStrongPassword(password string, length int) bool { if len(password) < length { return false @@ -234,15 +387,11 @@ func IsStrongPassword(password string, length int) bool { } return num && lower && upper && special - - // go doesn't support regexp (?=re) - //pattern := `^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&+=])(?=\S+$).$` - //reg := regexp.MustCompile(pattern) - //return reg.MatchString(password) } // IsWeakPassword check if the string is weak password -// Weak password: only letter or only number or letter + number +// Weak password: only letter or only number or letter + number. +// Play: https://go.dev/play/p/wqakscZH5gH func IsWeakPassword(password string) bool { var num, letter, special bool for _, r := range password { @@ -258,3 +407,166 @@ func IsWeakPassword(password string) bool { return (num || letter) && !special } + +// IsZeroValue checks if value is a zero value. +// Play: https://go.dev/play/p/UMrwaDCi_t4 +func IsZeroValue(value any) bool { + if value == nil { + return true + } + + rv := reflect.ValueOf(value) + if rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + + if !rv.IsValid() { + return true + } + + switch rv.Kind() { + case reflect.String: + return rv.Len() == 0 + case reflect.Bool: + return !rv.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0 + case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map: + return rv.IsNil() + } + + return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface()) +} + +// IsGBK check if data encoding is gbk +// Note: this function is implemented by whether double bytes fall within the encoding range of gbk, +// while each byte of utf-8 encoding format falls within the encoding range of gbk. +// Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding, +// and then call IsGBK() to check gbk encoding. like below +/** + data := []byte("你好") + if utf8.Valid(data) { + fmt.Println("data encoding is utf-8") + }else if(IsGBK(data)) { + fmt.Println("data encoding is GBK") + } + fmt.Println("data encoding is unknown") +**/ +// Play: https://go.dev/play/p/E2nt3unlmzP +func IsGBK(data []byte) bool { + i := 0 + for i < len(data) { + if data[i] <= 0xff { + i++ + continue + } else { + if data[i] >= 0x81 && + data[i] <= 0xfe && + data[i+1] >= 0x40 && + data[i+1] <= 0xfe && + data[i+1] != 0xf7 { + i += 2 + continue + } else { + return false + } + } + } + + return true +} + +// IsNumber check if the value is number(integer, float) or not. +// Play: https://go.dev/play/p/mdJHOAvtsvF +func IsNumber(v any) bool { + return IsInt(v) || IsFloat(v) +} + +// IsFloat check if the value is float(float32, float34) or not. +// Play: https://go.dev/play/p/vsyG-sxr99_Z +func IsFloat(v any) bool { + switch v.(type) { + case float32, float64: + return true + } + return false +} + +// IsInt check if the value is integer(int, unit) or not. +// Play: https://go.dev/play/p/eFoIHbgzl-z +func IsInt(v any) bool { + switch v.(type) { + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr: + return true + } + return false +} + +// IsBin check if a give string is a valid binary value or not. +// Play: https://go.dev/play/p/ogPeg2XJH4P +func IsBin(v string) bool { + return binMatcher.MatchString(v) +} + +// IsHex check if a give string is a valid hexadecimal value or not. +// Play: https://go.dev/play/p/M2qpHbEwmm7 +func IsHex(v string) bool { + return hexMatcher.MatchString(v) +} + +// IsBase64URL check if a give string is a valid URL-safe Base64 encoded string. +// Play: https://go.dev/play/p/vhl4mr8GZ6S +func IsBase64URL(v string) bool { + return base64URLMatcher.MatchString(v) +} + +// IsJWT check if a give string is a valid JSON Web Token (JWT). +// Play: https://go.dev/play/p/R6Op7heJbKI +func IsJWT(v string) bool { + strings := strings.Split(v, ".") + if len(strings) != 3 { + return false + } + + for _, s := range strings { + if !IsBase64URL(s) { + return false + } + } + + return true +} + +// IsVisa check if a give string is a valid visa card nubmer or not. +// Play: https://go.dev/play/p/SdS2keOyJsl +func IsVisa(v string) bool { + return visaMatcher.MatchString(v) +} + +// IsMasterCard check if a give string is a valid master card nubmer or not. +// Play: https://go.dev/play/p/CwWBFRrG27b +func IsMasterCard(v string) bool { + return masterCardMatcher.MatchString(v) +} + +// IsAmericanExpress check if a give string is a valid american expression card nubmer or not. +// Play: https://go.dev/play/p/HIDFpcOdpkd +func IsAmericanExpress(v string) bool { + return americanExpressMatcher.MatchString(v) +} + +// IsUnionPay check if a give string is a valid union pay nubmer or not. +// Play: https://go.dev/play/p/CUHPEwEITDf +func IsUnionPay(v string) bool { + return unionPay.MatchString(v) +} + +// IsChinaUnionPay check if a give string is a valid china union pay nubmer or not. +// Play: https://go.dev/play/p/yafpdxLiymu +func IsChinaUnionPay(v string) bool { + return chinaUnionPay.MatchString(v) +} diff --git a/validator/validator_example_test.go b/validator/validator_example_test.go new file mode 100644 index 00000000..088cb86d --- /dev/null +++ b/validator/validator_example_test.go @@ -0,0 +1,685 @@ +package validator + +import ( + "fmt" + + "golang.org/x/text/encoding/simplifiedchinese" +) + +func ExampleContainChinese() { + result1 := ContainChinese("你好") + result2 := ContainChinese("你好hello") + result3 := ContainChinese("hello") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleContainLetter() { + result1 := ContainLetter("你好") + result2 := ContainLetter("&@#$%^&*") + result3 := ContainLetter("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // false + // false + // true +} + +func ExampleContainNumber() { + result1 := ContainNumber("你好") + result2 := ContainNumber("&@#$%^&*") + result3 := ContainNumber("ab1") + result4 := ContainNumber("1234") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // true + // true +} + +func ExampleContainLower() { + result1 := ContainLower("abc") + result2 := ContainLower("aBC") + result3 := ContainLower("ABC") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleContainUpper() { + result1 := ContainUpper("ABC") + result2 := ContainUpper("abC") + result3 := ContainUpper("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleIsAlpha() { + result1 := IsAlpha("abc") + result2 := IsAlpha("ab1") + result3 := IsAlpha("") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsAllUpper() { + result1 := IsAllUpper("ABC") + result2 := IsAllUpper("ABc") + result3 := IsAllUpper("AB1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsAllLower() { + result1 := IsAllLower("abc") + result2 := IsAllLower("abC") + result3 := IsAllLower("ab1") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsBase64() { + result1 := IsBase64("aGVsbG8=") + result2 := IsBase64("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsChineseMobile() { + result1 := IsChineseMobile("13263527980") + result2 := IsChineseMobile("434324324") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsChineseIdNum() { + result1 := IsChineseIdNum("210911192105130714") + result2 := IsChineseIdNum("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsChinesePhone() { + result1 := IsChinesePhone("010-32116675") + result2 := IsChinesePhone("123-87562") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsCreditCard() { + result1 := IsCreditCard("4111111111111111") + result2 := IsCreditCard("123456") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsDns() { + result1 := IsDns("abc.com") + result2 := IsDns("a.b.com") + result3 := IsDns("http://abc.com") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleIsUrl() { + result1 := IsUrl("abc.com") + result2 := IsUrl("http://abc.com") + result3 := IsUrl("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // true + // false +} + +func ExampleIsEmail() { + result1 := IsEmail("abc@xyz.com") + result2 := IsEmail("a.b@@com") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsEmptyString() { + result1 := IsEmptyString("") + result2 := IsEmptyString(" ") + result3 := IsEmptyString("\t") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + + // Output: + // true + // false + // false +} + +func ExampleIsFloatStr() { + result1 := IsFloatStr("3.") + result2 := IsFloatStr("+3.") + result3 := IsFloatStr("12") + result4 := IsFloatStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} + +func ExampleIsNumberStr() { + result1 := IsNumberStr("3.") + result2 := IsNumberStr("+3.") + result3 := IsNumberStr("+3e2") + result4 := IsNumberStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} + +func ExampleIsIntStr() { + result1 := IsIntStr("+3") + result2 := IsIntStr("-3") + result3 := IsIntStr("3.") + result4 := IsIntStr("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsJSON() { + result1 := IsJSON("{}") + result2 := IsJSON("{\"name\": \"test\"}") + result3 := IsJSON("") + result4 := IsJSON("abc") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsRegexMatch() { + result1 := IsRegexMatch("abc", `^[a-zA-Z]+$`) + result2 := IsRegexMatch("ab1", `^[a-zA-Z]+$`) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsIp() { + result1 := IsIp("127.0.0.1") + result2 := IsIp("::0:0:0:0:0:0:1") + result3 := IsIp("127.0.0") + result4 := IsIp("::0:0:0:0:") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsIpPort() { + result1 := IsIpPort("127.0.0.1:8080") + result2 := IsIpPort("[0:0:0:0:0:0:0:1]:8080") + result3 := IsIpPort(":8080") + result4 := IsIpPort("::0:0:0:0:") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsIpV4() { + result1 := IsIpV4("127.0.0.1") + result2 := IsIpV4("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsIpV6() { + result1 := IsIpV6("127.0.0.1") + result2 := IsIpV6("::0:0:0:0:0:0:1") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} + +func ExampleIsStrongPassword() { + result1 := IsStrongPassword("abcABC", 6) + result2 := IsStrongPassword("abcABC123@#$", 10) + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // false + // true +} + +func ExampleIsWeakPassword() { + result1 := IsWeakPassword("abcABC") + result2 := IsWeakPassword("abc123@#$") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsZeroValue() { + result1 := IsZeroValue("") + result2 := IsZeroValue(0) + result3 := IsZeroValue("abc") + result4 := IsZeroValue(1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsGBK() { + str := "你好" + gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str)) + + result := IsGBK(gbkData) + + fmt.Println(result) + + // Output: + // true +} + +func ExampleIsASCII() { + result1 := IsASCII("ABC") + result2 := IsASCII("123") + result3 := IsASCII("") + result4 := IsASCII("😄") + result5 := IsASCII("你好") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // false + // false +} + +func ExampleIsPrintable() { + result1 := IsPrintable("ABC") + result2 := IsPrintable("{id: 123}") + result3 := IsPrintable("") + result4 := IsPrintable("😄") + result5 := IsPrintable("\u0000") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + fmt.Println(result5) + + // Output: + // true + // true + // true + // true + // false +} + +func ExampleIsInt() { + result1 := IsInt("") + result2 := IsInt("3") + result3 := IsInt(0.1) + result4 := IsInt(0) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} + +func ExampleIsFloat() { + result1 := IsFloat("") + result2 := IsFloat("3") + result3 := IsFloat(0) + result4 := IsFloat(0.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // false + // true +} + +func ExampleIsNumber() { + result1 := IsNumber("") + result2 := IsNumber("3") + result3 := IsNumber(0) + result4 := IsNumber(0.1) + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // false + // false + // true + // true +} + +func ExampleIsBin() { + result1 := IsBin("0101") + result2 := IsBin("0b1101") + result3 := IsBin("b1101") + result4 := IsBin("1201") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsHex() { + result1 := IsHex("0xabcde") + result2 := IsHex("0XABCDE") + result3 := IsHex("cdfeg") + result4 := IsHex("0xcdfeg") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsBase64URL() { + result1 := IsBase64URL("SAGsbG8sIHdvcmxkIQ") + result2 := IsBase64URL("SAGsbG8sIHdvcmxkIQ==") + result3 := IsBase64URL("SAGsbG8sIHdvcmxkIQ=") + result4 := IsBase64URL("SAGsbG8sIHdvcmxkIQ===") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // false + // false +} + +func ExampleIsJWT() { + result1 := IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910") + result2 := IsJWT("abc") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsVisa() { + result1 := IsVisa("4111111111111111") + result2 := IsVisa("123") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsMasterCard() { + result1 := IsMasterCard("5425233430109903") + result2 := IsMasterCard("4111111111111111") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsAmericanExpress() { + result1 := IsAmericanExpress("342883359122187") + result2 := IsAmericanExpress("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsUnionPay() { + result1 := IsUnionPay("6221263430109903") + result2 := IsUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsChinaUnionPay() { + result1 := IsChinaUnionPay("6250941006528599") + result2 := IsChinaUnionPay("3782822463100007") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleIsAlphaNumeric() { + result1 := IsAlphaNumeric("ABC") + result2 := IsAlphaNumeric("123") + result3 := IsAlphaNumeric("abc123") + result4 := IsAlphaNumeric("abc123@#$") + + fmt.Println(result1) + fmt.Println(result2) + fmt.Println(result3) + fmt.Println(result4) + + // Output: + // true + // true + // true + // false +} diff --git a/validator/validator_test.go b/validator/validator_test.go index f9f41f92..08420974 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -1,166 +1,440 @@ package validator import ( + "fmt" "testing" + "time" + "unicode/utf8" - "github.com/duke-git/lancet/internal" + "github.com/duke-git/lancet/v2/internal" + "golang.org/x/text/encoding/simplifiedchinese" ) func TestIsAllUpper(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsAllUpper") - assert.Equal(true, IsAllUpper("ABC")) - assert.Equal(false, IsAllUpper("")) - assert.Equal(false, IsAllUpper("abc")) - assert.Equal(false, IsAllUpper("aBC")) - assert.Equal(false, IsAllUpper("1BC")) - assert.Equal(false, IsAllUpper("1bc")) - assert.Equal(false, IsAllUpper("123")) - assert.Equal(false, IsAllUpper("你好")) - assert.Equal(false, IsAllUpper("A&")) - assert.Equal(false, IsAllUpper("&@#$%^&*")) + tests := []struct { + input string + expected bool + }{ + {"ABC", true}, + {"", false}, + {"abc", false}, + {"aBC", false}, + {"1BC", false}, + {"1bc", false}, + {"123", false}, + {"你好", false}, + {"A&", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsAllUpper(tt.input)) + } } func TestIsAllLower(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsAllLower") - assert.Equal(true, IsAllLower("abc")) - assert.Equal(false, IsAllLower("ABC")) - assert.Equal(false, IsAllLower("")) - assert.Equal(false, IsAllLower("aBC")) - assert.Equal(false, IsAllLower("1BC")) - assert.Equal(false, IsAllLower("1bc")) - assert.Equal(false, IsAllLower("123")) - assert.Equal(false, IsAllLower("你好")) - assert.Equal(false, IsAllLower("A&")) - assert.Equal(false, IsAllLower("&@#$%^&*")) + tests := []struct { + input string + expected bool + }{ + {"abc", true}, + {"", false}, + {"ABC", false}, + {"aBC", false}, + {"1BC", false}, + {"1bc", false}, + {"123", false}, + {"你好", false}, + {"A&", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsAllLower(tt.input)) + } } func TestContainLower(t *testing.T) { - assert := internal.NewAssert(t, "TestContainLower") + t.Parallel() - assert.Equal(true, ContainLower("abc")) - assert.Equal(true, ContainLower("aBC")) - assert.Equal(true, ContainLower("1bc")) - assert.Equal(true, ContainLower("a&")) + assert := internal.NewAssert(t, "TestContainLower") - assert.Equal(false, ContainLower("ABC")) - assert.Equal(false, ContainLower("")) - assert.Equal(false, ContainLower("1BC")) - assert.Equal(false, ContainLower("123")) - assert.Equal(false, ContainLower("你好")) - assert.Equal(false, ContainLower("&@#$%^&*")) + tests := []struct { + input string + expected bool + }{ + {"abc", true}, + {"aBC", true}, + {"1bc", true}, + {"a&", true}, + {"ABC", false}, + {"", false}, + {"1BC", false}, + {"123", false}, + {"你好", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, ContainLower(tt.input)) + } } func TestContainUpper(t *testing.T) { - assert := internal.NewAssert(t, "TestContainUpper") + t.Parallel() - assert.Equal(true, ContainUpper("ABC")) - assert.Equal(true, ContainUpper("aBC")) - assert.Equal(true, ContainUpper("1BC")) - assert.Equal(true, ContainUpper("A&")) + assert := internal.NewAssert(t, "TestContainUpper") - assert.Equal(false, ContainUpper("abc")) - assert.Equal(false, ContainUpper("")) - assert.Equal(false, ContainUpper("1bc")) - assert.Equal(false, ContainUpper("123")) - assert.Equal(false, ContainUpper("你好")) - assert.Equal(false, ContainUpper("&@#$%^&*")) + tests := []struct { + input string + expected bool + }{ + {"ABC", true}, + {"aBC", true}, + {"1BC", true}, + {"A&", true}, + {"abc", false}, + {"", false}, + {"1bc", false}, + {"123", false}, + {"你好", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, ContainUpper(tt.input)) + } } func TestContainLetter(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestContainLetter") - assert.Equal(true, ContainLetter("ABC")) - assert.Equal(true, ContainLetter("1Bc")) - assert.Equal(true, ContainLetter("1ab")) - assert.Equal(true, ContainLetter("A&")) + tests := []struct { + input string + expected bool + }{ + {"ABC", true}, + {"1Bc", true}, + {"1ab", true}, + {"A&", true}, + {"", false}, + {"123", false}, + {"你好", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, ContainLetter(tt.input)) + } +} - assert.Equal(false, ContainLetter("")) - assert.Equal(false, ContainLetter("123")) - assert.Equal(false, ContainLetter("你好")) - assert.Equal(false, ContainLetter("&@#$%^&*")) +func TestContainNumber(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestContainNumber") + + tests := []struct { + input string + expected bool + }{ + {"123", true}, + {"1Bc", true}, + {"a2c", true}, + {"ab3", true}, + {"a23", true}, + {"a23c", true}, + {"1%%%", true}, + {"ABC", false}, + {"", false}, + {"你好", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, ContainNumber(tt.input)) + } } func TestIsJSON(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsJSON") - assert.Equal(true, IsJSON("{}")) - assert.Equal(true, IsJSON("{\"name\": \"test\"}")) - assert.Equal(true, IsJSON("[]")) - assert.Equal(true, IsJSON("123")) + tests := []struct { + input string + expected bool + }{ + {"{}", true}, + {"{\"name\": \"test\"}", true}, + {"[]", true}, + {"123", true}, + {"", false}, + {"abc", false}, + {"你好", false}, + {"&@#$%^&*", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsJSON(tt.input)) + } +} + +func TestIsNumber(t *testing.T) { + t.Parallel() - assert.Equal(false, IsJSON("")) - assert.Equal(false, IsJSON("abc")) - assert.Equal(false, IsJSON("你好")) - assert.Equal(false, IsJSON("&@#$%^&*")) + assert := internal.NewAssert(t, "TestIsNumber") + + tests := []struct { + input interface{} + expected bool + }{ + {"", false}, + {"3", false}, + {0, true}, + {0.1, true}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsNumber(tt.input)) + } +} + +func TestIsFloat(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsFloat") + + tests := []struct { + input interface{} + expected bool + }{ + {"", false}, + {"3", false}, + {0, false}, + {0.1, true}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsFloat(tt.input)) + } +} + +func TestIsInt(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsInt") + + tests := []struct { + input interface{} + expected bool + }{ + {"", false}, + {"3", false}, + {0.1, false}, + {0, true}, + {-1, true}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsInt(tt.input)) + } } func TestIsNumberStr(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsNumberStr") - assert.Equal(true, IsNumberStr("3.")) - assert.Equal(true, IsNumberStr("+3.")) - assert.Equal(true, IsNumberStr("-3.")) - assert.Equal(true, IsNumberStr("+3e2")) - assert.Equal(false, IsNumberStr("abc")) + tests := []struct { + input string + expected bool + }{ + {"3", true}, + {"3.", true}, + {"+3.", true}, + {"-3.", true}, + {"+3e2", true}, + {"abc", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsNumberStr(tt.input)) + } } func TestIsFloatStr(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsFloatStr") - assert.Equal(true, IsFloatStr("3.")) - assert.Equal(true, IsFloatStr("+3.")) - assert.Equal(true, IsFloatStr("-3.")) - assert.Equal(true, IsFloatStr("12")) - assert.Equal(false, IsFloatStr("abc")) + tests := []struct { + input string + expected bool + }{ + {"3", true}, + {"3.", true}, + {"+3.", true}, + {"-3.", true}, + {"12", true}, + {"abc", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsFloatStr(tt.input)) + } } func TestIsIntStr(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsIntStr") - assert.Equal(true, IsIntStr("+3")) - assert.Equal(true, IsIntStr("-3")) - assert.Equal(false, IsIntStr("3.")) - assert.Equal(false, IsIntStr("abc")) + tests := []struct { + input string + expected bool + }{ + {"3", true}, + {"3.", false}, + {"+3", true}, + {"-3", true}, + {"abc", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsIntStr(tt.input)) + } } func TestIsPort(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsPort") - assert.Equal(true, IsPort("1")) - assert.Equal(true, IsPort("65535")) - assert.Equal(false, IsPort("abc")) - assert.Equal(false, IsPort("123abc")) - assert.Equal(false, IsPort("")) - assert.Equal(false, IsPort("-1")) - assert.Equal(false, IsPort("65536")) + tests := []struct { + input string + expected bool + }{ + {"1", true}, + {"65535", true}, + {"abc", false}, + {"123abc", false}, + {"", false}, + {"-1", false}, + {"65536", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsPort(tt.input)) + } } func TestIsIp(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsIntStr") - assert.Equal(true, IsIp("127.0.0.1")) - assert.Equal(true, IsIp("::0:0:0:0:0:0:1")) - assert.Equal(false, IsIp("127.0.0")) - assert.Equal(false, IsIp("127")) + tests := []struct { + input string + expected bool + }{ + {"127.0.0.1", true}, + {"::0:0:0:0:0:0:1", true}, + {"127.0.0", false}, + {"127", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsIp(tt.input)) + } +} + +func TestIsIpPort(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsIpPort") + + tests := []struct { + input string + expected bool + }{ + {"[::0:0:0:0:0:0:1]:8080", true}, + {"127.0.0.1:8080", true}, + + {"", false}, + {":8080", false}, + {"127.0.0.1", false}, + {"0:0:0:0:0:0:0:1", false}, + {"256.256.256.256:8080", false}, + {"256.256.256.256:abc", false}, + {"127.0.0.1:70000", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsIpPort(tt.input)) + } } func TestIsIpV4(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsIpV4") - assert.Equal(true, IsIpV4("127.0.0.1")) - assert.Equal(false, IsIpV4("::0:0:0:0:0:0:1")) + tests := []struct { + input string + expected bool + }{ + {"127.0.0.1", true}, + {"::0:0:0:0:0:0:1", false}, + + {"::0:0:0:0:0:0:1", false}, + {"127.0.0.1.1", false}, + {"256.0.0.1", false}, + {"127.0.0.a", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsIpV4(tt.input)) + } } func TestIsIpV6(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsIpV6") - assert.Equal(false, IsIpV6("127.0.0.1")) - assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1")) + tests := []struct { + input string + expected bool + }{ + {"::0:0:0:0:0:0:1", true}, + {"::1", true}, + {"::", true}, + {"127.0.0.1", false}, + {"2001:db8::8a2e:37023:7334", false}, + {"2001::25de::cade", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsIpV6(tt.input)) + } + } func TestIsUrl(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsUrl") assert.Equal(true, IsUrl("http://abc.com")) @@ -170,21 +444,40 @@ func TestIsUrl(t *testing.T) { } func TestIsDns(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsDns") - assert.Equal(true, IsDns("abc.com")) - assert.Equal(false, IsDns("a.b.com")) - assert.Equal(false, IsDns("http://abc.com")) + tests := []struct { + input string + expected bool + }{ + {"abc.com", true}, + {"123.cn", true}, + {"a.b.com", true}, + {"a.b.c", false}, + {"a@b.com", false}, + {"http://abc.com", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsDns(tt.input)) + } } func TestIsEmail(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsEmail") assert.Equal(true, IsEmail("abc@xyz.com")) + assert.Equal(false, IsEmail("@abc@xyz.com")) assert.Equal(false, IsEmail("a.b@@com")) } func TestContainChinese(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestContainChinese") assert.Equal(true, ContainChinese("你好")) @@ -193,6 +486,8 @@ func TestContainChinese(t *testing.T) { } func TestIsChineseMobile(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsChineseMobile") assert.Equal(true, IsChineseMobile("13263527980")) @@ -200,24 +495,52 @@ func TestIsChineseMobile(t *testing.T) { } func TestIsChinesePhone(t *testing.T) { - assert := internal.NewAssert(t, "TestIsChinesePhone") + t.Parallel() - assert.Equal(true, IsChinesePhone("010-32116675")) - assert.Equal(true, IsChinesePhone("0464-8756213")) - assert.Equal(false, IsChinesePhone("123-87562")) + assert := internal.NewAssert(t, "TestIsChinesePhone") + tests := []struct { + input string + expected bool + }{ + {"010-32116675", true}, + {"0464-8756213", true}, + {"0731-82251545", true}, + {"123-87562", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsChinesePhone(tt.input)) + } } func TestIsChineseIdNum(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsChineseIdNum") - assert.Equal(true, IsChineseIdNum("210911192105130715")) - assert.Equal(true, IsChineseIdNum("21091119210513071X")) - assert.Equal(true, IsChineseIdNum("21091119210513071x")) - assert.Equal(false, IsChineseIdNum("123456")) + tests := []struct { + input string + expected bool + }{ + {"210911192105130714", true}, + {"11010519491231002X", true}, + {"11010519491231002x", true}, + {"123456", false}, + {"990911192105130714", false}, + {"990911189905130714", false}, + {"210911222205130714", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsChineseIdNum(tt.input)) + } } func TestIsCreditCard(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsCreditCard") assert.Equal(true, IsCreditCard("4111111111111111")) @@ -225,6 +548,8 @@ func TestIsCreditCard(t *testing.T) { } func TestIsBase64(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsBase64") assert.Equal(true, IsBase64("aGVsbG8=")) @@ -232,6 +557,8 @@ func TestIsBase64(t *testing.T) { } func TestIsEmptyString(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsEmptyString") assert.Equal(true, IsEmptyString("")) @@ -241,6 +568,8 @@ func TestIsEmptyString(t *testing.T) { } func TestIsAlpha(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsAlpha") assert.Equal(true, IsAlpha("abc")) @@ -251,6 +580,8 @@ func TestIsAlpha(t *testing.T) { } func TestIsRegexMatch(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsRegexMatch") assert.Equal(true, IsRegexMatch("abc", `^[a-zA-Z]+$`)) @@ -259,23 +590,337 @@ func TestIsRegexMatch(t *testing.T) { } func TestIsStrongPassword(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsStrongPassword") - assert.Equal(false, IsStrongPassword("abc", 3)) - assert.Equal(false, IsStrongPassword("abc123", 6)) - assert.Equal(false, IsStrongPassword("abcABC", 6)) - assert.Equal(false, IsStrongPassword("abc123@#$", 9)) - assert.Equal(false, IsStrongPassword("abcABC123@#$", 16)) - assert.Equal(true, IsStrongPassword("abcABC123@#$", 12)) - assert.Equal(true, IsStrongPassword("abcABC123@#$", 10)) + tests := []struct { + input string + length int + expected bool + }{ + {"abc", 3, false}, + {"abc123", 6, false}, + {"abcABC", 6, false}, + {"abc123@#$", 9, false}, + {"abcABC123@#$", 16, false}, + {"abcABC123@#$", 12, true}, + {"abcABC123@#$", 10, true}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsStrongPassword(tt.input, tt.length)) + } } func TestIsWeakPassword(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsWeakPassword") - assert.Equal(true, IsWeakPassword("abc")) - assert.Equal(true, IsWeakPassword("123")) - assert.Equal(true, IsWeakPassword("abc123")) - assert.Equal(true, IsWeakPassword("abcABC123")) - assert.Equal(false, IsWeakPassword("abc123@#$")) + tests := []struct { + input string + expected bool + }{ + {"abc", true}, + {"123", true}, + {"abc123", true}, + {"abcABC123", true}, + {"abc123@#$", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsWeakPassword(tt.input)) + } +} + +func TestIsZeroValue(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsZeroValue") + + var ( + zeroPtr *string + zeroSlice []int + zeroFunc func() string + zeroMap map[string]string + nilIface interface{} + zeroIface fmt.Formatter + ) + zeroValues := []interface{}{ + nil, + false, + 0, + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + + 0.0, + float32(0.0), + float64(0.0), + + "", + + // func + zeroFunc, + + // array / slice + [0]int{}, + zeroSlice, + + // map + zeroMap, + + // interface + nilIface, + zeroIface, + + // pointer + zeroPtr, + + // struct + time.Time{}, + } + + for _, value := range zeroValues { + assert.Equal(true, IsZeroValue(value)) + } + + var nonZeroIface fmt.Stringer = time.Now() + + nonZeroValues := []interface{}{ + // bool + true, + + // int + 1, + int8(1), + int16(1), + int32(1), + int64(1), + uint8(1), + uint16(1), + uint32(1), + uint64(1), + + // float + 1.0, + float32(1.0), + float64(1.0), + + // string + "test", + + // func + time.Now, + + // array / slice + []int{}, + []int{42}, + [1]int{42}, + + // map + make(map[string]string, 1), + + // interface + nonZeroIface, + + // pointer + &nonZeroIface, + + // struct + time.Now(), + } + + for _, value := range nonZeroValues { + assert.Equal(false, IsZeroValue(value)) + } +} + +func TestIsGBK(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsGBK") + + str := "你好" + gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str)) + + assert.Equal(true, IsGBK(gbkData)) + assert.Equal(false, utf8.Valid(gbkData)) +} + +func TestIsASCII(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsASCII") + + assert.Equal(true, IsASCII("ABC")) + assert.Equal(true, IsASCII("123")) + assert.Equal(true, IsASCII("")) + assert.Equal(false, IsASCII("😄")) + assert.Equal(false, IsASCII("你好")) +} + +func TestIsPrintable(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsPrintable") + + tests := []struct { + input string + expected bool + }{ + {"ABC", true}, + {"123", true}, + {"你好", true}, + {"", true}, + {"😄", true}, + {"\u0000", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsPrintable(tt.input)) + } +} + +func TestIsBin(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsBin") + + tests := []struct { + input string + expected bool + }{ + {"0101", true}, + {"0b1101", true}, + {"b1101", false}, + {"1201", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsBin(tt.input)) + } +} + +func TestIsHex(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsHex") + + tests := []struct { + input string + expected bool + }{ + {"ABCDE", true}, + {"abcde", true}, + {"0xabcde", true}, + {"0Xabcde", true}, + {"#abcde", true}, + {"cdfeg", false}, + {"0xcdfeg", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsHex(tt.input)) + } +} + +func TestIsBase64URL(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsBase64URL") + + tests := []struct { + input string + expected bool + }{ + {"SAGsbG8sIHdvcmxkIQ", true}, + {"SAGsbG8sIHdvcmxkIQ==", true}, + {"SAGsbG8sIHdvcmxkIQ=", false}, + {"SAGsbG8sIHdvcmxkIQ===", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsBase64URL(tt.input)) + } +} + +func TestIsJWT(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsJWT") + + assert.Equal(true, IsJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibWVzc2FnZSI6IlB1dGluIGlzIGFic29sdXRlIHNoaXQiLCJpYXQiOjE1MTYyMzkwMjJ9.wkLWA5GtCpWdxNOrRse8yHZgORDgf8TpJp73WUQb910")) + assert.Equal(false, IsJWT("abc")) +} + +func TestIsVisa(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsVisa") + + assert.Equal(true, IsVisa("4111111111111111")) + assert.Equal(false, IsVisa("123")) +} + +func TestIsMasterCard(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsMasterCard") + + assert.Equal(true, IsMasterCard("5425233430109903")) + assert.Equal(false, IsMasterCard("4111111111111111")) +} + +func TestIsAmericanExpress(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsAmericanExpress") + + assert.Equal(true, IsAmericanExpress("342883359122187")) + assert.Equal(false, IsAmericanExpress("3782822463100007")) +} + +func TestIsUnionPay(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsUnionPay") + + assert.Equal(true, IsUnionPay("6221263430109903")) + assert.Equal(false, IsUnionPay("3782822463100007")) +} + +func TestIsChinaUnionPay(t *testing.T) { + t.Parallel() + assert := internal.NewAssert(t, "TestIsChinaUnionPay") + + assert.Equal(true, IsChinaUnionPay("6250941006528599")) + assert.Equal(false, IsChinaUnionPay("3782822463100007")) +} + +func TestIsAlphaNumeric(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestIsAlphaNumeric") + + tests := []struct { + input string + expected bool + }{ + {"ABC", true}, + {"abc", true}, + {"aBC", true}, + {"1BC", true}, + {"1bc", true}, + {"123", true}, + {"你好", false}, + {"A&", false}, + {"&@#$%^&*", false}, + {"", false}, + } + + for _, tt := range tests { + assert.Equal(tt.expected, IsAlphaNumeric(tt.input)) + } } diff --git a/xerror/stack.go b/xerror/stack.go new file mode 100644 index 00000000..f62aaacc --- /dev/null +++ b/xerror/stack.go @@ -0,0 +1,228 @@ +// Copyright 2023 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +package xerror + +import ( + "fmt" + "io" + "path" + "runtime" + "strconv" + "strings" +) + +// Stack contains function, file and line number info in the stack trace. +type Stack struct { + Func string `json:"func"` + File string `json:"file"` + Line int `json:"line"` +} + +// Stacks returns stack trace array generated by pkg/errors +func (e *XError) Stacks() []*Stack { + resp := make([]*Stack, len(*e.stack)) + for i, st := range *e.stack { + f := frame(st) + resp[i] = &Stack{ + Func: f.name(), + File: f.file(), + Line: f.line(), + } + } + return resp +} + +// StackTrace returns stack trace which is compatible with pkg/errors +func (e *XError) StackTrace() StackTrace { + return e.stack.StackTrace() +} + +// --------------------------------------- +// Stacktrace part is implemented based on copy of https://github.com/pkg/errors +// +// Copyright (c) 2015, Dave Cheney +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +type frame uintptr +type stack []uintptr + +// StackTrace is array of frame. It's exported for compatibility with github.com/pkg/errors +type StackTrace []frame + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// name returns the name of this function, if known. +func (f frame) name() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + return fn.Name() +} + +// Format of frame formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + _, _ = io.WriteString(s, f.name()) + _, _ = io.WriteString(s, "\n\t") + _, _ = io.WriteString(s, f.file()) + default: + _, _ = io.WriteString(s, path.Base(f.file())) + } + case 'd': + _, _ = io.WriteString(s, strconv.Itoa(f.line())) + case 'n': + _, _ = io.WriteString(s, funcname(f.name())) + case 'v': + f.Format(s, 's') + _, _ = io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// MarshalText formats a stacktrace Frame as a text string. The output is the +// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. +func (f frame) MarshalText() ([]byte, error) { + name := f.name() + if name == "unknown" { + return []byte(name), nil + } + return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil +} + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + _, _ = io.WriteString(s, "\n") + f.Format(s, verb) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []frame(st)) + default: + st.formatSlice(s, verb) + } + case 's': + st.formatSlice(s, verb) + } +} + +// formatSlice will format this StackTrace into the given buffer as a slice of +// Frame, only valid when called with '%s' or '%v'. +func (st StackTrace) formatSlice(s fmt.State, verb rune) { + _, _ = io.WriteString(s, "[") + for i, f := range st { + if i > 0 { + _, _ = io.WriteString(s, " ") + } + f.Format(s, verb) + } + _, _ = io.WriteString(s, "]") +} + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + frames := make([]frame, len(*s)) + for i := 0; i < len(frames); i++ { + frames[i] = frame((*s)[i]) + } + return frames +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(4, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/xerror/trycatch.go b/xerror/trycatch.go new file mode 100644 index 00000000..e6885846 --- /dev/null +++ b/xerror/trycatch.go @@ -0,0 +1,100 @@ +package xerror + +import ( + "context" + "fmt" + "runtime" +) + +// TryCatch is a struct to handle try-catch-finally block. +// This implementation is merely a simulation of Java-style try-catch and does not align with Go's error-handling philosophy. It is recommended to use it with caution. +type TryCatch struct { + ctx context.Context + tryFunc func(ctx context.Context) error + catchFunc func(ctx context.Context, err error) + finallyFunc func(ctx context.Context) +} + +// NewTryCatch creates a new TryCatch instance. +func NewTryCatch(ctx context.Context) *TryCatch { + return &TryCatch{ + ctx: ctx, + } +} + +// Try sets the try function. +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch { + tc.tryFunc = tryFunc + return tc +} + +// Catch sets the catch function. +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch { + tc.catchFunc = catchFunc + return tc +} + +// Finally sets the finally function. +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch { + tc.finallyFunc = finallyFunc + return tc +} + +// Do executes the try-catch-finally block. +func (tc *TryCatch) Do() { + defer func() { + if r := recover(); r != nil { + if tc.catchFunc != nil { + err := fmt.Errorf("panic: %v", r) + tc.catchFunc(tc.ctx, WrapCatchError(err, "Recovered from panic")) + } + } + + if tc.finallyFunc != nil { + tc.finallyFunc(tc.ctx) + } + }() + + if tc.ctx.Err() != nil { + if tc.catchFunc != nil { + tc.catchFunc(tc.ctx, WrapCatchError(tc.ctx.Err(), "Context cancelled or timed out")) + } + return + } + + if tc.tryFunc != nil { + if err := tc.tryFunc(tc.ctx); err != nil { + if tc.catchFunc != nil { + tc.catchFunc(tc.ctx, WrapCatchError(err, "Error in try block")) + } + } + } +} + +// CatchError is an error type to handle try-catch-finally block. +type CatchError struct { + Msg string + File string + Line int + Cause error +} + +// Error returns the error message. +func (e *CatchError) Error() string { + return fmt.Sprintf("%s at %s:%d - Cause: %v", e.Msg, e.File, e.Line, e.Cause) +} + +// WrapCatchError wraps an error with message, file, and line. +func WrapCatchError(err error, msg string) *CatchError { + _, file, line, ok := runtime.Caller(2) + if !ok { + file = "unknown" + line = 0 + } + return &CatchError{ + Msg: msg, + File: file, + Line: line, + Cause: err, + } +} diff --git a/xerror/trycatch_test.go b/xerror/trycatch_test.go new file mode 100644 index 00000000..08e7a785 --- /dev/null +++ b/xerror/trycatch_test.go @@ -0,0 +1,172 @@ +package xerror + +import ( + "context" + "errors" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestTryCatchSuccess(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchSuccess") + + counter := 0 + calledCatch := false + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + counter++ + return nil + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + t.Errorf("Catch should not be called") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal(1, counter) + assert.Equal(false, calledCatch) + assert.Equal(true, calledFinally) +} + +func TestTryCatchError(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchError") + + var catchedError error + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchPanic(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchPanic") + + var catchedError error + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + panic("panic info") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextCancelled(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextCancelled") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return nil + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextTimeout(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextTimeout") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithTimeout(context.Background(), 0) + defer cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return nil + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextError(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextError") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithTimeout(context.Background(), 0) + defer cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchNoCatch(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchNoCatch") + + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal(true, calledFinally) +} diff --git a/xerror/xerror.go b/xerror/xerror.go new file mode 100644 index 00000000..856dc13b --- /dev/null +++ b/xerror/xerror.go @@ -0,0 +1,207 @@ +// Copyright 2021 dudaodong@gmail.com. All rights reserved. +// Use of this source code is governed by MIT license + +// Package xerror implements helpers for errors +package xerror + +import ( + "errors" + "fmt" + "io" + "strings" + + "github.com/duke-git/lancet/v2/random" +) + +// XError is to handle error related information. +type XError struct { + id string + message string + stack *stack + cause error + values map[string]any +} + +// New creates a new XError with message +func New(format string, args ...any) *XError { + err := newXError() + err.message = fmt.Sprintf(format, args...) + return err +} + +// Wrap creates a new XError and add message. +func Wrap(cause error, message ...any) *XError { + err := newXError() + + if len(message) > 0 { + newMsgs := make([]string, 0, len(message)) + for _, m := range message { + newMsgs = append(newMsgs, fmt.Sprintf("%v", m)) + } + err.message = strings.Join(newMsgs, " ") + } + + err.cause = cause + + return err +} + +// Unwrap returns unwrapped XError from err by errors.As. If no XError, returns nil +func Unwrap(err error) *XError { + var e *XError + if errors.As(err, &e) { + return e + } + return nil +} + +func newXError() *XError { + id, err := random.UUIdV4() + if err != nil { + return nil + } + + return &XError{ + id: id, + stack: callers(), + values: make(map[string]any), + } +} + +func (e *XError) copy(dest *XError) { + dest.message = e.message + dest.id = e.id + dest.cause = e.cause + + for k, v := range e.values { + dest.values[k] = v + } +} + +// Error implements standard error interface. +func (e *XError) Error() string { + msg := e.message + cause := e.cause + + if cause == nil { + return msg + } + + msg = fmt.Sprintf("%s: %v", msg, cause.Error()) + + return msg +} + +// Format returns: +// - %v, %s, %q: formatted message +// - %+v: formatted message with stack trace +func (e *XError) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + _, _ = io.WriteString(s, e.Error()) + e.stack.Format(s, verb) + return + } + fallthrough + case 's': + _, _ = io.WriteString(s, e.Error()) + case 'q': + fmt.Fprintf(s, "%q", e.Error()) + } +} + +// Wrap creates a new XError and copy message and id to new one. +func (e *XError) Wrap(cause error) *XError { + err := newXError() + e.copy(err) + err.cause = cause + return err +} + +// Unwrap compatible with github.com/pkg/errors +func (e *XError) Unwrap() error { + return e.cause +} + +// With adds key and value related to the error object +func (e *XError) With(key string, value any) *XError { + e.values[key] = value + return e +} + +// Is checks if target error is XError and Error.id of two errors are matched. +func (e *XError) Is(target error) bool { + var err *XError + + if errors.As(target, &err) { + if e.id != "" && e.id == err.id { + return true + } + } + + return e == target +} + +// Id sets id to check equality in XError.Is +func (e *XError) Id(id string) *XError { + e.id = id + return e +} + +// Values returns map of key and value that is set by With. All wrapped xerror.XError key and values will be merged. +// Key and values of wrapped error is overwritten by upper xerror.XError. +func (e *XError) Values() map[string]any { + var values map[string]any + + if cause := e.Unwrap(); cause != nil { + if err, ok := cause.(*XError); ok { + values = err.Values() + } + } + + if values == nil { + values = make(map[string]any) + } + + for key, value := range e.values { + values[key] = value + } + + return values +} + +type errInfo struct { + Message string `json:"message"` + Id string `json:"id"` + StackTrace []*Stack `json:"stacktrace"` + Cause error `json:"cause"` + Values map[string]any `json:"values"` +} + +// Info returns information of xerror, which can be printed. +func (e *XError) Info() *errInfo { + errInfo := &errInfo{ + Message: e.message, + Id: e.id, + StackTrace: e.Stacks(), + Cause: e.cause, + Values: make(map[string]any), + } + + for k, v := range e.values { + errInfo.Values[k] = v + } + + return errInfo +} + +// TryUnwrap if err is nil then it returns a valid value +// If err is not nil, Unwrap panics with err. +// Play: https://go.dev/play/p/w84d7Mb3Afk +func TryUnwrap[T any](val T, err error) T { + if err != nil { + panic(err) + } + return val +} diff --git a/xerror/xerror_example_test.go b/xerror/xerror_example_test.go new file mode 100644 index 00000000..b03003fc --- /dev/null +++ b/xerror/xerror_example_test.go @@ -0,0 +1,181 @@ +package xerror + +import ( + "context" + "errors" + "fmt" + "reflect" + "strconv" + "strings" +) + +func ExampleNew() { + err := New("error") + fmt.Println(err.Error()) + + // Output: + // error +} + +func ExampleWrap() { + err := New("wrong password") + wrapErr := Wrap(err, "error") + + fmt.Println(wrapErr.Error()) + + // Output: + // error: wrong password +} + +func ExampleXError_Wrap() { + err1 := New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + fmt.Println(err2.Error()) + + // Output: + // error: invalid username +} + +func ExampleXError_Unwrap() { + err1 := New("error").With("level", "high") + err2 := err1.Wrap(errors.New("invalid username")) + + err := err2.Unwrap() + + fmt.Println(err.Error()) + + // Output: + // invalid username +} + +func ExampleXError_StackTrace() { + err := New("error") + + stacks := err.Stacks() + + fmt.Println(stacks[0].Func) + fmt.Println(stacks[0].Line) + + containFile := strings.Contains(stacks[0].File, "xerror_example_test.go") + fmt.Println(containFile) + + // Output: + // github.com/duke-git/lancet/v2/xerror.ExampleXError_StackTrace + // 53 + // true +} + +func ExampleXError_With() { + err := New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} + +func ExampleXError_Id() { + err1 := New("error").Id("e001") + err2 := New("error").Id("e001") + err3 := New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} + +func ExampleXError_Is() { + err1 := New("error").Id("e001") + err2 := New("error").Id("e001") + err3 := New("error").Id("e003") + + equal := err1.Is(err2) + notEqual := err1.Is(err3) + + fmt.Println(equal) + fmt.Println(notEqual) + + // Output: + // true + // false +} + +func ExampleXError_Values() { + err := New("error").With("level", "high") + + errLevel := err.Values()["level"] + + fmt.Println(errLevel) + + // Output: + // high +} + +func ExampleXError_Info() { + cause := errors.New("error") + err := Wrap(cause, "invalid username").Id("e001").With("level", "high") + + errInfo := err.Info() + + fmt.Println(errInfo.Id) + fmt.Println(errInfo.Cause) + fmt.Println(errInfo.Values["level"]) + fmt.Println(errInfo.Message) + + // Output: + // e001 + // error + // high + // invalid username +} + +func ExampleTryUnwrap() { + result1 := TryUnwrap(strconv.Atoi("42")) + fmt.Println(result1) + + _, err := strconv.Atoi("4o2") + defer func() { + v := recover() + result2 := reflect.DeepEqual(err.Error(), v.(*strconv.NumError).Error()) + fmt.Println(result2) + }() + + TryUnwrap(strconv.Atoi("4o2")) + + // Output: + // 42 + // true +} + +func ExampleTryCatch() { + calledFinally := false + calledCatch := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error message") + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + // Error in try block at /path/xxx.go:174 - Cause: error message + // fmt.Println(err.Error()) + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +} diff --git a/xerror/xerror_test.go b/xerror/xerror_test.go new file mode 100644 index 00000000..c14714ec --- /dev/null +++ b/xerror/xerror_test.go @@ -0,0 +1,117 @@ +package xerror + +import ( + "errors" + "strconv" + "strings" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestTryUnwrap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryUnwrap") + assert.Equal(42, TryUnwrap(strconv.Atoi("42"))) + + _, err := strconv.Atoi("4o2") + defer func() { + v := recover() + assert.Equal(err.Error(), v.(*strconv.NumError).Error()) + }() + + TryUnwrap(strconv.Atoi("4o2")) +} + +func TestNew(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestNew") + + err := New("error occurs") + assert.Equal("error occurs", err.Error()) +} + +func TestWrap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestWrap") + + err := New("wrong password") + wrapErr := Wrap(err, "error") + + assert.Equal("error: wrong password", wrapErr.Error()) +} + +func TestXError_Wrap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestXError_Wrap") + + err1 := New("error").With("level", "high") + err2 := err1.Wrap(errors.New("bad")) + + assert.Equal("error: bad", err2.Error()) +} + +func TestXError_Unwrap(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestXError_Unwrap") + + err1 := New("error").With("level", "high") + + err2 := err1.Wrap(errors.New("bad")) + + err := err2.Unwrap() + + assert.Equal("bad", err.Error()) +} + +func TestXError_StackTrace(t *testing.T) { + assert := internal.NewAssert(t, "TestXError_StackTrace") + + err := New("error") + + stacks := err.Stacks() + + assert.Equal(3, len(stacks)) + assert.Equal("github.com/duke-git/lancet/v2/xerror.TestXError_StackTrace", stacks[0].Func) + assert.Equal(75, stacks[0].Line) + assert.Equal(true, strings.Contains(stacks[0].File, "xerror_test.go")) +} + +func TestXError_With_Id_Is_Values(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestXError_With_Id_Is_Values") + + baseErr := New("baseError") + err1 := New("error1").Id("e001").With("level", "high") + err2 := New("error2").Id("e002").With("level", "low") + + err := err1.Wrap(baseErr).With("v", "1.0") + + assert.Equal(true, errors.Is(err, baseErr)) + assert.NotEqual(err, err1) + assert.IsNotNil(err.Values()["v"]) + assert.IsNil(err1.Values()["v"]) + + assert.Equal(false, errors.Is(err, err2)) +} + +func TestXError_Info(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestXError_Info") + + cause := errors.New("error") + err := Wrap(cause, "invalid username").Id("e001").With("level", "high") + + errInfo := err.Info() + assert.Equal("invalid username", errInfo.Message) + assert.Equal("e001", errInfo.Id) + assert.Equal(cause, errInfo.Cause) + assert.Equal("high", errInfo.Values["level"]) +}