mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
Compare commits
305 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69b34faeec | ||
|
|
9af645606b | ||
|
|
f58b706285 | ||
|
|
0f9683a764 | ||
|
|
4595a94b4c | ||
|
|
e1e15883e9 | ||
|
|
b4e7977feb | ||
|
|
1d3585b22f | ||
|
|
2145808268 | ||
|
|
cb7ff904d9 | ||
|
|
87c8799e9e | ||
|
|
c9a8c70ee6 | ||
|
|
ab2e5a3137 | ||
|
|
b942b7a074 | ||
|
|
d910af24e0 | ||
|
|
5fc21330da | ||
|
|
928e3a390d | ||
|
|
48519aba2b | ||
|
|
97ea636e9e | ||
|
|
f82f49a4c2 | ||
|
|
0fc8733915 | ||
|
|
44022c5861 | ||
|
|
ec0222c5b1 | ||
|
|
76b82a2fa1 | ||
|
|
d9dc2993b3 | ||
|
|
ae42a9fdff | ||
|
|
f37e55d9f1 | ||
|
|
d920a51988 | ||
|
|
561c42a24e | ||
|
|
12d74489d5 | ||
|
|
4959f26003 | ||
|
|
eb683a33d2 | ||
|
|
0e63489e9e | ||
|
|
d9e318550a | ||
|
|
993b0e6023 | ||
|
|
7cf358a0ec | ||
|
|
b85545c584 | ||
|
|
c3c6c92cd4 | ||
|
|
8587abc977 | ||
|
|
5c01b7e675 | ||
|
|
0247ac232a | ||
|
|
e77354d7ff | ||
|
|
8998dc35bb | ||
|
|
ad9fd196ce | ||
|
|
c8a65c33a4 | ||
|
|
69a797f8ed | ||
|
|
1e5b69e9bf | ||
|
|
275abcc8c2 | ||
|
|
065b3b84fe | ||
|
|
4bc43f3278 | ||
|
|
f3382ceac9 | ||
|
|
d20f8783b2 | ||
|
|
8432a4e1ee | ||
|
|
5a8ff17b52 | ||
|
|
6b46a0c05c | ||
|
|
4b8b624b4c | ||
|
|
f274375e62 | ||
|
|
6dc017a5fa | ||
|
|
e1fb97095d | ||
|
|
d8f5a15590 | ||
|
|
1202ac955d | ||
|
|
84b2cec9b5 | ||
|
|
2d01a13787 | ||
|
|
db9b045500 | ||
|
|
2701bcc4d2 | ||
|
|
b9745fd08b | ||
|
|
3b1597d6f7 | ||
|
|
40ab5e8f7b | ||
|
|
cacedf2a05 | ||
|
|
4fc391895b | ||
|
|
b419319e66 | ||
|
|
db34b5f69c | ||
|
|
ee44526d9e | ||
|
|
6576b1f0cb | ||
|
|
93108bafb9 | ||
|
|
07f5e0697f | ||
|
|
4ba91d7e4c | ||
|
|
ab81d9c283 | ||
|
|
a74038466f | ||
|
|
02daa7f6cb | ||
|
|
fef6fd7b9d | ||
|
|
f6cd98086f | ||
|
|
24eb2bbacd | ||
|
|
15c1537bf0 | ||
|
|
c02654559a | ||
|
|
634ca09e8c | ||
|
|
f2e743dcf4 | ||
|
|
f8f58cae10 | ||
|
|
215b79140d | ||
|
|
0bd675340f | ||
|
|
f3d73899b1 | ||
|
|
d4a20b239a | ||
|
|
4c28431451 | ||
|
|
168ed096c7 | ||
|
|
a060769635 | ||
|
|
0a99492cf6 | ||
|
|
fb3de03f37 | ||
|
|
43c2fd2a22 | ||
|
|
279d0754ba | ||
|
|
f133b32faa | ||
|
|
b98d5edbb5 | ||
|
|
9e39c31087 | ||
|
|
26bc40c614 | ||
|
|
76d68e326b | ||
|
|
67b4782ac2 | ||
|
|
1d8b9a2625 | ||
|
|
94ae1acc78 | ||
|
|
aece2995d6 | ||
|
|
f5ec5eb58d | ||
|
|
d62284e9a6 | ||
|
|
b6815224fd | ||
|
|
5931b882ee | ||
|
|
f8096e3585 | ||
|
|
979381bfb7 | ||
|
|
3e2c25f827 | ||
|
|
2c7bcc9fbb | ||
|
|
3a944ab12f | ||
|
|
387079d034 | ||
|
|
33e3631b72 | ||
|
|
b3149ea619 | ||
|
|
116ff284c3 | ||
|
|
0cb251f7b8 | ||
|
|
683def2242 | ||
|
|
d4a90f2869 | ||
|
|
be67de3b40 | ||
|
|
6898ed413e | ||
|
|
5dbdbcd651 | ||
|
|
ae0facd32d | ||
|
|
2d7e19fb87 | ||
|
|
24d4a03227 | ||
|
|
93fb089f6e | ||
|
|
2fe272f2ef | ||
|
|
77859ffa15 | ||
|
|
f83f47df3a | ||
|
|
885c08847d | ||
|
|
cc4a20751f | ||
|
|
b0d1d39452 | ||
|
|
02fa7bc8be | ||
|
|
433eb63b86 | ||
|
|
a2541dac03 | ||
|
|
690e746811 | ||
|
|
7cb97a26c5 | ||
|
|
39d373d37b | ||
|
|
1aefd6aa12 | ||
|
|
c7aa44b8a4 | ||
|
|
0e3dc68de5 | ||
|
|
4083e75ed4 | ||
|
|
1327eff62f | ||
|
|
eb24c37143 | ||
|
|
b7a6c91064 | ||
|
|
555e185871 | ||
|
|
cb0efc5cc7 | ||
|
|
7ac6816532 | ||
|
|
81c4216699 | ||
|
|
2e30389703 | ||
|
|
0e1d5cf04b | ||
|
|
4e2d84e4fc | ||
|
|
13ee045b99 | ||
|
|
f1fd4c876b | ||
|
|
cd63b2b18f | ||
|
|
bab9b4a6b3 | ||
|
|
b3c34578bf | ||
|
|
f26477904e | ||
|
|
85886cf618 | ||
|
|
a19a861552 | ||
|
|
96d57a6d24 | ||
|
|
b48155c249 | ||
|
|
41685022c0 | ||
|
|
4e7274ce05 | ||
|
|
2384b0e51f | ||
|
|
6b6b938d1e | ||
|
|
29d8987909 | ||
|
|
43fb3f1a06 | ||
|
|
34c28fece3 | ||
|
|
40c2402493 | ||
|
|
c965e79bfc | ||
|
|
eeadb1fecb | ||
|
|
1dd826f050 | ||
|
|
8b04aa5f31 | ||
|
|
6855fe0bce | ||
|
|
300be9e3dc | ||
|
|
384be2e2e9 | ||
|
|
1d71a0ad3a | ||
|
|
0e0ed316f1 | ||
|
|
961eedda04 | ||
|
|
f4881d2f49 | ||
|
|
a3eb269bdb | ||
|
|
e367123070 | ||
|
|
0f1d3fb553 | ||
|
|
a4658b2341 | ||
|
|
739113ef9e | ||
|
|
1629b861cd | ||
|
|
acce85557f | ||
|
|
05aefbaa62 | ||
|
|
6988fdc451 | ||
|
|
5963830879 | ||
|
|
134aded4d8 | ||
|
|
35780d9dc1 | ||
|
|
c40ac9bb9b | ||
|
|
51bd974cc9 | ||
|
|
0b93fbffd9 | ||
|
|
349f8b6c97 | ||
|
|
5899921054 | ||
|
|
ec19014578 | ||
|
|
1ad990ce9d | ||
|
|
8bb102cb6e | ||
|
|
e07d54d1da | ||
|
|
6f035f710e | ||
|
|
92967e0add | ||
|
|
6a1a0b8677 | ||
|
|
ca88687f3d | ||
|
|
aa64bf5bee | ||
|
|
a3399503f7 | ||
|
|
3ca096b4ac | ||
|
|
28317a1683 | ||
|
|
2ab898741d | ||
|
|
454efd486d | ||
|
|
efa20a97c4 | ||
|
|
25ef78bc64 | ||
|
|
261370e30d | ||
|
|
764a6fe107 | ||
|
|
f3749c52b9 | ||
|
|
f368854b2d | ||
|
|
c424b88d40 | ||
|
|
aeebd63eda | ||
|
|
22b3c4dd42 | ||
|
|
bd976642f6 | ||
|
|
e31fb28003 | ||
|
|
fd271fe176 | ||
|
|
6890bbfe05 | ||
|
|
24ae47a12f | ||
|
|
d8d85efedf | ||
|
|
ba73847b80 | ||
|
|
69453eba19 | ||
|
|
f147f78a41 | ||
|
|
bbfc5b7060 | ||
|
|
1f45937190 | ||
|
|
52c5a91606 | ||
|
|
49f62c3550 | ||
|
|
23701e6998 | ||
|
|
1199c30ef3 | ||
|
|
b0e17c7bc4 | ||
|
|
d3525dfe8f | ||
|
|
9da7115169 | ||
|
|
9cb9aa2f2f | ||
|
|
e4cd7dad35 | ||
|
|
31e08197d4 | ||
|
|
642d0b8077 | ||
|
|
25b2ae6b98 | ||
|
|
65719515bd | ||
|
|
3ffd81a98a | ||
|
|
f490ef2404 | ||
|
|
3438f3b18a | ||
|
|
73f4ae7b35 | ||
|
|
a8996933bf | ||
|
|
3905c0bde1 | ||
|
|
c7e961704d | ||
|
|
cb7df1b57d | ||
|
|
eeff28606e | ||
|
|
86d4b25a2b | ||
|
|
ad287ed99a | ||
|
|
df9de3065b | ||
|
|
71a2ea3f20 | ||
|
|
955f2e6de6 | ||
|
|
4aef9d6d22 | ||
|
|
4752725dd6 | ||
|
|
07d1704cb2 | ||
|
|
74d262e609 | ||
|
|
97e0789ea4 | ||
|
|
bc39b0887b | ||
|
|
4fd8a18677 | ||
|
|
3de906d7c9 | ||
|
|
56fc12c660 | ||
|
|
7a9b0847f9 | ||
|
|
9266d99249 | ||
|
|
f15131f032 | ||
|
|
b4a49fccfd | ||
|
|
94b1a1d383 | ||
|
|
7db46696f6 | ||
|
|
d4f49af2ad | ||
|
|
ed4acc1c67 | ||
|
|
042a00296f | ||
|
|
b42aac53b3 | ||
|
|
c625a88067 | ||
|
|
e15ae9eb98 | ||
|
|
bb4ac01209 | ||
|
|
147c1625b5 | ||
|
|
2aed55f704 | ||
|
|
051f20caef | ||
|
|
613785b07c | ||
|
|
0b0eb695e8 | ||
|
|
745082fff1 | ||
|
|
24b8da360e | ||
|
|
b106c428ae | ||
|
|
8b1171d0cb | ||
|
|
ab012f2545 | ||
|
|
a952cb208a | ||
|
|
f239a8ca8e | ||
|
|
5c6626b37e | ||
|
|
16b5101600 | ||
|
|
ec983b7aa6 | ||
|
|
56fc2aabd6 | ||
|
|
0ddd52225b | ||
|
|
3919160e38 | ||
|
|
40ec5bc0f6 |
4
.github/workflows/codecov.yml
vendored
4
.github/workflows/codecov.yml
vendored
@@ -1,11 +1,13 @@
|
||||
name: Test and coverage
|
||||
name: test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# - v2
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
# - v2
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -3,4 +3,11 @@
|
||||
.DS_Store
|
||||
cryptor/*.txt
|
||||
fileutil/*.txt
|
||||
cryptor/*.pem
|
||||
fileutil/*.zip
|
||||
fileutil/*.link
|
||||
fileutil/tempdir
|
||||
fileutil/unzip/*
|
||||
slice/testdata/*
|
||||
cryptor/*.pem
|
||||
docs/node_modules
|
||||
docs/.vitepress
|
||||
838
README.md
838
README.md
@@ -1,46 +1,50 @@
|
||||
<div align="center">
|
||||
<h1 style="width: 100%; text-align: center;">Lancet</h1>
|
||||
<p style="font-size: 18px">
|
||||
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
|
||||
</p>
|
||||
<div align="center" style="text-align: center;">
|
||||
<div align=center>
|
||||
<img src="./logo.png" width="200" height="200"/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
[](https://codecov.io/gh/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/blob/main/LICENSE)
|
||||
[](https://github.com/duke-git/lancet/blob/v1/LICENSE)
|
||||
|
||||
</div>
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
<p style="font-size: 20px">
|
||||
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
|
||||
</p>
|
||||
|
||||
English | [简体中文](./README_zh-CN.md)
|
||||
|
||||
</div>
|
||||
## Feature
|
||||
|
||||
### Feature
|
||||
- 👏 Comprehensive, efficient and reusable.
|
||||
- 💪 400+ 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.
|
||||
- 💪 100+ common go util functions, support string, slice, datetime, net, crypt...
|
||||
- 💅 Only depend on the go standard library.
|
||||
- 🌍 Unit test for exery exported function.
|
||||
|
||||
### Installation
|
||||
## Installation
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet
|
||||
```
|
||||
|
||||
### Usage
|
||||
## 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,import the strutil package like below:
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
### Example
|
||||
## 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
|
||||
@@ -52,441 +56,513 @@ import (
|
||||
|
||||
func main() {
|
||||
s := "hello"
|
||||
rs := strutil.ReverseStr(s)
|
||||
rs := strutil.Reverse(s)
|
||||
fmt.Println(rs) //olleh
|
||||
}
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
## API Documentation
|
||||
|
||||
#### 1. convertor contains some functions for data convertion
|
||||
|
||||
- Support conversion between commonly used data types.
|
||||
- Usage: import "github.com/duke-git/lancet/cryptor"
|
||||
### 1. Compare package provides a lightweight comparison function on any type.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "12.3"
|
||||
f, err := convertor.ToFloat(s)
|
||||
if err != nil {
|
||||
fmt.Errorf("error is %s", err.Error())
|
||||
}
|
||||
fmt.Println(f) // 12.3
|
||||
}
|
||||
import "github.com/duke-git/lancet/compare"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [Equal](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#Equal)
|
||||
- [EqualValue](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#EqualValue)
|
||||
- [LessThan](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#LessThan)
|
||||
- [GreaterThan](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#GreaterThan)
|
||||
- [LessOrEqual](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#LessOrEqual)
|
||||
- [GreaterOrEqual](https://github.com/duke-git/lancet/blob/v1/docs/compare.md#GreaterOrEqual)
|
||||
|
||||
### 2. Convertor package contains some functions for data convertion.
|
||||
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) //convert color hex to color rgb
|
||||
func ColorRGBToHex(red, green, blue int) string //convert color rgb to color hex
|
||||
func ToBool(s string) (bool, error) //convert string to a boolean
|
||||
func ToBytes(data interface{}) ([]byte, error) //convert interface to bytes
|
||||
func ToChar(s string) []string //convert string to char slice
|
||||
func ToFloat(value interface{}) (float64, error) //convert value to float64, if input is not a float return 0.0 and error
|
||||
func ToInt(value interface{}) (int64, error) //convert value to int64, if input is not a numeric format return 0 and error
|
||||
func ToJson(value interface{}) (string, error) //convert value to a valid json string
|
||||
func ToString(value interface{}) string //convert value to string
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) //convert struct to map, only convert exported field, tag `json` should be set
|
||||
import "github.com/duke-git/lancet/convertor"
|
||||
```
|
||||
|
||||
#### 2. cryptor is for data encryption and decryption
|
||||
#### Function list:
|
||||
|
||||
- Support md5, hmac, aes, des, ras.
|
||||
- Usage: import "github.com/duke-git/lancet/cryptor"
|
||||
- [ColorHexToRGB](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ColorHexToRGB)
|
||||
- [ColorRGBToHex](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ColorRGBToHex)
|
||||
- [ToBool](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToBool)
|
||||
- [ToBytes](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToBytes)
|
||||
- [ToChar](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToChar)
|
||||
- [ToChannel](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToChannel)
|
||||
- [ToInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToInt)
|
||||
- [ToJson](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToJson)
|
||||
- [ToString](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToString)
|
||||
- [StructToMap](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#StructToMap)
|
||||
- [MapToStruct](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#MapToStruct)
|
||||
- [EncodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#EncodeByte)
|
||||
- [DecodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#DecodeByte)
|
||||
- [DeepClone](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#DeepClone)
|
||||
- [CopyProperties](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#CopyProperties)
|
||||
- [ToInterface](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToInterface)
|
||||
- [Utf8ToGbk](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#Utf8ToGbk)
|
||||
- [GbkToUtf8](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#GbkToUtf8)
|
||||
- [ToStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToStdBase64)
|
||||
- [ToUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToUrlBase64)
|
||||
- [ToRawStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToRawStdBase64)
|
||||
- [ToRawUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToRawUrlBase64)
|
||||
- [ToBigInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor.md#ToBigInt)
|
||||
|
||||
### 3. Cryptor package is for data encryption and decryption.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/cryptor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key))
|
||||
decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key))
|
||||
fmt.Println(string(decrypted)) // hello
|
||||
}
|
||||
import "github.com/duke-git/lancet/cryptor"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [AesEcbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesEcbEncrypt)
|
||||
- [AesEcbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesEcbDecrypt)
|
||||
- [AesCbcEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesCbcEncrypt)
|
||||
- [AesCbcDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesCbcDecrypt)
|
||||
- [AesCtrCrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesCtrCrypt)
|
||||
- [AesCfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesCfbEncrypt)
|
||||
- [AesCfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesCfbDecrypt)
|
||||
- [AesOfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesOfbEncrypt)
|
||||
- [AesOfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#AesOfbDecrypt)
|
||||
- [Base64StdEncode](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Base64StdEncode)
|
||||
- [Base64StdDecode](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Base64StdDecode)
|
||||
- [DesEcbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesEcbEncrypt)
|
||||
- [DesEcbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesEcbDecrypt)
|
||||
- [DesCbcEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesCbcEncrypt)
|
||||
- [DesCbcDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesCbcDecrypt)
|
||||
- [DesCtrCrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesCtrCrypt)
|
||||
- [DesCfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesCfbEncrypt)
|
||||
- [DesCfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesCfbDecrypt)
|
||||
- [DesOfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesOfbEncrypt)
|
||||
- [DesOfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#DesOfbDecrypt)
|
||||
- [HmacMd5](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacMd5)
|
||||
- [HmacMd5WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacMd5WithBase64)
|
||||
- [HmacSha1](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha1)
|
||||
- [HmacSha1WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha1WithBase64)
|
||||
- [HmacSha256](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha256)
|
||||
- [HmacSha256WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha256WithBase64)
|
||||
- [HmacSha512](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha512)
|
||||
- [HmacSha512WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha512WithBase64)
|
||||
- [Md5String](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Md5String)
|
||||
- [Md5StringWithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Md5StringWithBase64)
|
||||
- [Md5Byte](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Md5Byte)
|
||||
- [Md5ByteWithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Md5ByteWithBase64)
|
||||
- [Md5File](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Md5File)
|
||||
- [Sha1](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha1)
|
||||
- [Sha1WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha1WithBase64)
|
||||
- [Sha256](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha256)
|
||||
- [Sha256WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha256WithBase64)
|
||||
- [Sha512](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha512)
|
||||
- [Sha512WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#Sha512WithBase64)
|
||||
- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#GenerateRsaKey)
|
||||
- [RsaEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaEncrypt)
|
||||
- [RsaDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaDecrypt)
|
||||
- [GenerateRsaKeyPair](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#GenerateRsaKeyPair)
|
||||
- [RsaEncryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaEncryptOAEP)
|
||||
- [RsaDecryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#RsaDecryptOAEP)
|
||||
### 4. Datetime package supports date and time format and compare.
|
||||
|
||||
```go
|
||||
func AesEcbEncrypt(data, key []byte) []byte //AES ECB encrypt
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte //AES ECB decrypt
|
||||
func AesCbcEncrypt(data, key []byte) []byte //AES CBC encrypt
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte //AES CBC decrypt
|
||||
func AesCtrCrypt(data, key []byte) []byte //AES CTR encrypt / decrypt
|
||||
func AesCfbEncrypt(data, key []byte) []byte //AES CFB encrypt
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte //AES CFB decrypt
|
||||
func AesOfbEncrypt(data, key []byte) []byte //AES OFB encrypt
|
||||
func AesOfbDecrypt(data, key []byte) []byte //AES OFB decrypt
|
||||
func Base64StdEncode(s string) string //base64 encode
|
||||
func Base64StdDecode(s string) string //base64 decode
|
||||
func DesCbcEncrypt(data, key []byte) []byte //DES CBC encrypt
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte //DES CBC decrypt
|
||||
func DesCtrCrypt(data, key []byte) []byte //DES CTR encrypt/decrypt
|
||||
func DesCfbEncrypt(data, key []byte) []byte //DES CFB encrypt
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte //DES CFB decrypt
|
||||
func DesOfbEncrypt(data, key []byte) []byte //DES OFB encrypt
|
||||
func DesOfbDecrypt(data, key []byte) []byte //DES OFB decrypt
|
||||
func HmacMd5(data, key string) string //get hmac md5 value
|
||||
func HmacSha1(data, key string) string //get hmac sha1 value
|
||||
func HmacSha256(data, key string) string //get hmac sha256 value
|
||||
func HmacSha512(data, key string) string //get hmac sha512 value
|
||||
func Sha1(data string) string //get sha1 value
|
||||
func Sha256(data string) string //getsha256 value
|
||||
func Sha512(data string) string //get sha512 value
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) //generate RSA pem file
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte //RSA encrypt
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA decrypt
|
||||
|
||||
import "github.com/duke-git/lancet/datetime"
|
||||
```
|
||||
|
||||
#### 3. datetime parse and format datetime
|
||||
#### Function list:
|
||||
|
||||
- Parse and format datetime
|
||||
- Usage: import "github.com/duke-git/lancet/datetime"
|
||||
- [AddDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#AddDay)
|
||||
- [AddHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#AddHour)
|
||||
- [AddMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#AddMinute)
|
||||
- [AddYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#AddYear)
|
||||
- [BeginOfMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfMinute)
|
||||
- [BeginOfHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfHour)
|
||||
- [BeginOfDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfDay)
|
||||
- [BeginOfWeek](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfWeek)
|
||||
- [BeginOfMonth](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfMonth)
|
||||
- [BeginOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfYear)
|
||||
- [EndOfMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfMinute)
|
||||
- [EndOfHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfHour)
|
||||
- [EndOfDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfDay)
|
||||
- [EndOfWeek](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfWeek)
|
||||
- [EndOfMonth](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfMonth)
|
||||
- [EndOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfYear)
|
||||
- [GetNowDate](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GetNowDate)
|
||||
- [GetNowTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GetNowTime)
|
||||
- [GetNowDateTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GetNowDateTime)
|
||||
- [GetZeroHourTimestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GetZeroHourTimestamp)
|
||||
- [GetNightTimestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GetNightTimestamp)
|
||||
- [FormatTimeToStr](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#FormatTimeToStr)
|
||||
- [FormatStrToTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#FormatStrToTime)
|
||||
- [NewUnix](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#NewUnix)
|
||||
- [NewUnixNow](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#NewUnixNow)
|
||||
- [NewFormat](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#NewFormat)
|
||||
- [NewISO8601](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#NewISO8601)
|
||||
- [ToUnix](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#ToUnix)
|
||||
- [ToFormat](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#ToFormat)
|
||||
- [ToFormatForTpl](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#ToFormatForTpl)
|
||||
- [ToIso8601](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#ToIso8601)
|
||||
- [IsLeapYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#IsLeapYear)
|
||||
- [BetweenSeconds](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BetweenSeconds)
|
||||
- [DayOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#DayOfYear)
|
||||
- [IsWeekend](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#IsWeekend)
|
||||
- [NowDateOrTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#NowDateOrTime)
|
||||
- [Timestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#Timestamp)
|
||||
- [TimestampMilli](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampMilli)
|
||||
- [TimestampMicro](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampMicro)
|
||||
- [TimestampNano](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TimestampNano)
|
||||
- [TrackFuncTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#TrackFuncTime)
|
||||
- [DaysBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#DaysBetween)
|
||||
- [GenerateDatetimesBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#GenerateDatetimesBetween)
|
||||
|
||||
### 5. Fileutil package implements some basic functions for file operations.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
now := time.Now()
|
||||
s := datetime.FormatTimeToStr(now, "yyyy-mm-dd hh:mm:ss")
|
||||
fmt.Println(s) // 2021-11-24 11:16:55
|
||||
}
|
||||
import "github.com/duke-git/lancet/fileutil"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [ClearFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ClearFile)
|
||||
- [CreateFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#CreateFile)
|
||||
- [CreateDir](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#CreateDir)
|
||||
- [CopyFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#CopyFile)
|
||||
- [FileMode](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#FileMode)
|
||||
- [MiMeType](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#MiMeType)
|
||||
- [IsExist](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#IsExist)
|
||||
- [IsLink](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#IsLink)
|
||||
- [IsDir](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#IsDir)
|
||||
- [ListFileNames](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ListFileNames)
|
||||
- [RemoveFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#RemoveFile)
|
||||
- [ReadFileToString](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadFileToString)
|
||||
- [ReadFileByLine](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadFileByLine)
|
||||
- [Zip](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#Zip)
|
||||
- [ZipAppendEntry](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ZipAppendEntry)
|
||||
- [UnZip](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#UnZip)
|
||||
- [CurrentPath](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#CurrentPath)
|
||||
- [IsZipFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#IsZipFile)
|
||||
- [FileSize](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#FileSize)
|
||||
- [MTime](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#MTime)
|
||||
- [Sha](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#Sha)
|
||||
- [ReadCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadCsvFile)
|
||||
- [WriteCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteCsvFile)
|
||||
- [WriteMapsToCsv](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteMapsToCsv)
|
||||
- [WriteStringToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteStringToFile)
|
||||
- [WriteBytesToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#WriteBytesToFile)
|
||||
- [ReadFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil.md#ReadFile)
|
||||
|
||||
### 6. Formatter contains some functions for data formatting.
|
||||
|
||||
```go
|
||||
func AddDay(t time.Time, day int64) time.Time //add or sub days to time
|
||||
func AddHour(t time.Time, hour int64) time.Time //add or sub hours to time
|
||||
func AddMinute(t time.Time, minute int64) time.Time //add or sub minutes to time
|
||||
func GetNowDate() string //get current date, format is yyyy-mm-dd
|
||||
func GetNowTime() string //get current time, format is hh:mm:ss
|
||||
func GetNowDateTime() string //get current date and time, format is yyyy-mm-dd hh:mm:ss
|
||||
func GetZeroHourTimestamp() int64 //return timestamp of zero hour (timestamp of 00:00)
|
||||
func GetNightTimestamp() int64 //return timestamp of zero hour (timestamp of 23:59)
|
||||
func FormatTimeToStr(t time.Time, format string) string //convert time to string
|
||||
func FormatStrToTime(str, format string) time.Time //convert string to time
|
||||
import "github.com/duke-git/lancet/formatter"
|
||||
```
|
||||
|
||||
#### 4. fileutil basic functions for file operations
|
||||
#### Function list:
|
||||
|
||||
- Basic functions for file operations.
|
||||
- Usage: import "github.com/duke-git/lancet/fileutil"
|
||||
- [Comma](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#Comma)
|
||||
- [Pretty](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#Pretty)
|
||||
- [PrettyToWriter](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#PrettyToWriter)
|
||||
- [DecimalBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#DecimalBytes)
|
||||
- [BinaryBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#BinaryBytes)
|
||||
- [ParseDecimalBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#ParseDecimalBytes)
|
||||
- [ParseBinaryBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter.md#ParseBinaryBytes)
|
||||
|
||||
### 7. Function package can control the flow of function execution and support part of functional programming
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(fileutil.IsDir("./")) // true
|
||||
}
|
||||
import "github.com/duke-git/lancet/function"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [After](https://github.com/duke-git/lancet/blob/v1/docs/function.md#After)
|
||||
- [Before](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Before)
|
||||
- [Curry](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Curry)
|
||||
- [Compose](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Compose)
|
||||
- [Debounced](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Debounced)
|
||||
- [Debounce](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Debounce)
|
||||
- [Throttle](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Throttle)
|
||||
- [Delay](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Delay)
|
||||
- [Pipeline](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Pipeline)
|
||||
- [Schedule](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Schedule)
|
||||
- [Watcher](https://github.com/duke-git/lancet/blob/v1/docs/function.md#Watcher)
|
||||
|
||||
### 8. Mathutil package implements some functions for math calculation.
|
||||
|
||||
```go
|
||||
func ClearFile(path string) error //write empty string to path file
|
||||
func CreateFile(path string) bool // create a file in path
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error //copy src file to dst file
|
||||
func IsExist(path string) bool //checks if a file or directory exists
|
||||
func IsDir(path string) bool //checks if the path is directy or not
|
||||
func ListFileNames(path string) ([]string, error) //return all file names in the path
|
||||
func RemoveFile(path string) error //remove the path file
|
||||
func ReadFileToString(path string) (string, error) //return string of file content
|
||||
func ReadFileByLine(path string)([]string, error) //read file content by line
|
||||
import "github.com/duke-git/lancet/mathutil"
|
||||
```
|
||||
|
||||
#### 5. formatter is for data format
|
||||
#### Function list:
|
||||
|
||||
- Contain some formatting function
|
||||
- Usage: import "github.com/duke-git/lancet/formatter"
|
||||
- [Exponent](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Exponent)
|
||||
- [Fibonacci](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Fibonacci)
|
||||
- [Factorial](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Factorial)
|
||||
- [Percent](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Percent)
|
||||
- [RoundToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RoundToFloat)
|
||||
- [RoundToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RoundToString)
|
||||
- [TruncRound](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#TruncRound)
|
||||
- [CeilToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#CeilToFloat)
|
||||
- [CeilToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#CeilToString)
|
||||
- [FloorToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#FloorToFloat)
|
||||
- [FloorToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#FloorToString)
|
||||
- [AngleToRadian](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#AngleToRadian)
|
||||
- [RadianToAngle](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#RadianToAngle)
|
||||
- [PointDistance](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#PointDistance)
|
||||
- [IsPrime](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#IsPrime)
|
||||
- [GCD](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#GCD)
|
||||
- [LCM](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#LCM)
|
||||
- [Cos](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Cos)
|
||||
- [Sin](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Sin)
|
||||
- [Log](https://github.com/duke-git/lancet/blob/v1/docs/mathutil.md#Log)
|
||||
|
||||
### 9. Netutil package contains functions to get net information and send http request.
|
||||
|
||||
```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"
|
||||
}
|
||||
import "github.com/duke-git/lancet/netutil"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [ConvertMapToQueryString](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#ConvertMapToQueryString)
|
||||
- [EncodeUrl](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#EncodeUrl)
|
||||
- [GetInternalIp](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetInternalIp)
|
||||
- [GetIps](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetIps)
|
||||
- [GetMacAddrs](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetMacAddrs)
|
||||
- [GetPublicIpInfo](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetPublicIpInfo)
|
||||
- [GetRequestPublicIp](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetRequestPublicIp)
|
||||
- [IsPublicIP](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#IsPublicIP)
|
||||
- [IsInternalIP](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#IsInternalIP)
|
||||
- [HttpGet](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#HttpGet)
|
||||
- [HttpDelete](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#HttpDelete)
|
||||
- [HttpPost](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#HttpPost)
|
||||
- [HttpPut](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#HttpPut)
|
||||
- [HttpPatch](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#HttpPatch)
|
||||
- [ParseHttpResponse](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#ParseHttpResponse)
|
||||
- [UploadFile](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#UploadFile)
|
||||
- [DownloadFile](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#DownloadFile)
|
||||
- [IsPingConnected](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#IsPingConnected)
|
||||
- [IsTelnetConnected](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#IsTelnetConnected)
|
||||
|
||||
### 10. Random package implements some basic functions to generate random int and string.
|
||||
|
||||
```go
|
||||
func Comma(v interface{}, symbol string) string //add comma to number by every 3 numbers from right. ahead by symbol char
|
||||
import "github.com/duke-git/lancet/random"
|
||||
```
|
||||
|
||||
#### 6. function can control the function execution and support functional programming
|
||||
#### Function list:
|
||||
|
||||
- Control function execution and support functional programming.
|
||||
- Usage: import "github.com/duke-git/lancet/function"
|
||||
- [RandBool](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBool)
|
||||
- [RandBoolSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBoolSlice)
|
||||
- [RandBytes](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandBytes)
|
||||
- [RandInt](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandInt)
|
||||
- [RandIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandIntSlice)
|
||||
- [RandString](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandString)
|
||||
- [RandStringSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandStringSlice)
|
||||
- [RandFloat](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandFloat)
|
||||
- [RandFloats](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandFloats)
|
||||
- [RandUpper](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandUpper)
|
||||
- [RandLower](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandLower)
|
||||
- [RandNumeral](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandNumeral)
|
||||
- [RandNumeralOrLetter](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandNumeralOrLetter)
|
||||
- [UUIdV4](https://github.com/duke-git/lancet/blob/v1/docs/random.md#UUIdV4)
|
||||
- [RandUniqueIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandUniqueIntSlice)
|
||||
|
||||
### 11. Retry package is for executing a function repeatedly until it was successful or canceled by the context.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var print = func(s string) {
|
||||
fmt.Println(s)
|
||||
}
|
||||
function.Delay(2*time.Second, print, "hello world")
|
||||
}
|
||||
import "github.com/duke-git/lancet/retry"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [Context](https://github.com/duke-git/lancet/blob/v1/docs/retry.md#Context)
|
||||
- [Retry](https://github.com/duke-git/lancet/blob/v1/docs/retry.md#Retry)
|
||||
- [RetryFunc](https://github.com/duke-git/lancet/blob/v1/docs/retry.md#RetryFunc)
|
||||
- [RetryDuration](https://github.com/duke-git/lancet/blob/v1/docs/retry.md#RetryDuration)
|
||||
- [RetryTimes](https://github.com/duke-git/lancet/blob/v1/docs/retry.md#RetryTimes)
|
||||
|
||||
### 12. Slice contains some functions to manipulate slice.
|
||||
|
||||
```go
|
||||
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called n or more times
|
||||
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called less than n times
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //make a curryed function
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //compose the functions from right to left
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //invoke function after delayed time
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //invoke function every duration time, util close the returned bool chan
|
||||
import "github.com/duke-git/lancet/slice"
|
||||
```
|
||||
|
||||
#### 7. netutil is for net process
|
||||
#### Function list:
|
||||
|
||||
- Ip and http request method.
|
||||
- Usage: import "github.com/duke-git/lancet/netutil".
|
||||
- The Http function params order:url, header, query string, body, httpclient.
|
||||
- [AppendIfAbsent](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#AppendIfAbsent)
|
||||
- [Contain](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Contain)
|
||||
- [ContainSubSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#ContainSubSlice)
|
||||
- [Chunk](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Chunk)
|
||||
- [Compact](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Compact)
|
||||
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Concat)
|
||||
- [Count](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Count)
|
||||
- [Difference](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Difference)
|
||||
- [DifferenceBy](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#DifferenceBy)
|
||||
- [DeleteByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#DeleteByIndex)
|
||||
- [Drop](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Drop)
|
||||
- [Every](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Every)
|
||||
- [Equal](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Equal)
|
||||
- [EqualWith](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#EqualWith)
|
||||
- [Filter](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Filter)
|
||||
- [Find](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Find)
|
||||
- [FindLast](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#FindLast)
|
||||
- [FlattenDeep](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#FlattenDeep)
|
||||
- [ForEach](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#ForEach)
|
||||
- [GroupBy](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#GroupBy)
|
||||
- [IntSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#IntSlice)
|
||||
- [IndexOf](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#IndexOf)
|
||||
- [LastIndexOf](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#LastIndexOf)
|
||||
- [InterfaceSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#InterfaceSlice)
|
||||
- [Intersection](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Intersection)
|
||||
- [InsertByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#InsertByIndex)
|
||||
- [Map](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Map)
|
||||
- [ReverseSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#ReverseSlice)
|
||||
- [Reduce](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Reduce)
|
||||
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Shuffle)
|
||||
- [SortByField](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#SortByField)
|
||||
- [Some](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Some)
|
||||
- [StringSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#StringSlice)
|
||||
- [ToSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#ToSlice)
|
||||
- [ToSlicePointer](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#ToSlice)
|
||||
- [Unique](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Unique)
|
||||
- [UniqueBy](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#UniqueBy)
|
||||
- [Union](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Union)
|
||||
- [UpdateByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#UpdateByIndex)
|
||||
- [Without](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Without)
|
||||
- [Join](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#Join)
|
||||
|
||||
### 13. Strutil package contains some functions to manipulate string.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "https://gutendex.com/books?"
|
||||
header := make(map[string]string)
|
||||
header["Content-Type"] = "application/json"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := netutil.HttpGet(url, header, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
- [After](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#After)
|
||||
- [AfterLast](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#AfterLast)
|
||||
- [Before](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Before)
|
||||
- [BeforeLast](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#BeforeLast)
|
||||
- [CamelCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#CamelCase)
|
||||
- [Capitalize](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Capitalize)
|
||||
- [ContainsAll](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#ContainsAll)
|
||||
- [ContainsAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#ContainsAny)
|
||||
- [IsString](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#IsString)
|
||||
- [KebabCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#KebabCase)
|
||||
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#UpperKebabCase)
|
||||
- [LowerFirst](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#LowerFirst)
|
||||
- [UpperFirst](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#UpperFirst)
|
||||
- [Pad](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Pad)
|
||||
- [PadEnd](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#PadEnd)
|
||||
- [PadStart](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#PadStart)
|
||||
- [Reverse](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Reverse)
|
||||
- [SnakeCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SnakeCase)
|
||||
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#UpperSnakeCase)
|
||||
- [SplitEx](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SplitEx)
|
||||
- [Wrap](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Wrap)
|
||||
- [Unwrap](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Unwrap)
|
||||
- [SplitWords](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SplitWords)
|
||||
- [WordCount](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#WordCount)
|
||||
- [RemoveNonPrintable](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#RemoveNonPrintable)
|
||||
- [StringToBytes](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#StringToBytes)
|
||||
- [BytesToString](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#BytesToString)
|
||||
- [IsBlank](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#IsBlank)
|
||||
- [HasPrefixAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HasPrefixAny)
|
||||
- [HasSuffixAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HasSuffixAny)
|
||||
- [IndexOffset](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#IndexOffset)
|
||||
- [ReplaceWithMap](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#ReplaceWithMap)
|
||||
- [Trim](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Trim)
|
||||
- [SplitAndTrim](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SplitAndTrim)
|
||||
- [HideString](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HideString)
|
||||
- [RemoveWhiteSpace](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#RemoveWhiteSpace)
|
||||
- [SubInBetween](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#SubInBetween)
|
||||
- [HammingDistance](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#HammingDistance)
|
||||
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Concat)
|
||||
- [Ellipsis](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Ellipsis)
|
||||
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Shuffle)
|
||||
- [Rotate](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Rotate)
|
||||
- [TemplateReplace](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#TemplateReplace)
|
||||
- [RegexMatchAllGroups](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#RegexMatchAllGroups)
|
||||
- [Cut](https://github.com/duke-git/lancet/blob/v1/docs/strutil.md#Cut)
|
||||
|
||||
|
||||
### 14. System package contain some functions about os, runtime, shell command.
|
||||
|
||||
```go
|
||||
func GetInternalIp() string //get internal ip
|
||||
func GetPublicIpInfo() (*PublicIpInfo, error) //get public ip info: country, region, isp, city, lat, lon, ip
|
||||
func IsPublicIP(IP net.IP) bool //判断ip是否为公共ip
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) //http get request
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) //http post request
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) //http put request
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete request
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch request
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string //convert map to url query string
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error //decode http response to specified interface
|
||||
import "github.com/duke-git/lancet/system"
|
||||
```
|
||||
|
||||
#### 8. random is for rand string and int generation
|
||||
#### Function list:
|
||||
|
||||
- Generate random string and int.
|
||||
- Usage: import "github.com/duke-git/lancet/random".
|
||||
- [IsWindows](https://github.com/duke-git/lancet/blob/v1/docs/system.md#IsWindows)
|
||||
- [IsLinux](https://github.com/duke-git/lancet/blob/v1/docs/system.md#IsLinux)
|
||||
- [IsMac](https://github.com/duke-git/lancet/blob/v1/docs/system.md#IsMac)
|
||||
- [GetOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system.md#GetOsEnv)
|
||||
- [SetOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system.md#SetOsEnv)
|
||||
- [RemoveOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system.md#RemoveOsEnv)
|
||||
- [CompareOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system.md#CompareOsEnv)
|
||||
- [ExecCommand](https://github.com/duke-git/lancet/blob/v1/docs/system.md#ExecCommand)
|
||||
- [GetOsBits](https://github.com/duke-git/lancet/blob/v1/docs/system.md#GetOsBits)
|
||||
|
||||
### 15. Validator package contains some functions for data validation.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr)
|
||||
}
|
||||
import "github.com/duke-git/lancet/validator"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### Function list:
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte //generate random []byte
|
||||
func RandInt(min, max int) int //generate random int
|
||||
func RandString(length int) string //generate random string
|
||||
```
|
||||
- [ContainChinese](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#ContainChinese)
|
||||
- [ContainLetter](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#ContainLetter)
|
||||
- [ContainLower](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#ContainLower)
|
||||
- [ContainUpper](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#ContainUpper)
|
||||
- [IsAlpha](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsAlpha)
|
||||
- [IsAllUpper](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsAllUpper)
|
||||
- [IsAllLower](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsAllLower)
|
||||
- [IsBase64](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsBase64)
|
||||
- [IsChineseMobile](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsChineseMobile)
|
||||
- [IsChineseIdNum](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsChineseIdNum)
|
||||
- [IsChinesePhone](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsChinesePhone)
|
||||
- [IsCreditCard](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsCreditCard)
|
||||
- [IsDns](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsDns)
|
||||
- [IsEmail](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsEmail)
|
||||
- [IsEmptyString](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsEmptyString)
|
||||
- [IsInt](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsInt)
|
||||
- [IsFloat](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsFloat)
|
||||
- [IsNumber](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsNumber)
|
||||
- [IsIntStr](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsIntStr)
|
||||
- [IsFloatStr](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsFloatStr)
|
||||
- [IsNumberStr](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsNumberStr)
|
||||
- [IsJSON](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsJSON)
|
||||
- [IsRegexMatch](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsRegexMatch)
|
||||
- [IsIp](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsIp)
|
||||
- [IsIpV4](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsIpV4)
|
||||
- [IsIpV6](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsIpV6)
|
||||
- [IsStrongPassword](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsStrongPassword)
|
||||
- [IsUrl](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsUrl)
|
||||
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsWeakPassword)
|
||||
- [IsZeroValue](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsZeroValue)
|
||||
- [IsGBK](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsGBK)
|
||||
- [IsASCII](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsASCII)
|
||||
- [IsPrintable](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsPrintable)
|
||||
- [IsBin](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsBin)
|
||||
- [IsHex](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsHex)
|
||||
- [IsBase64URL](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsBase64URL)
|
||||
- [IsJWT](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsJWT)
|
||||
- [IsVisa](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsVisa)
|
||||
- [IsMasterCard](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsMasterCard)
|
||||
- [IsAmericanExpress](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsAmericanExpress)
|
||||
- [IsUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsUnionPay)
|
||||
- [IsChinaUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator.md#IsChinaUnionPay)
|
||||
|
||||
#### 9. slice is for process slice
|
||||
|
||||
- Contain function for process slice.
|
||||
- Usage: import "github.com/duke-git/lancet/slice"
|
||||
- Due to the unstable support of generic, most of the slice processing function parameter and return value is interface {}. After go generic is stable, the related functions will be refactored.
|
||||
## How to Contribute
|
||||
|
||||
```go
|
||||
package main
|
||||
I really appreciate any code commits which make lancet lib powerful. Please follow the rules below to create your pull request.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 4, 3, 4, 6, 7, 3}
|
||||
uniqueNums, _ := slice.IntSlice(slice.Unique(nums))
|
||||
fmt.Println(uniqueNums) //[1 4 3 6 7]
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func Contain(slice interface{}, value interface{}) bool //check if the value is in the slice or not
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} //creates an slice of elements split into groups the length of `size`.
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //convert originalSlice to newSliceType
|
||||
func Difference(slice1, slice2 interface{}) interface{} //creates an slice of whose element not included in the other given slice
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1
|
||||
func Every(slice, function interface{}) bool //return true if all of the values in the slice pass the predicate function, function signature should be func(index int, value interface{}) bool
|
||||
func Filter(slice, function interface{}) interface{} //filter slice, function signature should be func(index int, value interface{}) bool
|
||||
func Find(slice, function interface{}) interface{} //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 .
|
||||
func IntSlice(slice interface{}) ([]int, error) //convert value to int slice
|
||||
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice
|
||||
func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices.
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //insert the element into slice at index.
|
||||
func Map(slice, function interface{}) interface{} //map lisce, function signature should be func(index int, value interface{}) interface{}
|
||||
func ReverseSlice(slice interface{}) //revere slice
|
||||
func Reduce(slice, function, zero interface{}) interface{} //reduce slice, function signature should be func(index int, value1, value2 interface{}) interface{}
|
||||
func SortByField(slice interface{}, field string, sortType ...string) error //sort struct slice by field
|
||||
func Some(slice, function interface{}) bool //return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool
|
||||
func StringSlice(slice interface{}) []string //convert value to string slice
|
||||
func Unique(slice interface{}) interface{} //remove duplicate elements in slice
|
||||
func Union(slices ...interface{}) interface{} //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index.
|
||||
func Without(slice interface{}, values ...interface{}) interface{} //creates a slice excluding all given values
|
||||
```
|
||||
|
||||
#### 10. strutil is for processing string
|
||||
|
||||
- Contain functions to precess string
|
||||
- Usage: import "github.com/duke-git/lancet/strutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
camelCaseStr := strutil.CamelCase(str)
|
||||
fmt.Println(camelCaseStr) //fooBar
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func After(s, char string) string //create substring in source string after position when char first appear
|
||||
func AfterLast(s, char string) string //create substring in source string after position when char last appear
|
||||
func Before(s, char string) string //create substring in source string before position when char first appear
|
||||
func BeforeLast(s, char string) string //create substring in source string before position when char last appear
|
||||
func CamelCase(s string) string //covert string to camelCase string. "foo bar" -> "fooBar"
|
||||
func Capitalize(s string) string //convert the first character of a string to upper case, "fOO" -> "Foo"
|
||||
func IsString(v interface{}) bool //check if the value data type is string or not
|
||||
func KebabCase(s string) string //covert string to kebab-case, "foo_Bar" -> "foo-bar"
|
||||
func LowerFirst(s string) string //convert the first character of string to lower case
|
||||
func PadEnd(source string, size int, padStr string) string //pads string on the right side if it's shorter than size
|
||||
func PadStart(source string, size int, padStr string) string//pads string on the left side if it's shorter than size
|
||||
func ReverseStr(s string) string //return string whose char order is reversed to the given string
|
||||
func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar"
|
||||
```
|
||||
|
||||
#### 11. validator is for data validation
|
||||
|
||||
- Contain function for data validation.
|
||||
- Usage: import "github.com/duke-git/lancet/validator".
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
isAlpha := validator.IsAlpha(str)
|
||||
fmt.Println(isAlpha) //false
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func ContainChinese(s string) bool //check if the string contain mandarin chinese
|
||||
func IsAlpha(s string) bool //checks if the string contains only letters (a-zA-Z)
|
||||
func IsBase64(base64 string) bool //check if the string is base64 string
|
||||
func IsChineseMobile(mobileNum string) bool //check if the string is chinese mobile number
|
||||
func IsChineseIdNum(id string) bool //check if the string is chinese id number
|
||||
func IsChinesePhone(phone string) bool //check if the string is chinese phone number
|
||||
func IsCreditCard(creditCart string) bool //check if the string is credit card
|
||||
func IsDns(dns string) bool //check if the string is dns
|
||||
func IsEmail(email string) bool //check if the string is a email address
|
||||
func IsEmptyString(s string) bool //check if the string is empty
|
||||
func IsFloatStr(s string) bool //check if the string can convert to a float
|
||||
func IsNumberStr(s string) bool //check if the string can convert to a number
|
||||
func IsRegexMatch(s, regex string) bool //check if the string match the regexp
|
||||
func IsIntStr(s string) bool //check if the string can convert to a integer
|
||||
func IsIp(ipstr string) bool //check if the string is a ip address
|
||||
func IsIpV4(ipstr string) bool //check if the string is a ipv4 address
|
||||
func IsIpV6(ipstr string) bool //check if the string is a ipv6 address
|
||||
func IsStrongPassword(password string, length int) bool //check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?><))
|
||||
func IsWeakPassword(password string) bool //check if the string is weak password(only letter or only number or letter + number)
|
||||
```
|
||||
1. Fork the repository.
|
||||
2. Create your feature branch.
|
||||
3. Commit your changes.
|
||||
4. Push to the branch
|
||||
5. Create new pull request.
|
||||
|
||||
841
README_zh-CN.md
841
README_zh-CN.md
@@ -1,47 +1,49 @@
|
||||
<div align="center">
|
||||
<h1 style="width: 100%; text-align: center;">Lancet</h1>
|
||||
<p style="font-size: 18px">
|
||||
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
||||
</p>
|
||||
<div align="center" style="text-align: center;">
|
||||
<div align=center>
|
||||
<img src="./logo.png" width="200" height="200"/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
[](https://codecov.io/gh/duke-git/lancet)
|
||||
[](https://github.com/duke-git/lancet/blob/main/LICENSE)
|
||||
[](https://github.com/duke-git/lancet/blob/v1/LICENSE)
|
||||
|
||||
</div>
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
<p style="font-size: 18px">
|
||||
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
||||
</p>
|
||||
|
||||
简体中文 | [English](./README.md)
|
||||
|
||||
</div>
|
||||
## 特性
|
||||
|
||||
### 特性
|
||||
- 👏 全面、高效、可复用
|
||||
- 💪 400+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
|
||||
- 💅 只依赖 go 标准库
|
||||
- 🌍 所有导出函数单元测试覆盖率 100%
|
||||
|
||||
- 👏 全面、高效、可复用
|
||||
- 💪 100+常用go工具函数,支持string、slice、datetime、net、crypt...
|
||||
- 💅 只依赖go标准库
|
||||
- 🌍 所有导出函数单元测试覆盖率100%
|
||||
|
||||
### 安装
|
||||
## 安装
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet
|
||||
```
|
||||
|
||||
### 用法
|
||||
## 用法
|
||||
|
||||
lancet是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入strutil包:
|
||||
lancet 是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入 strutil 包:
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
### 例子
|
||||
## 例子
|
||||
|
||||
此处以字符串工具函数ReverseStr(逆序字符串)为例,需要导入strutil包:
|
||||
此处以字符串工具函数 Reverse(逆序字符串)为例,需要导入 strutil 包:
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -53,441 +55,514 @@ import (
|
||||
|
||||
func main() {
|
||||
s := "hello"
|
||||
rs := strutil.ReverseStr(s)
|
||||
rs := strutil.Reverse(s)
|
||||
fmt.Println(rs) //olleh
|
||||
}
|
||||
```
|
||||
|
||||
### API文档
|
||||
## API 文档
|
||||
|
||||
#### 1. convertor数据转换包
|
||||
|
||||
- 转换函数支持常用数据类型之间的转换
|
||||
- 导入包:import "github.com/duke-git/lancet/convertor"
|
||||
|
||||
### 1. compare包提供几个轻量级的类型比较函数。
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "12.3"
|
||||
f, err := convertor.ToFloat(s)
|
||||
if err != nil {
|
||||
fmt.Errorf("error is %s", err.Error())
|
||||
}
|
||||
fmt.Println(f) // 12.3
|
||||
}
|
||||
import "github.com/duke-git/lancet/compare"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### Function list:
|
||||
|
||||
- [Equal](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#Equal)
|
||||
- [EqualValue](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#EqualValue)
|
||||
- [LessThan](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#LessThan)
|
||||
- [GreaterThan](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#GreaterThan)
|
||||
- [LessOrEqual](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#LessOrEqual)
|
||||
- [GreaterOrEqual](https://github.com/duke-git/lancet/blob/v1/docs/compare_zh-CN.md#GreaterOrEqual)
|
||||
|
||||
### 2. convertor转换器包支持一些常见的数据类型转换。
|
||||
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) //颜色值16进制转rgb
|
||||
func ColorRGBToHex(red, green, blue int) string //颜色值rgb转16进制
|
||||
func ToBool(s string) (bool, error) //字符串转成Bool
|
||||
func ToBytes(data interface{}) ([]byte, error) //interface转成byte slice
|
||||
func ToChar(s string) []string //字符串转成字符slice
|
||||
func ToFloat(value interface{}) (float64, error) //interface转成float64
|
||||
func ToInt(value interface{}) (int64, error) //interface转成int64
|
||||
func ToJson(value interface{}) (string, error) //interface转成json string
|
||||
func ToString(value interface{}) string //interface转成string
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) //struct串转成map, 需要设置struct tag `json`
|
||||
import "github.com/duke-git/lancet/convertor"
|
||||
```
|
||||
|
||||
#### 2. cryptor加解密包
|
||||
#### 函数列表:
|
||||
|
||||
- 加密函数支持md5, hmac, aes, des, ras
|
||||
- 导入包:import "github.com/duke-git/lancet/cryptor"
|
||||
- [ColorHexToRGB](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ColorHexToRGB)
|
||||
- [ColorRGBToHex](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ColorRGBToHex)
|
||||
- [ToBool](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToBool)
|
||||
- [ToBytes](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToBytes)
|
||||
- [ToChar](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToChar)
|
||||
- [ToChannel](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToChannel)
|
||||
- [ToInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToInt)
|
||||
- [ToJson](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToJson)
|
||||
- [ToString](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToString)
|
||||
- [StructToMap](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#StructToMap)
|
||||
- [MapToStruct](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#MapToStruct)
|
||||
- [EncodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#EncodeByte)
|
||||
- [DecodeByte](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#DecodeByte)
|
||||
- [DeepClone](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#DeepClone)
|
||||
- [CopyProperties](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#CopyProperties)
|
||||
- [ToInterface](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToInterface)
|
||||
- [Utf8ToGbk](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#Utf8ToGbk)
|
||||
- [GbkToUtf8](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#GbkToUtf8)
|
||||
- [ToStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToStdBase64)
|
||||
- [ToUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToUrlBase64)
|
||||
- [ToRawStdBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToRawStdBase64)
|
||||
- [ToRawUrlBase64](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToRawUrlBase64)
|
||||
- [ToBigInt](https://github.com/duke-git/lancet/blob/v1/docs/convertor_zh-CN.md#ToBigInt)
|
||||
|
||||
|
||||
### 3. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/cryptor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key))
|
||||
decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key))
|
||||
fmt.Println(string(decrypted)) // hello
|
||||
}
|
||||
import "github.com/duke-git/lancet/cryptor"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
- [AesEcbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesEcbEncrypt)
|
||||
- [AesEcbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesEcbDecrypt)
|
||||
- [AesCbcEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesCbcEncrypt)
|
||||
- [AesCbcDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesCbcDecrypt)
|
||||
- [AesCtrCrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesCtrCrypt)
|
||||
- [AesCfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesCfbEncrypt)
|
||||
- [AesCfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesCfbDecrypt)
|
||||
- [AesOfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesOfbEncrypt)
|
||||
- [AesOfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#AesOfbDecrypt)
|
||||
- [Base64StdEncode](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Base64StdEncode)
|
||||
- [Base64StdDecode](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Base64StdDecode)
|
||||
- [DesEcbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesEcbEncrypt)
|
||||
- [DesEcbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesEcbDecrypt)
|
||||
- [DesCbcEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesCbcEncrypt)
|
||||
- [DesCbcDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesCbcDecrypt)
|
||||
- [DesCtrCrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesCtrCrypt)
|
||||
- [DesCfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesCfbEncrypt)
|
||||
- [DesCfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesCfbDecrypt)
|
||||
- [DesOfbEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesOfbEncrypt)
|
||||
- [DesOfbDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#DesOfbDecrypt)
|
||||
- [HmacMd5](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacMd5)
|
||||
- [HmacMd5WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacMd5WithBase64)
|
||||
md#HmacSha256WithBase64)
|
||||
- [HmacSha1](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacSha1)
|
||||
- [HmacSha1WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacSha1WithBase64)
|
||||
- [HmacSha256](https://github.com/duke-git/lancet/blob/v1/docs/cryptor.md#HmacSha256)
|
||||
- [HmacSha256WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacSha256WithBase64)
|
||||
- [HmacSha512](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacSha512)
|
||||
- [HmacSha512WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#HmacSha512WithBase64)
|
||||
- [Md5String](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Md5String)
|
||||
- [Md5StringWithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Md5StringWithBase64)
|
||||
- [Md5Byte](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Md5Byte)
|
||||
- [Md5ByteWithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Md5ByteWithBase64)
|
||||
- [Md5File](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Md5File)
|
||||
- [Sha1](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha1)
|
||||
- [Sha1WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha1WithBase64)
|
||||
- [Sha256](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha256)
|
||||
- [Sha256WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha256WithBase64)
|
||||
- [Sha512](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha512)
|
||||
- [Sha512WithBase64](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#Sha512WithBase64)
|
||||
- [GenerateRsaKey](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#GenerateRsaKey)
|
||||
- [RsaEncrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaEncrypt)
|
||||
- [RsaDecrypt](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaDecrypt)
|
||||
- [GenerateRsaKeyPair](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#GenerateRsaKeyPair)
|
||||
- [RsaEncryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaEncryptOAEP)
|
||||
- [RsaDecryptOAEP](https://github.com/duke-git/lancet/blob/v1/docs/cryptor_zh-CN.md#RsaDecryptOAEP)
|
||||
|
||||
|
||||
### 4. datetime 日期时间处理包,格式化日期,比较日期。
|
||||
|
||||
```go
|
||||
func AesEcbEncrypt(data, key []byte) []byte //AES ECB模式加密
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte //AES ECB模式解密
|
||||
func AesCbcEncrypt(data, key []byte) []byte //AES CBC模式加密
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte //AES CBC模式解密
|
||||
func AesCtrCrypt(data, key []byte) []byte //AES CTR模式加密/解密
|
||||
func AesCfbEncrypt(data, key []byte) []byte //AES CFB模式加密
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte //AES CFB模式解密
|
||||
func AesOfbEncrypt(data, key []byte) []byte //AES OFB模式加密
|
||||
func AesOfbDecrypt(data, key []byte) []byte //AES OFB模式解密
|
||||
func Base64StdEncode(s string) string //base64编码
|
||||
func Base64StdDecode(s string) string //base64解码
|
||||
func DesCbcEncrypt(data, key []byte) []byte //DES CBC模式加密
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte //DES CBC模式解密
|
||||
func DesCtrCrypt(data, key []byte) []byte //DES CTR模式加密/解密
|
||||
func DesCfbEncrypt(data, key []byte) []byte //DES CFB模式加密
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte //DES CFB模式解密
|
||||
func DesOfbEncrypt(data, key []byte) []byte //DES OFB模式加密
|
||||
func DesOfbDecrypt(data, key []byte) []byte //DES OFB模式解密
|
||||
func HmacMd5(data, key string) string //获取hmac md5值
|
||||
func HmacSha1(data, key string) string //获取hmac sha1值
|
||||
func HmacSha256(data, key string) string //获取hmac sha256值
|
||||
func HmacSha512(data, key string) string //获取hmac sha512值
|
||||
func Sha1(data string) string //获取sha1值
|
||||
func Sha256(data string) string //获取sha256值
|
||||
func Sha512(data string) string //获取sha512值
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) //生成RSA私钥文件
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte //RSA加密
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA解密
|
||||
|
||||
import "github.com/duke-git/lancet/datetime"
|
||||
```
|
||||
|
||||
#### 3. datetime日期时间处理包
|
||||
#### 函数列表:
|
||||
|
||||
- 处理日期时间
|
||||
- 导入包:import "github.com/duke-git/lancet/datetime"
|
||||
- [AddDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#AddDay)
|
||||
- [AddHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#AddHour)
|
||||
- [AddMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#AddMinute)
|
||||
- [AddYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#AddYear)
|
||||
- [BeginOfMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfMinute)
|
||||
- [BeginOfHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfHour)
|
||||
- [BeginOfDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfDay)
|
||||
- [BeginOfWeek](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfWeek)
|
||||
- [BeginOfMonth](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfMonth)
|
||||
- [BeginOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#BeginOfYear)
|
||||
- [EndOfMinute](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfMinute)
|
||||
- [EndOfHour](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfHour)
|
||||
- [EndOfDay](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfDay)
|
||||
- [EndOfWeek](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfWeek)
|
||||
- [EndOfMonth](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfMonth)
|
||||
- [EndOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime.md#EndOfYear)
|
||||
- [GetNowDate](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GetNowDate)
|
||||
- [GetNowTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GetNowTime)
|
||||
- [GetNowDateTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GetNowDateTime)
|
||||
- [GetZeroHourTimestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GetZeroHourTimestamp)
|
||||
- [GetNightTimestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GetNightTimestamp)
|
||||
- [FormatTimeToStr](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#FormatTimeToStr)
|
||||
- [FormatStrToTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#FormatStrToTime)
|
||||
- [NewUnix](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#NewUnix)
|
||||
- [NewUnixNow](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#NewUnixNow)
|
||||
- [NewFormat](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#NewFormat)
|
||||
- [NewISO8601](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#NewISO8601)
|
||||
- [ToUnix](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#ToUnix)
|
||||
- [ToFormat](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#ToFormat)
|
||||
- [ToFormatForTpl](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#ToFormatForTpl)
|
||||
- [ToIso8601](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#ToIso8601)
|
||||
- [IsLeapYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#IsLeapYear)
|
||||
- [BetweenSeconds](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#BetweenSeconds)
|
||||
- [DayOfYear](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#DayOfYear)
|
||||
- [IsWeekend](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#IsWeekend)
|
||||
- [NowDateOrTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#NowDateOrTime)
|
||||
- [Timestamp](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#Timestamp)
|
||||
- [TimestampMilli](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampMilli)
|
||||
- [TimestampMicro](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampMicro)
|
||||
- [TimestampNano](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TimestampNano)
|
||||
- [TrackFuncTime](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#TrackFuncTime)
|
||||
- [DaysBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#DaysBetween)
|
||||
- [GenerateDatetimesBetween](https://github.com/duke-git/lancet/blob/v1/docs/datetime_zh-CN.md#GenerateDatetimesBetween)
|
||||
|
||||
### 5. fileutil 包支持文件基本操作。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
now := time.Now()
|
||||
s := datetime.FormatTimeToStr(now, "yyyy-mm-dd hh:mm:ss")
|
||||
fmt.Println(s) // 2021-11-24 11:16:55
|
||||
}
|
||||
import "github.com/duke-git/lancet/fileutil"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
- [ClearFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ClearFile)
|
||||
- [CreateFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#CreateFile)
|
||||
- [CreateDir](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#CreateDir)
|
||||
- [CopyFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#CopyFile)
|
||||
- [FileMode](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#FileMode)
|
||||
- [MiMeType](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#MiMeType)
|
||||
- [IsExist](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#IsExist)
|
||||
- [IsLink](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#IsLink)
|
||||
- [IsDir](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#IsDir)
|
||||
- [ListFileNames](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ListFileNames)
|
||||
- [RemoveFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#RemoveFile)
|
||||
- [ReadFileToString](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadFileToString)
|
||||
- [ReadFileByLine](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadFileByLine)
|
||||
- [Zip](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#Zip)
|
||||
- [ZipAppendEntry](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ZipAppendEntry)
|
||||
- [UnZip](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#UnZip)
|
||||
- [CurrentPath](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#CurrentPath)
|
||||
- [IsZipFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#IsZipFile)
|
||||
- [FileSize](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#FileSize)
|
||||
- [MTime](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#MTime)
|
||||
- [Sha](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#Sha)
|
||||
- [ReadCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadCsvFile)
|
||||
- [WriteCsvFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteCsvFile)
|
||||
- [WriteMapsToCsv](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteMapsToCsv)
|
||||
- [WriteStringToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteStringToFile)
|
||||
- [WriteBytesToFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#WriteBytesToFile)
|
||||
- [ReadFile](https://github.com/duke-git/lancet/blob/v1/docs/fileutil_zh-CN.md#ReadFile)
|
||||
|
||||
### 6. formatter 格式化器包含一些数据格式化处理方法。
|
||||
|
||||
```go
|
||||
func AddDay(t time.Time, day int64) time.Time //加减天数
|
||||
func AddHour(t time.Time, hour int64) time.Time //加减小时数
|
||||
func AddMinute(t time.Time, minute int64) time.Time //加减分钟数
|
||||
func GetNowDate() string //获取当天日期 格式yyyy-mm-dd
|
||||
func GetNowTime() string //获取当前时间 格式hh:mm:ss
|
||||
func GetNowDateTime() string //获取当前日期时间 格式yyyy-mm-dd hh:mm:ss
|
||||
func GetZeroHourTimestamp() int64 //获取当天零时时间戳(00:00)
|
||||
func GetNightTimestamp() int64 //获取当天23时时间戳(23:59)
|
||||
func FormatTimeToStr(t time.Time, format string) string //时间格式化字符串
|
||||
func FormatStrToTime(str, format string) time.Time //字符串转换成时间
|
||||
import "github.com/duke-git/lancet/formatter"
|
||||
```
|
||||
|
||||
#### 4. fileutil文件处理包
|
||||
#### 函数列表:
|
||||
|
||||
- 文件处理常用函数
|
||||
- 导入包:import "github.com/duke-git/lancet/fileutil"
|
||||
- [Comma](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#Comma)
|
||||
- [Pretty](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#Pretty)
|
||||
- [PrettyToWriter](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#PrettyToWriter)
|
||||
- [DecimalBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#DecimalBytes)
|
||||
- [BinaryBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#BinaryBytes)
|
||||
- [ParseDecimalBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#ParseDecimalBytes)
|
||||
- [ParseBinaryBytes](https://github.com/duke-git/lancet/blob/v1/docs/formatter_zh-CN.md#ParseBinaryBytes)
|
||||
|
||||
### 7. function 函数包控制函数执行流程,包含部分函数式编程。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(fileutil.IsDir("./")) // true
|
||||
}
|
||||
import "github.com/duke-git/lancet/function"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
- [After](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#After)
|
||||
- [Before](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Before)
|
||||
- [Curry](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Curry)
|
||||
- [Compose](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Compose)
|
||||
- [Debounced](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Debounced)
|
||||
- [Debounce](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Debounce)
|
||||
- [Throttle](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Throttle)
|
||||
- [Delay](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Delay)
|
||||
- [Pipeline](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Pipeline)
|
||||
- [Schedule](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Schedule)
|
||||
- [Watcher](https://github.com/duke-git/lancet/blob/v1/docs/function_zh-CN.md#Watcher)
|
||||
|
||||
### 8. mathutil 包实现了一些数学计算的函数。
|
||||
|
||||
```go
|
||||
func ClearFile(path string) error //清空文件内容
|
||||
func IsExist(path string) bool //判断文件/目录是否存在
|
||||
func CreateFile(path string) bool //创建文件
|
||||
func IsDir(path string) bool //判断是否为目录
|
||||
func RemoveFile(path string) error //删除文件
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error //复制文件
|
||||
func ListFileNames(path string) ([]string, error) //列出目录下所有文件名称
|
||||
func ReadFileToString(path string) (string, error) //读取文件内容为字符串
|
||||
func ReadFileByLine(path string)([]string, error) //按行读取文件内容
|
||||
import "github.com/duke-git/lancet/mathutil"
|
||||
```
|
||||
|
||||
#### 5. formatter格式化处理包
|
||||
#### Function list:
|
||||
|
||||
- 格式化相关处理函数
|
||||
- 导入包:import "github.com/duke-git/lancet/formatter"
|
||||
- [Exponent](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Exponent)
|
||||
- [Fibonacci](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Fibonacci)
|
||||
- [Factorial](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Factorial)
|
||||
- [Percent](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Percent)
|
||||
- [RoundToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RoundToFloat)
|
||||
- [RoundToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RoundToString)
|
||||
- [TruncRound](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#TruncRound)
|
||||
- [CeilToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#CeilToFloat)
|
||||
- [CeilToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#CeilToString)
|
||||
- [FloorToFloat](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#FloorToFloat)
|
||||
- [FloorToString](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#FloorToString)
|
||||
- [AngleToRadian](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#AngleToRadian)
|
||||
- [RadianToAngle](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#RadianToAngle)
|
||||
- [PointDistance](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#PointDistance)
|
||||
- [IsPrime](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#IsPrime)
|
||||
- [GCD](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#GCD)
|
||||
- [LCM](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#LCM)
|
||||
- [Cos](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Cos)
|
||||
- [Sin](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Sin)
|
||||
- [Log](https://github.com/duke-git/lancet/blob/v1/docs/mathutil_zh-CN.md#Log)
|
||||
### 9. netutil 网络包支持获取 ip 地址,发送 http 请求。
|
||||
|
||||
```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"
|
||||
}
|
||||
import "github.com/duke-git/lancet/netutil"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
- [ConvertMapToQueryString](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#ConvertMapToQueryString)
|
||||
- [EncodeUrl](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#EncodeUrl)
|
||||
- [GetInternalIp](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#GetInternalIp)
|
||||
- [GetIps](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetIps)
|
||||
- [GetMacAddrs](https://github.com/duke-git/lancet/blob/v1/docs/netutil.md#GetMacAddrs)
|
||||
- [GetPublicIpInfo](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#GetPublicIpInfo)
|
||||
- [GetRequestPublicIp](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#GetRequestPublicIp)
|
||||
- [IsPublicIP](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#IsPublicIP)
|
||||
- [IsInternalIP](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#IsInternalIP)
|
||||
- [HttpGet](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#HttpGet)
|
||||
- [HttpDelete](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#HttpDelete)
|
||||
- [HttpPost](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#HttpPost)
|
||||
- [HttpPut](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#HttpPut)
|
||||
- [HttpPatch](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#HttpPatch)
|
||||
- [ParseHttpResponse](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#ParseHttpResponse)
|
||||
- [UploadFile](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#UploadFile)
|
||||
- [DownloadFile](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#DownloadFile)
|
||||
- [IsPingConnected](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#IsPingConnected)
|
||||
- [IsTelnetConnected](https://github.com/duke-git/lancet/blob/v1/docs/netutil_zh-CN.md#IsTelnetConnected)
|
||||
|
||||
### 10. random 随机数生成器包,可以生成随机[]bytes, int, string。
|
||||
|
||||
```go
|
||||
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
|
||||
import "github.com/duke-git/lancet/random"
|
||||
```
|
||||
|
||||
#### 6. function包可以控制函数执行,支持部分函数式编程
|
||||
#### 函数列表:
|
||||
|
||||
- 控制函数执行,支持部分函数式编程
|
||||
- 导入包:import "github.com/duke-git/lancet/function"
|
||||
- [RandBool](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBool)
|
||||
- [RandBoolSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBoolSlice)
|
||||
- [RandBytes](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandBytes)
|
||||
- [RandInt](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandInt)
|
||||
- [RandIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandIntSlice)
|
||||
- [RandString](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandString)
|
||||
- [RandStringSlice](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandStringSlice)
|
||||
- [RandFloat](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandFloat)
|
||||
- [RandFloats](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandFloats)
|
||||
- [RandUpper](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandUpper)
|
||||
- [RandLower](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandLower)
|
||||
- [RandNumeral](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandNumeral)
|
||||
- [RandNumeralOrLetter](https://github.com/duke-git/lancet/blob/v1/docs/random_zh-CN.md#RandNumeralOrLetter)
|
||||
- [UUIdV4](https://github.com/duke-git/lancet/blob/v1/docs/random.md#UUIdV4)
|
||||
- [RandUniqueIntSlice](https://github.com/duke-git/lancet/blob/v1/docs/random.md#RandUniqueIntSlice)
|
||||
|
||||
### 11. retry 重试执行函数直到函数运行成功或被 context cancel。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var print = func(s string) {
|
||||
fmt.Println(s)
|
||||
}
|
||||
function.Delay(2*time.Second, print, "hello world")
|
||||
}
|
||||
import "github.com/duke-git/lancet/retry"
|
||||
```
|
||||
|
||||
- Function list:
|
||||
#### 函数列表:
|
||||
|
||||
- [Context](https://github.com/duke-git/lancet/blob/v1/docs/retry_zh-CN.md#Context)
|
||||
- [Retry](https://github.com/duke-git/lancet/blob/v1/docs/retry_zh-CN.md#Retry)
|
||||
- [RetryFunc](https://github.com/duke-git/lancet/blob/v1/docs/retry_zh-CN.md#RetryFunc)
|
||||
- [RetryDuration](https://github.com/duke-git/lancet/blob/v1/docs/retry_zh-CN.md#RetryDuration)
|
||||
- [RetryTimes](https://github.com/duke-git/lancet/blob/v1/docs/retry_zh-CN.md#RetryTimes)
|
||||
|
||||
### 12. slice 包包含操作切片的方法集合。
|
||||
|
||||
```go
|
||||
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //创建一个函数, 只有在运行了n次之后才有效果
|
||||
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //创建一个函数,调用不超过n次。 当n已经达到时,最后一个函数调用的结果将被记住并返回
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //函数柯里化
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //从右至左组合函数
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //延迟调用函数
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //每隔duration时间调用函数, 关闭返回通道可以停止调用
|
||||
import "github.com/duke-git/lancet/slice"
|
||||
```
|
||||
|
||||
#### 7. netutil网络处理包
|
||||
#### 函数列表:
|
||||
|
||||
- 处理ip, http请求相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/netutil"
|
||||
- http方法params参数顺序:header, query string, body, httpclient
|
||||
- [AppendIfAbsent](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#AppendIfAbsent)
|
||||
- [Contain](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Contain)
|
||||
- [ContainSubSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#ContainSubSlice)
|
||||
- [Chunk](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Chunk)
|
||||
- [Compact](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Compact)
|
||||
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Concat)
|
||||
- [Count](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Count)
|
||||
- [Difference](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Difference)
|
||||
- [DifferenceBy](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#DifferenceBy)
|
||||
- [DeleteByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#DeleteByIndex)
|
||||
- [Drop](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Drop)
|
||||
- [Every](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Every)
|
||||
- [Equal](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Equal)
|
||||
- [EqualWith](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#EqualWith)
|
||||
- [Filter](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Filter)
|
||||
- [Find](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Find)
|
||||
- [FindLast](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#FindLast)
|
||||
- [FlattenDeep](https://github.com/duke-git/lancet/blob/v1/docs/slice.md#FlattenDeep)
|
||||
- [ForEach](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#ForEach)
|
||||
- [GroupBy](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#GroupBy)
|
||||
- [IntSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#IntSlice)
|
||||
- [IndexOf](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#IndexOf)
|
||||
- [LastIndexOf](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#LastIndexOf)
|
||||
- [InterfaceSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#InterfaceSlice)
|
||||
- [Intersection](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Intersection)
|
||||
- [InsertByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#InsertByIndex)
|
||||
- [Map](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Map)
|
||||
- [ReverseSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#ReverseSlice)
|
||||
- [Reduce](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Reduce)
|
||||
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Shuffle)
|
||||
- [SortByField](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#SortByField)
|
||||
- [Some](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Some)
|
||||
- [StringSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#StringSlice)
|
||||
- [ToSlice](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#ToSlice)
|
||||
- [ToSlicePointer](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#ToSlice)
|
||||
- [Unique](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Unique)
|
||||
- [UniqueBy](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#UniqueBy)
|
||||
- [Union](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Union)
|
||||
- [UpdateByIndex](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#UpdateByIndex)
|
||||
- [Without](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Without)
|
||||
- [Join](https://github.com/duke-git/lancet/blob/v1/docs/slice_zh-CN.md#Join)
|
||||
|
||||
### 13. strutil 包含处理字符串的相关函数。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "https://gutendex.com/books?"
|
||||
header := make(map[string]string)
|
||||
header["Content-Type"] = "application/json"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := netutil.HttpGet(url, header, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
- [After](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#After)
|
||||
- [AfterLast](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#AfterLast)
|
||||
- [Before](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Before)
|
||||
- [BeforeLast](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#BeforeLast)
|
||||
- [CamelCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#CamelCase)
|
||||
- [Capitalize](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Capitalize)
|
||||
- [ContainsAll](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#ContainsAll)
|
||||
- [ContainsAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#ContainsAny)
|
||||
- [IsString](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#IsString)
|
||||
- [KebabCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#KebabCase)
|
||||
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#UpperKebabCase)
|
||||
- [LowerFirst](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#LowerFirst)
|
||||
- [UpperFirst](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#UpperFirst)
|
||||
- [Pad](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Pad)
|
||||
- [PadEnd](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#PadEnd)
|
||||
- [PadStart](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#PadStart)
|
||||
- [Reverse](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Reverse)
|
||||
- [SnakeCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SnakeCase)
|
||||
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#UpperSnakeCase)
|
||||
- [SplitEx](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SplitEx)
|
||||
- [Wrap](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Wrap)
|
||||
- [Unwrap](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Unwrap)
|
||||
- [SplitWords](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SplitWords)
|
||||
- [WordCount](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#WordCount)
|
||||
- [RemoveNonPrintable](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#RemoveNonPrintable)
|
||||
- [StringToBytes](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#StringToBytes)
|
||||
- [BytesToString](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#BytesToString)
|
||||
- [IsBlank](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#IsBlank)
|
||||
- [HasPrefixAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HasPrefixAny)
|
||||
- [HasSuffixAny](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HasSuffixAny)
|
||||
- [IndexOffset](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#IndexOffset)
|
||||
- [ReplaceWithMap](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#ReplaceWithMap)
|
||||
- [Trim](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Trim)
|
||||
- [SplitAndTrim](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SplitAndTrim)
|
||||
- [HideString](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HideString)
|
||||
- [RemoveWhiteSpace](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#RemoveWhiteSpace)
|
||||
- [SubInBetween](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#SubInBetween)
|
||||
- [HammingDistance](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#HammingDistance)
|
||||
- [Concat](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Concat)
|
||||
- [Ellipsis](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Ellipsis)
|
||||
- [Shuffle](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Shuffle)
|
||||
- [Rotate](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Rotate)
|
||||
- [TemplateReplace](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#TemplateReplace)
|
||||
- [RegexMatchAllGroups](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#RegexMatchAllGroups)
|
||||
- [Cut](https://github.com/duke-git/lancet/blob/v1/docs/strutil_zh-CN.md#Cut)
|
||||
|
||||
### 14. system 包含 os, runtime, shell command 相关函数。
|
||||
|
||||
```go
|
||||
func GetInternalIp() string //获取内部ip
|
||||
func GetPublicIpInfo() (*PublicIpInfo, error) //获取公共ip信息: country, region, isp, city, lat, lon, ip
|
||||
func IsPublicIP(IP net.IP) bool //判断ip是否为公共ip
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) //http get请求
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) //http post请求
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) //http put请求
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete请求
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch请求
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string //将map转换成url query string
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error //将http响应解码成特定interface
|
||||
import "github.com/duke-git/lancet/system"
|
||||
```
|
||||
|
||||
#### 8. random随机数处理包
|
||||
#### 函数列表:
|
||||
|
||||
- 生成和处理随机数
|
||||
- 导入包:import "github.com/duke-git/lancet/random"
|
||||
- [IsWindows](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#IsWindows)
|
||||
- [IsLinux](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#IsLinux)
|
||||
- [IsMac](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#IsMac)
|
||||
- [GetOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#GetOsEnv)
|
||||
- [SetOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#SetOsEnv)
|
||||
- [RemoveOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#RemoveOsEnv)
|
||||
- [CompareOsEnv](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#CompareOsEnv)
|
||||
- [ExecCommand](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#ExecCommand)
|
||||
- [GetOsBits](https://github.com/duke-git/lancet/blob/v1/docs/system_zh-CN.md#GetOsBits)
|
||||
|
||||
### 15. validator 验证器包,包含常用字符串格式验证函数。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr)
|
||||
}
|
||||
import "github.com/duke-git/lancet/validator"
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
#### 函数列表:
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte //生成随机[]byte
|
||||
func RandInt(min, max int) int //生成随机int
|
||||
func RandString(length int) string //生成随机string
|
||||
```
|
||||
- [ContainChinese](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#ContainChinese)
|
||||
- [ContainLetter](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#ContainLetter)
|
||||
- [ContainLower](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#ContainLower)
|
||||
- [ContainUpper](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#ContainUpper)
|
||||
- [IsAlpha](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsAlpha)
|
||||
- [IsAllUpper](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsAllUpper)
|
||||
- [IsAllLower](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsAllLower)
|
||||
- [IsBase64](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsBase64)
|
||||
- [IsChineseMobile](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsChineseMobile)
|
||||
- [IsChineseIdNum](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsChineseIdNum)
|
||||
- [IsChinesePhone](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsChinesePhone)
|
||||
- [IsCreditCard](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsCreditCard)
|
||||
- [IsDns](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsDns)
|
||||
- [IsEmail](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsEmail)
|
||||
- [IsEmptyString](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsEmptyString)
|
||||
- [IsInt](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsInt)
|
||||
- [IsFloat](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsFloat)
|
||||
- [IsNumber](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsNumber)
|
||||
- [IsIntStr](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsIntStr)
|
||||
- [IsFloatStr](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsFloatStr)
|
||||
- [IsNumberStr](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsNumberStr)
|
||||
- [IsJSON](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsJSON)
|
||||
- [IsRegexMatch](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsRegexMatch)
|
||||
- [IsIp](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsIp)
|
||||
- [IsIpV4](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsIpV4)
|
||||
- [IsIpV6](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsIpV6)
|
||||
- [IsStrongPassword](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsStrongPassword)
|
||||
- [IsUrl](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsUrl)
|
||||
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsWeakPassword)
|
||||
- [IsZeroValue](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsZeroValue)
|
||||
- [IsGBK](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsGBK)
|
||||
- [IsASCII](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsASCII)
|
||||
- [IsPrintable](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsPrintable)
|
||||
- [IsBin](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsBin)
|
||||
- [IsHex](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsHex)
|
||||
- [IsBase64URL](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsBase64URL)
|
||||
- [IsJWT](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsJWT)
|
||||
- [IsVisa](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsVisa)
|
||||
- [IsMasterCard](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsMasterCard)
|
||||
- [IsAmericanExpress](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsAmericanExpress)
|
||||
- [IsUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsUnionPay)
|
||||
- [IsChinaUnionPay](https://github.com/duke-git/lancet/blob/v1/docs/validator_zh-CN.md#IsChinaUnionPay)
|
||||
|
||||
#### 9. slice切片操作包
|
||||
## 如何贡献代码
|
||||
|
||||
- 切片操作相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/slice"
|
||||
- 由于go目前对范型支持不稳定,slice处理函数参数和返回值大部分为interface{}, 待范型特性稳定后,会重构相关函数
|
||||
非常感激任何的代码提交以使 lancet 的功能越来越强大。创建 pull request 时请遵守以下规则。
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 4, 3, 4, 6, 7, 3}
|
||||
uniqueNums, _ := slice.IntSlice(slice.Unique(nums))
|
||||
fmt.Println(uniqueNums) //[1 4 3 6 7]
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func Contain(slice interface{}, value interface{}) bool //判断slice是否包含value
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} //均分slice
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //将originalSlice转换为 newSliceType
|
||||
func Difference(slice1, slice2 interface{}) interface{} //返回
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值
|
||||
func Every(slice, function interface{}) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名:func(index int, value interface{}) bool
|
||||
func Find(slice, function interface{}) interface{} //查找slice中第一个符合条件的元素,函数签名:func(index int, value interface{}) bool
|
||||
func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名:func(index int, value interface{}) bool
|
||||
func IntSlice(slice interface{}) ([]int, error) //转成int切片
|
||||
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
|
||||
func Intersection(slices ...interface{}) interface{} //slice交集,去重
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置插入value
|
||||
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名:func(index int, value interface{}) interface{}
|
||||
func ReverseSlice(slice interface{}) //反转切片
|
||||
func Reduce(slice, function, zero interface{}) interface{} //切片reduce操作, 函数签名:func(index int, value1, value2 interface{}) interface{}
|
||||
func Some(slice, function interface{}) bool //slice中任意一个元素都符合函数条件时返回true, 否则返回false. 函数签名:func(index int, value interface{}) bool
|
||||
func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序
|
||||
func StringSlice(slice interface{}) []string //转为string切片
|
||||
func Unique(slice interface{}) interface{} //去重切片
|
||||
func Union(slices ...interface{}) interface{} //slice并集, 去重
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value
|
||||
func Without(slice interface{}, values ...interface{}) interface{} //slice去除values
|
||||
```
|
||||
|
||||
#### 10. strutil字符串处理包
|
||||
|
||||
- 字符串操作相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/strutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
camelCaseStr := strutil.CamelCase(str)
|
||||
fmt.Println(camelCaseStr) //fooBar
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func After(s, char string) string //截取字符串中char第一次出现之后的字符串
|
||||
func AfterLast(s, char string) string //截取字符串中char最后一次出现之后的字符串
|
||||
func Before(s, char string) string //截取字符串中char第一次出现之前的字符串
|
||||
func BeforeLast(s, char string) string //截取字符串中char最后一次出现之前的字符串
|
||||
func CamelCase(s string) string //字符串转为cameCase, "foo bar" -> "fooBar"
|
||||
func Capitalize(s string) string //字符串转为Capitalize, "fOO" -> "Foo"
|
||||
func IsString(v interface{}) bool //判断是否是字符串
|
||||
func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-bar"
|
||||
func LowerFirst(s string) string //字符串的第一个字母转为小写字母
|
||||
func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符
|
||||
func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符
|
||||
func ReverseStr(s string) string //字符串逆袭
|
||||
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
|
||||
```
|
||||
|
||||
#### 11. validator验证器包
|
||||
|
||||
- 数据校验相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/validator"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
isAlpha := validator.IsAlpha(str)
|
||||
fmt.Println(isAlpha) //false
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func ContainChinese(s string) bool //判断字符串中是否含有中文字符
|
||||
func IsAlpha(s string) bool //判断字符串是否只含有字母
|
||||
func IsBase64(base64 string) bool //判断字符串是base64
|
||||
func IsChineseMobile(mobileNum string) bool //判断字符串是否是手机号
|
||||
func IsChineseIdNum(id string) bool //判断字符串是否是身份证号
|
||||
func IsChinesePhone(phone string) bool //判断字符串是否是座机电话号码
|
||||
func IsCreditCard(creditCart string) bool //判断字符串是否是信用卡
|
||||
func IsDns(dns string) bool //判断字符串是否是DNS
|
||||
func IsEmail(email string) bool //判断字符串是否是邮箱
|
||||
func IsEmptyString(s string) bool //判断字符串是否为空
|
||||
func IsFloatStr(s string) bool //判断字符串是否可以转成float
|
||||
func IsNumberStr(s string) bool //判断字符串是否可以转成数字
|
||||
func IsRegexMatch(s, regex string) bool //判断字符串是否match正则表达式
|
||||
func IsIntStr(s string) bool //判断字符串是否可以转成整数
|
||||
func IsIp(ipstr string) bool //判断字符串是否是ip
|
||||
func IsIpV4(ipstr string) bool //判断字符串是否是ipv4
|
||||
func IsIpV6(ipstr string) bool //判断字符串是否是ipv6
|
||||
func IsStrongPassword(password string, length int) bool //判断字符串是否是强密码(大小写字母+数字+特殊字符)
|
||||
func IsWeakPassword(password string) bool //判断字符串是否是弱密码(只有字母或数字)
|
||||
```
|
||||
1. Fork lancet 仓库。
|
||||
2. 创建自己的特性分支。
|
||||
3. 提交变更。
|
||||
4. Push 分支。
|
||||
5. 创建新的 pull request。
|
||||
|
||||
58
compare/compare.go
Normal file
58
compare/compare.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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 interface{} type.
|
||||
// reference: https://github.com/stretchr/testify
|
||||
package compare
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
// 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)
|
||||
func Equal(left, right interface{}) bool {
|
||||
return compareValue(equal, left, right)
|
||||
}
|
||||
|
||||
// EqualValue checks if two values are equal or not. (check value only)
|
||||
func EqualValue(left, right interface{}) bool {
|
||||
ls, rs := convertor.ToString(left), convertor.ToString(right)
|
||||
return ls == rs
|
||||
}
|
||||
|
||||
// LessThan checks if value `left` less than value `right`.
|
||||
func LessThan(left, right interface{}) bool {
|
||||
return compareValue(lessThan, left, right)
|
||||
}
|
||||
|
||||
// GreaterThan checks if value `left` greater than value `right`.
|
||||
func GreaterThan(left, right interface{}) bool {
|
||||
return compareValue(greaterThan, left, right)
|
||||
}
|
||||
|
||||
// LessOrEqual checks if value `left` less than or equal to value `right`.
|
||||
func LessOrEqual(left, right interface{}) bool {
|
||||
return compareValue(lessOrEqual, left, right)
|
||||
}
|
||||
|
||||
// GreaterOrEqual checks if value `left` greater than or equal to value `right`.
|
||||
func GreaterOrEqual(left, right interface{}) bool {
|
||||
return compareValue(greaterOrEqual, left, right)
|
||||
}
|
||||
170
compare/compare_example_test.go
Normal file
170
compare/compare_example_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
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
|
||||
}
|
||||
297
compare/compare_internal.go
Normal file
297
compare/compare_internal.go
Normal file
@@ -0,0 +1,297 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
func compareValue(operator string, left, right interface{}) 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())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func compareRefValue(operator string, leftObj, rightObj interface{}, kind reflect.Kind) bool {
|
||||
leftVal, rightVal := reflect.ValueOf(leftObj), reflect.ValueOf(rightObj)
|
||||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
|
||||
// compare time
|
||||
// fix: issue #275
|
||||
if canConvert(leftObj, 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 canConvert(leftObj, 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.Compare(bytesObj1, bytesObj2) == 0 {
|
||||
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 interface{}) 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
|
||||
}
|
||||
|
||||
// canConvert checks if the value can be converted to the target type
|
||||
func canConvert(value interface{}, targetType reflect.Type) bool {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
}
|
||||
}()
|
||||
v.Convert(targetType)
|
||||
return true
|
||||
}
|
||||
145
compare/compare_test.go
Normal file
145
compare/compare_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEqual")
|
||||
|
||||
assert.Equal(true, Equal(1, 1))
|
||||
assert.Equal(true, Equal(int64(1), int64(1)))
|
||||
assert.Equal(true, Equal("a", "a"))
|
||||
assert.Equal(true, Equal(true, true))
|
||||
assert.Equal(true, Equal([]int{1, 2, 3}, []int{1, 2, 3}))
|
||||
assert.Equal(true, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}))
|
||||
|
||||
assert.Equal(false, Equal(1, 2))
|
||||
assert.Equal(false, Equal(1, int64(1)))
|
||||
assert.Equal(false, Equal("a", "b"))
|
||||
assert.Equal(false, Equal(true, false))
|
||||
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
|
||||
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
time3 := time1.Add(time.Second)
|
||||
|
||||
assert.Equal(false, Equal(time1, time2))
|
||||
assert.Equal(true, Equal(time2, time3))
|
||||
|
||||
st1 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
st2 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
st3 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a1",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
assert.Equal(true, Equal(st1, st2))
|
||||
assert.Equal(false, Equal(st1, st3))
|
||||
}
|
||||
|
||||
func TestEqualValue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEqualValue")
|
||||
|
||||
assert.Equal(true, EqualValue(1, 1))
|
||||
assert.Equal(true, EqualValue(int(1), int64(1)))
|
||||
assert.Equal(true, EqualValue(1, "1"))
|
||||
|
||||
assert.Equal(false, EqualValue(1, "2"))
|
||||
}
|
||||
|
||||
func TestLessThan(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestLessThan")
|
||||
|
||||
tests := []struct {
|
||||
left interface{}
|
||||
right interface{}
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestGreaterThan")
|
||||
|
||||
assert.Equal(true, GreaterThan(2, 1))
|
||||
assert.Equal(true, GreaterThan(2.2, 1.1))
|
||||
assert.Equal(true, GreaterThan("b", "a"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, GreaterThan(time2, time1))
|
||||
|
||||
assert.Equal(false, GreaterThan(1, 2))
|
||||
assert.Equal(false, GreaterThan(int64(2), 1))
|
||||
assert.Equal(false, GreaterThan("b", "c"))
|
||||
}
|
||||
|
||||
func TestLessOrEqual(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLessOrEqual")
|
||||
|
||||
assert.Equal(true, LessOrEqual(1, 2))
|
||||
assert.Equal(true, LessOrEqual(1, 1))
|
||||
assert.Equal(true, LessOrEqual(1.1, 2.2))
|
||||
assert.Equal(true, LessOrEqual("a", "b"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, LessOrEqual(time1, time2))
|
||||
|
||||
assert.Equal(false, LessOrEqual(2, 1))
|
||||
assert.Equal(false, LessOrEqual(1, int64(2)))
|
||||
}
|
||||
|
||||
func TestGreaterOrEqual(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestGreaterThan")
|
||||
|
||||
assert.Equal(true, GreaterOrEqual(2, 1))
|
||||
assert.Equal(true, GreaterOrEqual(1, 1))
|
||||
assert.Equal(true, GreaterOrEqual(2.2, 1.1))
|
||||
assert.Equal(true, GreaterOrEqual("b", "b"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, GreaterOrEqual(time2, time1))
|
||||
|
||||
assert.Equal(false, GreaterOrEqual(1, 2))
|
||||
assert.Equal(false, GreaterOrEqual(int64(2), 1))
|
||||
assert.Equal(false, GreaterOrEqual("b", "c"))
|
||||
}
|
||||
@@ -6,13 +6,22 @@ package convertor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// ToBool convert string to a boolean
|
||||
@@ -21,14 +30,44 @@ func ToBool(s string) (bool, error) {
|
||||
}
|
||||
|
||||
// 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
|
||||
func ToBytes(value interface{}) ([]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
|
||||
@@ -45,54 +84,56 @@ func ToChar(s string) []string {
|
||||
|
||||
// ToString convert value to string
|
||||
func ToString(value interface{}) string {
|
||||
var res string
|
||||
if value == nil {
|
||||
return res
|
||||
return ""
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
res = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
|
||||
switch val := value.(type) {
|
||||
case float32:
|
||||
res = strconv.FormatFloat(float64(v), 'f', -1, 64)
|
||||
return strconv.FormatFloat(float64(val), 'f', -1, 32)
|
||||
case float64:
|
||||
return strconv.FormatFloat(val, 'f', -1, 64)
|
||||
case int:
|
||||
res = strconv.Itoa(v)
|
||||
case uint:
|
||||
res = strconv.Itoa(int(v))
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int8:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint8:
|
||||
res = strconv.Itoa(int(v))
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int16:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint16:
|
||||
res = strconv.Itoa(int(v))
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int32:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint32:
|
||||
res = strconv.Itoa(int(v))
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int64:
|
||||
res = strconv.FormatInt(v, 10)
|
||||
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:
|
||||
res = strconv.FormatUint(v, 10)
|
||||
return strconv.FormatUint(val, 10)
|
||||
case string:
|
||||
res = value.(string)
|
||||
return val
|
||||
case []byte:
|
||||
res = string(value.([]byte))
|
||||
return string(val)
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
res = string(newValue)
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ToJson convert value to a valid json string
|
||||
func ToJson(value interface{}) (string, error) {
|
||||
res, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
res = []byte("")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(res), err
|
||||
return string(res), nil
|
||||
}
|
||||
|
||||
// ToFloat convert value to a float64, if input is not a float return 0.0 and error
|
||||
@@ -157,6 +198,7 @@ func StructToMap(value interface{}) (map[string]interface{}, error) {
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
v = v.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
|
||||
@@ -208,3 +250,247 @@ func ColorRGBToHex(red, green, blue int) string {
|
||||
|
||||
return "#" + r + g + b
|
||||
}
|
||||
|
||||
// ToChannel convert a array of elements to a read-only channels
|
||||
func ToChannel(array []interface{}) <-chan interface{} {
|
||||
ch := make(chan interface{})
|
||||
|
||||
go func() {
|
||||
for _, item := range array {
|
||||
ch <- item
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// EncodeByte encode data to byte
|
||||
func EncodeByte(data interface{}) ([]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 data to target object
|
||||
func DecodeByte(data []byte, target interface{}) 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
|
||||
func DeepClone(src interface{}) interface{} {
|
||||
c := cloner{
|
||||
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
|
||||
}
|
||||
result := c.clone(reflect.ValueOf(src))
|
||||
if result.Kind() == reflect.Invalid {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result.Interface()
|
||||
}
|
||||
|
||||
// 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.
|
||||
func CopyProperties(dst, src interface{}) 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.
|
||||
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.
|
||||
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.
|
||||
func GbkToUtf8(bs []byte) ([]byte, error) {
|
||||
r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder())
|
||||
b, err := io.ReadAll(r)
|
||||
return b, err
|
||||
}
|
||||
|
||||
// MapToStruct converts map to struct
|
||||
func MapToStruct(m map[string]interface{}, structObj interface{}) error {
|
||||
for k, v := range m {
|
||||
err := setStructField(structObj, k, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToStdBase64 convert data to standard base64 encoding.
|
||||
func ToStdBase64(value interface{}) 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.
|
||||
func ToUrlBase64(value interface{}) 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.
|
||||
func ToRawStdBase64(value interface{}) 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.
|
||||
func ToRawUrlBase64(value interface{}) 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: todo
|
||||
func ToBigInt(v interface{}) (*big.Int, error) {
|
||||
result := new(big.Int)
|
||||
|
||||
switch v := (v).(type) {
|
||||
case int:
|
||||
result.SetInt64(int64(v))
|
||||
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))
|
||||
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
|
||||
}
|
||||
|
||||
298
convertor/convertor_internal.go
Normal file
298
convertor/convertor_internal.go
Normal file
@@ -0,0 +1,298 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
|
||||
func setStructField(structObj interface{}, fieldName string, fieldValue interface{}) 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() {
|
||||
|
||||
// fix: issue #275
|
||||
if canConvert(fieldValue, fieldVal.Type()) {
|
||||
fieldVal.Set(val.Convert(fieldVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
if m, ok := fieldValue.(map[string]interface{}); 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 interface{}, jsonTag string) string {
|
||||
s := reflect.TypeOf(structObj).Elem()
|
||||
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
field := s.Field(i)
|
||||
tag := field.Tag
|
||||
name := tag.Get("json")
|
||||
|
||||
if name == jsonTag {
|
||||
return field.Name
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func canConvert(value interface{}, targetType reflect.Type) bool {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
}
|
||||
}()
|
||||
v.Convert(targetType)
|
||||
return true
|
||||
}
|
||||
@@ -1,14 +1,23 @@
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func TestToChar(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToChar")
|
||||
|
||||
cases := []string{"", "abc", "1 2#3"}
|
||||
expected := [][]string{
|
||||
{""},
|
||||
@@ -16,49 +25,51 @@ func TestToChar(t *testing.T) {
|
||||
{"1", " ", "2", "#", "3"},
|
||||
}
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := ToChar(cases[i])
|
||||
if !reflect.DeepEqual(res, expected[i]) {
|
||||
utils.LogFailedTestInfo(t, "ToChar", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(expected[i], ToChar(cases[i]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBool(t *testing.T) {
|
||||
cases := []string{"true", "True", "false", "False", "0", "1", "123"}
|
||||
expected := []bool{true, true, false, false, false, true, false}
|
||||
assert := internal.NewAssert(t, "TestToBool")
|
||||
|
||||
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
|
||||
expected := []bool{true, true, true, false, false, false, false, false, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToBool(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToBool", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual, _ := ToBool(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToBytes")
|
||||
|
||||
cases := []interface{}{
|
||||
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++ {
|
||||
res, _ := ToBytes(cases[i])
|
||||
fmt.Println(res)
|
||||
if !reflect.DeepEqual(res, expected[i]) {
|
||||
utils.LogFailedTestInfo(t, "ToBytes", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestToInt")
|
||||
|
||||
cases := []interface{}{"123", "-123", 123,
|
||||
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
|
||||
float32(12.3), float64(12.3),
|
||||
@@ -67,15 +78,14 @@ func TestToInt(t *testing.T) {
|
||||
expected := []int64{123, -123, 123, 123, 123, 123, 123, 123, 12, 12, 0, 0, 0}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToInt(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToInt", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual, _ := ToInt(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFloat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToFloat")
|
||||
|
||||
cases := []interface{}{
|
||||
"", "-1", "-.11", "1.23e3", ".123e10", "abc",
|
||||
int(0), int8(1), int16(-1), int32(123), int64(123),
|
||||
@@ -86,108 +96,78 @@ func TestToFloat(t *testing.T) {
|
||||
0, 1, -1, 123, 123, 123, 123, 123, 123, 123, 12.3, 12.300000190734863}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToFloat(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToFloat", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual, _ := ToFloat(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
// map
|
||||
assert := internal.NewAssert(t, "TestToString")
|
||||
|
||||
aMap := make(map[string]int)
|
||||
aMap["a"] = 1
|
||||
aMap["b"] = 2
|
||||
aMap["c"] = 3
|
||||
|
||||
// struct
|
||||
type TestStruct struct {
|
||||
Name string
|
||||
}
|
||||
aStruct := TestStruct{Name: "TestStruct"}
|
||||
|
||||
cases := []interface{}{
|
||||
"", 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}}
|
||||
|
||||
expected := []string{"0", "1", "-1", "123", "123", "123", "123", "123",
|
||||
"123", "123", "12.3", "12.300000190734863", "true", "false",
|
||||
expected := []string{
|
||||
"", "",
|
||||
"0", "1", "-1",
|
||||
"123", "123", "123", "123", "123", "123", "123",
|
||||
"12.3", "12.3",
|
||||
"true", "false",
|
||||
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := ToString(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToString", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual := ToString(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
func TestToJson(t *testing.T) {
|
||||
// map
|
||||
aMap := make(map[string]int)
|
||||
aMap["a"] = 1
|
||||
aMap["b"] = 2
|
||||
aMap["c"] = 3
|
||||
assert := internal.NewAssert(t, "TestToJson")
|
||||
|
||||
mapJson := "{\"a\":1,\"b\":2,\"c\":3}"
|
||||
r1, _ := ToJson(aMap)
|
||||
if r1 != mapJson {
|
||||
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
var aMap = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
mapJsonStr, _ := ToJson(aMap)
|
||||
assert.Equal("{\"a\":1,\"b\":2,\"c\":3}", mapJsonStr)
|
||||
|
||||
// struct
|
||||
type TestStruct struct {
|
||||
Name string
|
||||
}
|
||||
aStruct := TestStruct{Name: "TestStruct"}
|
||||
structJson := "{\"Name\":\"TestStruct\"}"
|
||||
r2, _ := ToJson(aStruct)
|
||||
if r2 != structJson {
|
||||
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
structJsonStr, _ := ToJson(aStruct)
|
||||
assert.Equal("{\"Name\":\"TestStruct\"}", structJsonStr)
|
||||
}
|
||||
|
||||
func TestStructToMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStructToMap")
|
||||
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
|
||||
p1 := People{
|
||||
p := &People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
|
||||
pm1, _ := StructToMap(p1)
|
||||
m1 := make(map[string]interface{})
|
||||
m1["name"] = "test"
|
||||
//exp1["100"] = 100
|
||||
|
||||
if !reflect.DeepEqual(pm1, m1) {
|
||||
utils.LogFailedTestInfo(t, "StructToMap", p1, m1, pm1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
p2 := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
|
||||
pm2, _ := StructToMap(p1)
|
||||
m2 := make(map[string]interface{})
|
||||
m2["name"] = "test"
|
||||
m2["100"] = 100
|
||||
|
||||
if reflect.DeepEqual(pm2, m2) {
|
||||
utils.LogFailedTestInfo(t, "StructToMap", p2, m2, pm2)
|
||||
t.FailNow()
|
||||
pm, _ := StructToMap(p)
|
||||
data, _ := StructToMap(p)
|
||||
var expected = map[string]interface{}{
|
||||
"name": "test",
|
||||
}
|
||||
assert.Equal(expected, pm)
|
||||
assert.Equal(expected, data)
|
||||
}
|
||||
|
||||
func TestColorHexToRGB(t *testing.T) {
|
||||
@@ -196,22 +176,592 @@ func TestColorHexToRGB(t *testing.T) {
|
||||
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
|
||||
expected := "0,51,102"
|
||||
|
||||
if colorRGB != expected {
|
||||
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorHex, expected, colorRGB)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestColorHexToRGB")
|
||||
assert.Equal(expected, colorRGB)
|
||||
}
|
||||
|
||||
func TestColorRGBToHex(t *testing.T) {
|
||||
r := 0
|
||||
g := 51
|
||||
b := 102
|
||||
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
|
||||
colorHex := ColorRGBToHex(r, g, b)
|
||||
expected := "#003366"
|
||||
|
||||
if colorHex != expected {
|
||||
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorRGB, expected, colorHex)
|
||||
t.FailNow()
|
||||
assert := internal.NewAssert(t, "TestColorRGBToHex")
|
||||
assert.Equal(expected, colorHex)
|
||||
}
|
||||
|
||||
func TestToChannel(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToChannel")
|
||||
|
||||
ch := ToChannel([]interface{}{1, 2, 3})
|
||||
val1, _ := <-ch
|
||||
assert.Equal(1, val1)
|
||||
|
||||
val2, _ := <-ch
|
||||
assert.Equal(2, val2)
|
||||
|
||||
val3, _ := <-ch
|
||||
assert.Equal(3, val3)
|
||||
|
||||
_, ok := <-ch
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestEncodeByte(t *testing.T) {
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestDecodeByte")
|
||||
|
||||
var obj string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
DecodeByte(byteData, &obj)
|
||||
assert.Equal("abc", obj)
|
||||
}
|
||||
|
||||
func TestDeepClone(t *testing.T) {
|
||||
// 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",
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range cases {
|
||||
cloned := DeepClone(item)
|
||||
|
||||
t.Log(cloned)
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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 TestMapToStruct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMapToStruct")
|
||||
|
||||
type Address struct {
|
||||
Street string `json:"street"`
|
||||
Number int `json:"number"`
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
Phone string `json:"phone"`
|
||||
Addr *Address `json:"address"`
|
||||
}
|
||||
|
||||
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 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]interface{}{"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]interface{}{"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]interface{}{"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]interface{}{"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 interface{}
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
168
cryptor/aes.go
168
cryptor/aes.go
@@ -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
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestAesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
|
||||
|
||||
if string(aesEcbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesEcbEncrypt/AesEcbDecrypt", data, data, string(aesEcbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
|
||||
|
||||
if string(aesCbcDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCbcEncrypt/AesCbcDecrypt", data, data, string(aesCbcDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
|
||||
|
||||
if string(aesCtrDeCrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCtrCrypt", data, data, string(aesCtrDeCrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
|
||||
|
||||
if string(aesCfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCfbEncrypt/AesCfbDecrypt", data, data, string(aesCfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
|
||||
|
||||
if string(aesOfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesOfbEncrypt/AesOfbDecrypt", data, data, string(aesOfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
103
cryptor/basic.go
103
cryptor/basic.go
@@ -6,6 +6,7 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
@@ -13,7 +14,9 @@ import (
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Base64StdEncode encode string with base64 encoding
|
||||
@@ -34,16 +37,57 @@ func Md5String(s string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5StringWithBase64 return the md5 value of string with base64.
|
||||
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.
|
||||
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.
|
||||
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) {
|
||||
f, err := ioutil.ReadFile(filename)
|
||||
if fileInfo, err := os.Stat(filename); err != nil {
|
||||
return "", err
|
||||
} else if fileInfo.IsDir() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
h := md5.New()
|
||||
h.Write(f)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
hash := md5.New()
|
||||
|
||||
chunkSize := 65536
|
||||
for buf, reader := make([]byte, chunkSize), bufio.NewReader(file); ; {
|
||||
n, err := reader.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
hash.Write(buf[:n])
|
||||
}
|
||||
|
||||
checksum := fmt.Sprintf("%x", hash.Sum(nil))
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
// HmacMd5 return the hmac hash of string use md5
|
||||
@@ -53,6 +97,13 @@ func HmacMd5(data, key string) string {
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacMd5WithBase64 return the hmac hash of string use md5 with base64.
|
||||
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
|
||||
func HmacSha1(data, key string) string {
|
||||
h := hmac.New(sha1.New, []byte(key))
|
||||
@@ -60,6 +111,13 @@ func HmacSha1(data, key string) string {
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha1WithBase64 return the hmac hash of string use sha1 with base64.
|
||||
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
|
||||
func HmacSha256(data, key string) string {
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
@@ -67,6 +125,13 @@ func HmacSha256(data, key string) string {
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha256WithBase64 return the hmac hash of string use sha256 with base64.
|
||||
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
|
||||
func HmacSha512(data, key string) string {
|
||||
h := hmac.New(sha512.New, []byte(key))
|
||||
@@ -74,6 +139,13 @@ func HmacSha512(data, key string) string {
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha512WithBase64 return the hmac hash of string use sha512 with base64.
|
||||
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
|
||||
func Sha1(data string) string {
|
||||
sha1 := sha1.New()
|
||||
@@ -81,6 +153,13 @@ func Sha1(data string) string {
|
||||
return hex.EncodeToString(sha1.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha1WithBase64 return the sha1 value (SHA-1 hash algorithm) of base64 string.
|
||||
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
|
||||
func Sha256(data string) string {
|
||||
sha256 := sha256.New()
|
||||
@@ -88,9 +167,23 @@ func Sha256(data string) string {
|
||||
return hex.EncodeToString(sha256.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha256WithBase64 return the sha256 value (SHA256 hash algorithm) of base64 string.
|
||||
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
|
||||
func Sha512(data string) string {
|
||||
sha512 := sha512.New()
|
||||
sha512.Write([]byte(data))
|
||||
return hex.EncodeToString(sha512.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha512WithBase64 return the sha512 value (SHA512 hash algorithm) of base64 string.
|
||||
func Sha512WithBase64(str string) string {
|
||||
sha512 := sha512.New()
|
||||
sha512.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(sha512.Sum([]byte("")))
|
||||
}
|
||||
|
||||
@@ -1,66 +1,57 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestBase64StdEncode(t *testing.T) {
|
||||
s := "hello world"
|
||||
bs := Base64StdEncode(s)
|
||||
|
||||
if bs != "aGVsbG8gd29ybGQ=" {
|
||||
utils.LogFailedTestInfo(t, "Base64StdEncode", s, "aGVsbG8gd29ybGQ=", bs)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestBase64StdEncode")
|
||||
assert.Equal("aGVsbG8gd29ybGQ=", Base64StdEncode("hello world"))
|
||||
}
|
||||
|
||||
func TestBase64StdDecode(t *testing.T) {
|
||||
bs := "aGVsbG8gd29ybGQ="
|
||||
s := Base64StdDecode(bs)
|
||||
|
||||
if s != "hello world" {
|
||||
utils.LogFailedTestInfo(t, "Base64StdDecode", bs, "hello world=", s)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestBase64StdDecode")
|
||||
assert.Equal("hello world", Base64StdDecode("aGVsbG8gd29ybGQ="))
|
||||
}
|
||||
|
||||
func TestMd5String(t *testing.T) {
|
||||
s := "hello"
|
||||
smd5 := Md5String(s)
|
||||
expected := "5d41402abc4b2a76b9719d911017c592"
|
||||
assert := internal.NewAssert(t, "TestMd5String")
|
||||
assert.Equal("5d41402abc4b2a76b9719d911017c592", Md5String("hello"))
|
||||
}
|
||||
|
||||
if smd5 != expected {
|
||||
utils.LogFailedTestInfo(t, "Md5String", s, expected, smd5)
|
||||
t.FailNow()
|
||||
}
|
||||
func TestMd5StringWithBase64(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMd5StringWithBase64")
|
||||
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5StringWithBase64("hello"))
|
||||
}
|
||||
|
||||
func TestMd5Byte(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMd5Byte")
|
||||
data := []byte{'a'}
|
||||
assert.Equal("0cc175b9c0f1b6a831c399e269772661", Md5Byte(data))
|
||||
}
|
||||
|
||||
func TestMd5ByteWithBase64(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMd5ByteWithBase64")
|
||||
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5ByteWithBase64([]byte("hello")))
|
||||
}
|
||||
|
||||
func TestMd5File(t *testing.T) {
|
||||
file, _ := os.Create("./hello.txt")
|
||||
defer file.Close()
|
||||
file.WriteString("hello\n")
|
||||
|
||||
fileMd5, err := Md5File("./hello.txt")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println(fileMd5)
|
||||
fileMd5, err := Md5File("./basic.go")
|
||||
assert := internal.NewAssert(t, "TestMd5File")
|
||||
assert.IsNotNil(fileMd5)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestHmacMd5(t *testing.T) {
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacMd5 := HmacMd5(s, key)
|
||||
expected := "5f4c9faaff0a1ad3007d9ddc06abe36d"
|
||||
assert := internal.NewAssert(t, "TestHmacMd5")
|
||||
assert.Equal("5f4c9faaff0a1ad3007d9ddc06abe36d", HmacMd5("hello world", "12345"))
|
||||
}
|
||||
|
||||
if hmacMd5 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacMd5", s, expected, hmacMd5)
|
||||
t.FailNow()
|
||||
}
|
||||
func TestHmacMd5WithBase64(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestHmacMd5WithBase64")
|
||||
assert.Equal("6DQwbquJLYclJdSRinpjmg==", HmacMd5WithBase64("hello", "12345"))
|
||||
}
|
||||
|
||||
func TestHmacSha1(t *testing.T) {
|
||||
@@ -69,10 +60,18 @@ func TestHmacSha1(t *testing.T) {
|
||||
hmacSha1 := HmacSha1(s, key)
|
||||
expected := "3826f812255d8683f051ee97346d1359234d5dbd"
|
||||
|
||||
if hmacSha1 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha1", s, expected, hmacSha1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestHmacSha1")
|
||||
assert.Equal(expected, hmacSha1)
|
||||
}
|
||||
|
||||
func TestHmacSha1WithBase64(t *testing.T) {
|
||||
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) {
|
||||
@@ -81,10 +80,18 @@ func TestHmacSha256(t *testing.T) {
|
||||
hmacSha256 := HmacSha256(s, key)
|
||||
expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8"
|
||||
|
||||
if hmacSha256 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha256", s, expected, hmacSha256)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestHmacSha256")
|
||||
assert.Equal(expected, hmacSha256)
|
||||
}
|
||||
|
||||
func TestHmacSha256WithBase64(t *testing.T) {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
hms := HmacSha256WithBase64(str, key)
|
||||
expected := "MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU="
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacSha256WithBase64")
|
||||
assert.Equal(expected, hms)
|
||||
}
|
||||
|
||||
func TestHmacSha512(t *testing.T) {
|
||||
@@ -93,10 +100,18 @@ func TestHmacSha512(t *testing.T) {
|
||||
hmacSha512 := HmacSha512(s, key)
|
||||
expected := "5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175"
|
||||
|
||||
if hmacSha512 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha512", s, expected, hmacSha512)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestHmacSha512")
|
||||
assert.Equal(expected, hmacSha512)
|
||||
}
|
||||
|
||||
func TestHmacSha512WithBase64(t *testing.T) {
|
||||
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) {
|
||||
@@ -104,10 +119,16 @@ func TestSha1(t *testing.T) {
|
||||
sha1 := Sha1(s)
|
||||
expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
|
||||
|
||||
if sha1 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha1", s, expected, sha1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestSha1")
|
||||
assert.Equal(expected, sha1)
|
||||
}
|
||||
|
||||
func TestSha1WithBase64(t *testing.T) {
|
||||
str := Sha1WithBase64("hello")
|
||||
expected := "qvTGHdzF6KLavt4PO0gs2a6pQ00="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha1WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
func TestSha256(t *testing.T) {
|
||||
@@ -115,10 +136,16 @@ func TestSha256(t *testing.T) {
|
||||
sha256 := Sha256(s)
|
||||
expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
|
||||
if sha256 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha256", s, expected, sha256)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestSha256")
|
||||
assert.Equal(expected, sha256)
|
||||
}
|
||||
|
||||
func TestSha256WithBase64(t *testing.T) {
|
||||
str := Sha256WithBase64("hello")
|
||||
expected := "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha256WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
func TestSha512(t *testing.T) {
|
||||
@@ -126,8 +153,14 @@ func TestSha512(t *testing.T) {
|
||||
sha512 := Sha512(s)
|
||||
expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f"
|
||||
|
||||
if sha512 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha512", s, expected, sha512)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestSha512")
|
||||
assert.Equal(expected, sha512)
|
||||
}
|
||||
|
||||
func TestSha512WithBase64(t *testing.T) {
|
||||
str := Sha512WithBase64("hello")
|
||||
expected := "m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha512WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
490
cryptor/crypto.go
Normal file
490
cryptor/crypto.go
Normal file
@@ -0,0 +1,490 @@
|
||||
// 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/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
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
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 {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
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 {
|
||||
block, _ := aes.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[aes.BlockSize:], 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)
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[des.BlockSize:], 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)
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// GenerateRsaKeyPair create rsa private and public key.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
}
|
||||
@@ -2,15 +2,15 @@ package cryptor
|
||||
|
||||
import "bytes"
|
||||
|
||||
func generateAesKey(key []byte) []byte {
|
||||
genKey := make([]byte, 16)
|
||||
copy(genKey, key)
|
||||
for i := 16; i < len(key); {
|
||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
func generateAesKey(key []byte, size int) []byte {
|
||||
aesKey := make([]byte, size)
|
||||
copy(aesKey, key)
|
||||
for i := size; i < len(key); {
|
||||
for j := 0; j < size && i < len(key); j, i = j+1, i+1 {
|
||||
aesKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
return genKey
|
||||
return aesKey
|
||||
}
|
||||
|
||||
func generateDesKey(key []byte) []byte {
|
||||
148
cryptor/crypto_test.go
Normal file
148
cryptor/crypto_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
166
cryptor/des.go
166
cryptor/des.go
@@ -1,166 +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
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestDesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
|
||||
|
||||
if string(desEcbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesEcbEncrypt/DesEcbDecrypt", data, data, string(desEcbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
|
||||
|
||||
if string(desCbcDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCbcEncrypt/DesCbcDecrypt", data, data, string(desCbcDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
|
||||
|
||||
if string(desCtrDeCrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCtrCrypt", data, data, string(desCtrDeCrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
|
||||
|
||||
if string(desCfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCfbEncrypt/DesCfbDecrypt", data, data, string(desCfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
|
||||
|
||||
if string(desOfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesOfbEncrypt/DesOfbDecrypt", data, data, string(desOfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
116
cryptor/rsa.go
116
cryptor/rsa.go
@@ -1,116 +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) {
|
||||
// private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
panic(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 {
|
||||
panic(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 {
|
||||
panic(err)
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestRsaEncrypt(t *testing.T) {
|
||||
GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
data := []byte("hello world")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
if string(data) != string(decrypted) {
|
||||
utils.LogFailedTestInfo(t, "RsaEncrypt/RsaDecrypt", string(data), string(data), string(decrypted))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
60
datetime/conversion.go
Normal file
60
datetime/conversion.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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
|
||||
func NewUnixNow() *theTime {
|
||||
return &theTime{unix: time.Now().Unix()}
|
||||
}
|
||||
|
||||
// NewUnix return unix timestamp of specified time
|
||||
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"
|
||||
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
|
||||
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
|
||||
func (t *theTime) ToUnix() int64 {
|
||||
return t.unix
|
||||
}
|
||||
|
||||
// ToFormat return the time string 'yyyy-mm-dd hh:mm:ss' of unix time
|
||||
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
|
||||
func (t *theTime) ToFormatForTpl(tpl string) string {
|
||||
return time.Unix(t.unix, 0).Format(tpl)
|
||||
}
|
||||
|
||||
// ToFormatForTpl return iso8601 time string
|
||||
func (t *theTime) ToIso8601() string {
|
||||
return time.Unix(t.unix, 0).Format(time.RFC3339)
|
||||
}
|
||||
54
datetime/conversion_test.go
Normal file
54
datetime/conversion_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestToUnix(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToUnix")
|
||||
|
||||
tm1 := NewUnixNow()
|
||||
unixTimestamp := tm1.ToUnix()
|
||||
tm2 := NewUnix(unixTimestamp)
|
||||
|
||||
assert.Equal(tm1, tm2)
|
||||
}
|
||||
|
||||
func TestToFormat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToFormat")
|
||||
|
||||
tm, err := NewFormat("2022/03/18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewFormat("2022-03-18 17:04:05")
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToFormat -> ", tm.ToFormat())
|
||||
}
|
||||
|
||||
func TestToFormatForTpl(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToFormatForTpl")
|
||||
|
||||
tm, err := NewFormat("2022/03/18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewFormat("2022-03-18 17:04:05")
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToFormatForTpl -> ", tm.ToFormatForTpl("2006/01/02 15:04:05"))
|
||||
|
||||
}
|
||||
|
||||
func TestToIso8601(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToIso8601")
|
||||
|
||||
tm, err := NewISO8601("2022-03-18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewISO8601("2006-01-02T15:04:05.999Z")
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToIso8601 -> ", tm.ToIso8601())
|
||||
}
|
||||
@@ -3,29 +3,36 @@
|
||||
|
||||
// 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 (
|
||||
"strconv"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -35,7 +42,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,33 +54,35 @@ 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 {
|
||||
s := strconv.FormatInt(minute, 10)
|
||||
m, _ := time.ParseDuration(s + "m")
|
||||
return t.Add(m)
|
||||
return t.Add(time.Minute * time.Duration(minute))
|
||||
}
|
||||
|
||||
// AddHour add or sub hour to the time
|
||||
func AddHour(t time.Time, hour int64) time.Time {
|
||||
s := strconv.FormatInt(hour, 10)
|
||||
h, _ := time.ParseDuration(s + "h")
|
||||
return t.Add(h)
|
||||
return t.Add(time.Hour * time.Duration(hour))
|
||||
}
|
||||
|
||||
// AddDay add or sub day to the time
|
||||
func AddDay(t time.Time, day int64) time.Time {
|
||||
dayHours := day * 24
|
||||
d := strconv.FormatInt(dayHours, 10)
|
||||
h, _ := time.ParseDuration(d + "h")
|
||||
return t.Add(h)
|
||||
return t.Add(24 * time.Hour * time.Duration(day))
|
||||
}
|
||||
|
||||
// AddYear add or sub year to the time.
|
||||
func AddYear(t time.Time, year int64) time.Time {
|
||||
return t.Add(365 * 24 * time.Hour * time.Duration(year))
|
||||
}
|
||||
|
||||
// GetNowDate return format yyyy-mm-dd of current date
|
||||
@@ -104,12 +113,270 @@ func GetNightTimestamp() int64 {
|
||||
}
|
||||
|
||||
// FormatTimeToStr convert time to string
|
||||
func FormatTimeToStr(t time.Time, format string) string {
|
||||
return t.Format(timeFormat[format])
|
||||
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 {
|
||||
t, _ := time.Parse(timeFormat[format], str)
|
||||
return t
|
||||
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 support", format)
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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())
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
// BeginOfMonth return beginning of month
|
||||
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
|
||||
func EndOfMonth(t time.Time) time.Time {
|
||||
return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// BeginOfYear return beginning of year
|
||||
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
|
||||
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.
|
||||
func IsLeapYear(year int) bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
// BetweenSeconds returns the number of seconds between two times.
|
||||
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.
|
||||
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.
|
||||
func IsWeekend(t time.Time) bool {
|
||||
return time.Saturday == t.Weekday() || time.Sunday == t.Weekday()
|
||||
}
|
||||
|
||||
// NowDateOrTime return current datetime with specific format and timezone.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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())()`
|
||||
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.
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,103 +1,85 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestAddDay(t *testing.T) {
|
||||
now := time.Now()
|
||||
assert := internal.NewAssert(t, "TestAddDay")
|
||||
|
||||
now := time.Now()
|
||||
after2Days := AddDay(now, 2)
|
||||
diff1 := after2Days.Sub(now)
|
||||
if diff1.Hours() != 48 {
|
||||
utils.LogFailedTestInfo(t, "AddDay", now, 48, diff1.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(float64(48), diff1.Hours())
|
||||
|
||||
before2Days := AddDay(now, -2)
|
||||
diff2 := before2Days.Sub(now)
|
||||
if diff2.Hours() != -48 {
|
||||
utils.LogFailedTestInfo(t, "AddDay", now, -48, diff2.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(float64(-48), diff2.Hours())
|
||||
}
|
||||
func TestAddHour(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
func TestAddHour(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestAddHour")
|
||||
|
||||
now := time.Now()
|
||||
after2Hours := AddHour(now, 2)
|
||||
diff1 := after2Hours.Sub(now)
|
||||
if diff1.Hours() != 2 {
|
||||
utils.LogFailedTestInfo(t, "AddHour", now, 2, diff1.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(float64(2), diff1.Hours())
|
||||
|
||||
before2Hours := AddHour(now, -2)
|
||||
diff2 := before2Hours.Sub(now)
|
||||
if diff2.Hours() != -2 {
|
||||
utils.LogFailedTestInfo(t, "AddHour", now, -2, diff2.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(float64(-2), diff2.Hours())
|
||||
}
|
||||
|
||||
func TestAddMinute(t *testing.T) {
|
||||
now := time.Now()
|
||||
assert := internal.NewAssert(t, "TestAddMinute")
|
||||
|
||||
now := time.Now()
|
||||
after2Minutes := AddMinute(now, 2)
|
||||
diff1 := after2Minutes.Sub(now)
|
||||
if diff1.Minutes() != 2 {
|
||||
utils.LogFailedTestInfo(t, "AddMinute", now, 2, diff1.Minutes())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(float64(2), diff1.Minutes())
|
||||
|
||||
before2Minutes := AddMinute(now, -2)
|
||||
diff2 := before2Minutes.Sub(now)
|
||||
if diff2.Minutes() != -2 {
|
||||
utils.LogFailedTestInfo(t, "AddMinute", now, -2, diff2.Minutes())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(float64(-2), diff2.Minutes())
|
||||
}
|
||||
|
||||
func TestAddYear(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestAddDay")
|
||||
|
||||
now := time.Now()
|
||||
after2Years := AddYear(now, 1)
|
||||
diff1 := after2Years.Sub(now)
|
||||
assert.Equal(float64(8760), diff1.Hours())
|
||||
|
||||
before2Years := AddYear(now, -1)
|
||||
diff2 := before2Years.Sub(now)
|
||||
assert.Equal(float64(-8760), diff2.Hours())
|
||||
}
|
||||
|
||||
func TestGetNowDate(t *testing.T) {
|
||||
date := GetNowDate()
|
||||
assert := internal.NewAssert(t, "TestGetNowDate")
|
||||
expected := time.Now().Format("2006-01-02")
|
||||
if date != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowDate", "", expected, date)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(expected, GetNowDate())
|
||||
}
|
||||
|
||||
func TestGetNotTime(t *testing.T) {
|
||||
ts := GetNowTime()
|
||||
assert := internal.NewAssert(t, "TestGetNotTime")
|
||||
expected := time.Now().Format("15:04:05")
|
||||
if ts != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowTime", "", expected, ts)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(expected, GetNowTime())
|
||||
}
|
||||
|
||||
func TestGetNowDateTime(t *testing.T) {
|
||||
ts := GetNowDateTime()
|
||||
assert := internal.NewAssert(t, "TestGetNowDateTime")
|
||||
expected := time.Now().Format("2006-01-02 15:04:05")
|
||||
if ts != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowDateTime", "", expected, ts)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(expected, GetNowDateTime())
|
||||
}
|
||||
|
||||
//todo
|
||||
//func TestGetZeroHourTimestamp(t *testing.T) {
|
||||
// ts := GetZeroHourTimestamp()
|
||||
// expected := time.Now().UTC().Unix() - 8*3600
|
||||
// if ts != expected {
|
||||
// utils.LogFailedTestInfo(t, "GetZeroHourTimestamp", "", expected, ts)
|
||||
// t.FailNow()
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestFormatTimeToStr(t *testing.T) {
|
||||
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",
|
||||
@@ -110,16 +92,15 @@ func TestFormatTimeToStr(t *testing.T) {
|
||||
"16:04:08", "2021/01"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := FormatTimeToStr(datetime, cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
actual := FormatTimeToStr(datetime, cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatStrToTime(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFormatStrToTime")
|
||||
|
||||
formats := []string{
|
||||
"2006-01-02 15:04:05", "2006-01-02",
|
||||
"02-01-06 15:04:05", "2006/01/02 15:04:05",
|
||||
@@ -135,11 +116,331 @@ func TestFormatStrToTime(t *testing.T) {
|
||||
"2021/01"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := FormatStrToTime(datetimeStr[i], cases[i])
|
||||
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)
|
||||
t.FailNow()
|
||||
actual, err := FormatStrToTime(datetimeStr[i], cases[i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeginOfMinute(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfMinute")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 48, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfMinute(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfMinute(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfMinute")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 48, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfMinute(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBeginOfHour(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfHour")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfHour(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfHour(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfHour")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfHour(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBeginOfDay(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfDay")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 0, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfDay(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfDay(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfDay")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 23, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfDay(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBeginOfWeek(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfWeek(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfWeek(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfWeek(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBeginOfMonth(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfMonth")
|
||||
|
||||
expected := time.Date(2022, 2, 1, 0, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfMonth(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfMonth(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfMonth")
|
||||
|
||||
expected := time.Date(2022, 2, 28, 23, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfMonth(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBeginOfYear(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBeginOfYear")
|
||||
|
||||
expected := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := BeginOfYear(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestEndOfYear(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfYear")
|
||||
|
||||
expected := time.Date(2022, 12, 31, 23, 59, 59, 999999999, time.Local)
|
||||
td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local)
|
||||
actual := EndOfYear(td)
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestIsLeapYear(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEndOfYear")
|
||||
|
||||
result1 := IsLeapYear(2000)
|
||||
result2 := IsLeapYear(2001)
|
||||
|
||||
assert.Equal(true, result1)
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
func TestBetweenSeconds(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBetweenSeconds")
|
||||
|
||||
today := time.Now()
|
||||
tomorrow := AddDay(today, 1)
|
||||
yesterday := AddDay(today, -1)
|
||||
|
||||
result1 := BetweenSeconds(today, tomorrow)
|
||||
result2 := BetweenSeconds(today, yesterday)
|
||||
|
||||
assert.Equal(int64(86400), result1)
|
||||
assert.Equal(int64(-86400), result2)
|
||||
}
|
||||
|
||||
func TestDayOfYear(t *testing.T) {
|
||||
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) {
|
||||
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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
326
docs/compare.md
Normal file
326
docs/compare.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# Compare
|
||||
|
||||
Package compare provides a lightweight comparison function on any type.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/v1/compare/compare.go](https://github.com/duke-git/lancet/blob/v1/compare/compare.go)
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/v1/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/v1/compare/compare_internal.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/condition"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [Equal](#Equal)
|
||||
- [EqualValue](#EqualValue)
|
||||
- [LessThan](#LessThan)
|
||||
- [GreaterThan](#GreaterThan)
|
||||
- [LessOrEqual](#LessOrEqual)
|
||||
- [GreaterOrEqual](#GreaterOrEqual)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>Checks if two values are equal or not. (check both type and value)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Equal(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EqualValue">EqualValue</span>
|
||||
|
||||
<p>Checks if two values are equal or not. (check value only)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func EqualValue(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessThan">LessThan</span>
|
||||
|
||||
<p>Checks if value `left` less than value `right`.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func LessThan(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterThan">GreaterThan</span>
|
||||
|
||||
<p>Checks if value `left` greater than value `right`.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GreaterThan(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessOrEqual">LessOrEqual</span>
|
||||
|
||||
<p>Checks if value `left` less than or equal than value `right`.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func LessOrEqual(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterOrEqual">GreaterOrEqual</span>
|
||||
|
||||
<p>Checks if value `left` less greater or equal than value `right`.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GreaterOrEqual(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
326
docs/compare_zh-CN.md
Normal file
326
docs/compare_zh-CN.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# Compare
|
||||
|
||||
compare包提供几个轻量级的类型比较函数。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/v1/compare/compare.go](https://github.com/duke-git/lancet/blob/v1/compare/compare.go)
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/v1/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/v1/compare/compare_internal.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/condition"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Equal](#Equal)
|
||||
- [EqualValue](#EqualValue)
|
||||
- [LessThan](#LessThan)
|
||||
- [GreaterThan](#GreaterThan)
|
||||
- [LessOrEqual](#LessOrEqual)
|
||||
- [GreaterOrEqual](#GreaterOrEqual)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>检查两个值是否相等(检查类型和值)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Equal(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EqualValue">EqualValue</span>
|
||||
|
||||
<p>检查两个值是否相等(只检查值)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func EqualValue(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessThan">LessThan</span>
|
||||
|
||||
<p>验证参数`left`的值是否小于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LessThan(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterThan">GreaterThan</span>
|
||||
|
||||
<p>验证参数`left`的值是否大于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GreaterThan(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessOrEqual">LessOrEqual</span>
|
||||
|
||||
<p>验证参数`left`的值是否小于或等于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LessOrEqual(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterOrEqual">GreaterOrEqual</span>
|
||||
|
||||
<p>验证参数`left`的值是否大于或参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GreaterOrEqual(left, right interface{}) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
1046
docs/convertor.md
Normal file
1046
docs/convertor.md
Normal file
File diff suppressed because it is too large
Load Diff
1047
docs/convertor_zh-CN.md
Normal file
1047
docs/convertor_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
1387
docs/cryptor.md
Normal file
1387
docs/cryptor.md
Normal file
File diff suppressed because it is too large
Load Diff
1415
docs/cryptor_zh-CN.md
Normal file
1415
docs/cryptor_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
1468
docs/datetime.md
Normal file
1468
docs/datetime.md
Normal file
File diff suppressed because it is too large
Load Diff
1390
docs/datetime_zh-CN.md
Normal file
1390
docs/datetime_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
924
docs/fileutil.md
Normal file
924
docs/fileutil.md
Normal file
@@ -0,0 +1,924 @@
|
||||
# Fileutil
|
||||
|
||||
Package fileutil implements some basic functions for file operations.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/fileutil/file.go](https://github.com/duke-git/lancet/blob/v1/fileutil/file.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [ClearFile](#ClearFile)
|
||||
- [CreateFile](#CreateFile)
|
||||
- [CreateDir](#CreateDir)
|
||||
- [CopyFile](#CopyFile)
|
||||
- [FileMode](#FileMode)
|
||||
- [MiMeType](#MiMeType)
|
||||
- [IsExist](#IsExist)
|
||||
- [IsLink](#IsLink)
|
||||
- [IsDir](#IsDir)
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
- [ReadFileByLine](#ReadFileByLine)
|
||||
- [Zip](#Zip)
|
||||
- [UnZip](#UnZip)
|
||||
- [ZipAppendEntry](#ZipAppendEntry)
|
||||
- [CurrentPath](#CurrentPath)
|
||||
- [IsZipFile](#IsZipFile)
|
||||
- [FileSize](#FileSize)
|
||||
- [MTime](#MTime)
|
||||
- [Sha](#Sha)
|
||||
- [ReadCsvFile](#ReadCsvFile)
|
||||
- [WriteCsvFile](#WriteCsvFile)
|
||||
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="ClearFile">ClearFile</span>
|
||||
|
||||
<p>Clear the file content, write empty string to the file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ClearFile(path string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.ClearFile("./test.txt")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CreateDir">CreateDir</span>
|
||||
|
||||
<p>Create directory in absolute path. param `absPath` like /a/, /a/b/.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CreateDir(absPath string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.CreateDir("/a/")
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CreateFile">CreateFile</span>
|
||||
|
||||
<p>Create file in path. return true if create succeed.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CreateFile(path string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isCreatedSucceed := fileutil.CreateFile("./test.txt")
|
||||
fmt.Println(isCreatedSucceed)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyFile">CopyFile</span>
|
||||
|
||||
<p>Copy src file to dest file. If dest file exist will overwrite it.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FileMode">FileMode</span>
|
||||
|
||||
<p>Return file mode infomation.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FileMode(path string) (fs.FileMode, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MiMeType">MiMeType</span>
|
||||
|
||||
<p>Get file mime type, 'file' param's type should be string or *os.File.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func MiMeType(file interface{}) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsExist">IsExist</span>
|
||||
|
||||
<p>Checks if a file or directory exists.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsExist(path string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsLink">IsLink</span>
|
||||
|
||||
<p>Checks if a file is symbol link or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsLink(path string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isLinkFile := fileutil.IsLink("./test.txt")
|
||||
fmt.Println(isLinkFile) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsDir">IsDir</span>
|
||||
|
||||
<p>Checks if the path is directy or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsDir(path string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ListFileNames">ListFileNames</span>
|
||||
|
||||
<p>List all file names in given path.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ListFileNames(path string) ([]string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fileNames, _ := fileutil.ListFileNames("./")
|
||||
fmt.Println(fileNames)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveFile">RemoveFile</span>
|
||||
|
||||
<p>Remove the file of path.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveFile(path string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.RemoveFile("./test.txt")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileToString">ReadFileToString</span>
|
||||
|
||||
<p>Return string of file content.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ReadFileToString(path string) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileByLine">ReadFileByLine</span>
|
||||
|
||||
<p>Read file line by line, and return slice of lines</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ReadFileByLine(path string)([]string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Zip">Zip</span>
|
||||
|
||||
<p>Create a zip file of fpath, fpath could be a file or a directory.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Zip(fpath string, destPath string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UnZip">UnZip</span>
|
||||
|
||||
<p>Unzip the file and save it to dest path.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func UnZip(zipFile string, destPath string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ZipAppendEntry">ZipAppendEntry</span>
|
||||
|
||||
<p>Append a single file or directory by fpath to an existing zip file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ZipAppendEntry(fpath string, destPath string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.ZipAppendEntry("./test.txt", "./test.zip")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CurrentPath">CurrentPath</span>
|
||||
|
||||
<p>return current absolute path.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CurrentPath() string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
absPath := CurrentPath()
|
||||
fmt.Println(absPath)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsZipFile">IsZipFile</span>
|
||||
|
||||
<p>Checks if file is zip file or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsZipFile(filepath string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isZip := IsZipFile("./zipfile.zip")
|
||||
fmt.Println(isZip)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FileSize">FileSize</span>
|
||||
|
||||
<p>Returns file size in bytes.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FileSize(path string) (int64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
size, err := fileutil.FileSize("./testdata/test.txt")
|
||||
|
||||
fmt.Println(size)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// 20
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MTime">MTime</span>
|
||||
|
||||
<p>Returns file modified time(unix timestamp).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func MTime(filepath string) (int64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mtime, err := fileutil.MTime("./testdata/test.txt")
|
||||
|
||||
fmt.Println(mtime)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// 1682391110
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Sha">Sha</span>
|
||||
|
||||
<p>returns file sha value, param `shaType` should be 1, 256 or 512.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Sha(filepath string, shaType ...int) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadCsvFile">ReadCsvFile</span>
|
||||
|
||||
<p>Reads file content into slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ReadCsvFile(filepath string) ([][]string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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]]
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="WriteCsvFile">WriteCsvFile</span>
|
||||
|
||||
<p>Write content to target csv file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func WriteCsvFile(filepath string, records [][]string, append bool) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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:
|
||||
// <nil>
|
||||
// [[Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
|
||||
|
||||
<p>write slice of map to csv file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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]interface{}, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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]interface{}{
|
||||
{"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]]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
||||
|
||||
<p>Writes bytes to target file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func WriteBytesToFile(filepath string, content []byte) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteStringToFile">WriteStringToFile</span>
|
||||
|
||||
<p>Writes string to target file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func WriteStringToFile(filepath string, content string, append bool) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFile">ReadFile</span>
|
||||
|
||||
<p>Read File/URL</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;"> </span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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
|
||||
}
|
||||
```
|
||||
922
docs/fileutil_zh-CN.md
Normal file
922
docs/fileutil_zh-CN.md
Normal file
@@ -0,0 +1,922 @@
|
||||
# Fileutil
|
||||
|
||||
fileutil 包支持文件基本操作。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/fileutil/file.go](https://github.com/duke-git/lancet/blob/v1/fileutil/file.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [ClearFile](#ClearFile)
|
||||
- [CreateFile](#CreateFile)
|
||||
- [CreateDir](#CreateDir)
|
||||
- [CopyFile](#CopyFile)
|
||||
- [FileMode](#FileMode)
|
||||
- [MiMeType](#MiMeType)
|
||||
- [IsExist](#IsExist)
|
||||
- [IsLink](#IsLink)
|
||||
- [IsDir](#IsDir)画
|
||||
- [ListFileNames](#ListFileNames)
|
||||
- [RemoveFile](#RemoveFile)
|
||||
- [ReadFileToString](#ReadFileToString)
|
||||
- [ReadFileByLine](#ReadFileByLine)
|
||||
- [Zip](#Zip)
|
||||
- [UnZip](#UnZip)
|
||||
- [ZipAppendEntry](#ZipAppendEntry)
|
||||
- [CurrentPath](#CurrentPath)
|
||||
- [IsZipFile](#IsZipFile)
|
||||
- [FileSize](#FileSize)
|
||||
- [MTime](#MTime)
|
||||
- [Sha](#Sha)
|
||||
- [ReadCsvFile](#ReadCsvFile)
|
||||
- [WriteCsvFile](#WriteCsvFile)
|
||||
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="ClearFile">ClearFile</span>
|
||||
|
||||
<p>清空文件内容</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ClearFile(path string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.ClearFile("./test.txt")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CreateDir">CreateDir</span>
|
||||
|
||||
<p>使用绝对路径创建嵌套目录,例如/a/, /a/b/</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CreateDir(absPath string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.CreateDir("/a/")
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CreateFile">CreateFile</span>
|
||||
|
||||
<p>创建文件,创建成功返回true, 否则返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CreateFile(path string) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isCreatedSucceed := fileutil.CreateFile("./test.txt")
|
||||
fmt.Println(isCreatedSucceed)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyFile">CopyFile</span>
|
||||
|
||||
<p>拷贝文件,会覆盖原有的拷贝文件</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FileMode">FileMode</span>
|
||||
|
||||
<p>获取文件mode信息</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FileMode(path string) (fs.FileMode, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MiMeType">MiMeType</span>
|
||||
|
||||
<p>获取文件mime类型, 'file'参数的类型必须是string或者*os.File</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MiMeType(file interface{}) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsExist">IsExist</span>
|
||||
|
||||
<p>判断文件或目录是否存在</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsExist(path string) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsLink">IsLink</span>
|
||||
|
||||
<p>判断文件是否是符号链接</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsLink(path string) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isLinkFile := fileutil.IsLink("./test.txt")
|
||||
fmt.Println(isLinkFile) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsDir">IsDir</span>
|
||||
|
||||
<p>判断目录是否存在</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsDir(path string) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ListFileNames">ListFileNames</span>
|
||||
|
||||
<p>返回目录下所有文件名</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ListFileNames(path string) ([]string, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fileNames, _ := fileutil.ListFileNames("./")
|
||||
fmt.Println(fileNames)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveFile">RemoveFile</span>
|
||||
|
||||
<p>删除文件</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RemoveFile(path string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.RemoveFile("./test.txt")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileToString">ReadFileToString</span>
|
||||
|
||||
<p>读取文件内容并返回字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ReadFileToString(path string) (string, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFileByLine">ReadFileByLine</span>
|
||||
|
||||
<p>按行读取文件内容,返回字符串切片包含每一行</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ReadFileByLine(path string)([]string, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Zip">Zip</span>
|
||||
|
||||
<p>zip压缩文件, fpath参数可以是文件或目录</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Zip(fpath string, destPath string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UnZip">UnZip</span>
|
||||
|
||||
<p>zip解压缩文件并保存在目录中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func UnZip(zipFile string, destPath string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ZipAppendEntry">ZipAppendEntry</span>
|
||||
|
||||
<p>通过将单个文件或目录追加到现有的zip文件</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ZipAppendEntry(fpath string, destPath string) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.ZipAppendEntry("./test.txt", "./test.zip")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CurrentPath">CurrentPath</span>
|
||||
|
||||
<p>返回当前位置的绝对路径。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CurrentPath() string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
absPath := CurrentPath()
|
||||
fmt.Println(absPath)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsZipFile">IsZipFile</span>
|
||||
|
||||
<p>判断文件是否是zip压缩文件。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsZipFile(filepath string) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isZip := IsZipFile("./zipfile.zip")
|
||||
fmt.Println(isZip)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FileSize">FileSize</span>
|
||||
|
||||
<p>返回文件字节大小。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FileSize(path string) (int64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
size, err := fileutil.FileSize("./testdata/test.txt")
|
||||
|
||||
fmt.Println(size)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// 20
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MTime">MTime</span>
|
||||
|
||||
<p>返回文件修改时间(unix timestamp).</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MTime(filepath string) (int64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mtime, err := fileutil.MTime("./testdata/test.txt")
|
||||
|
||||
fmt.Println(mtime)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// 1682391110
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Sha">Sha</span>
|
||||
|
||||
<p>返回文件sha值,参数`shaType` 应传值为: 1, 256,512.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Sha(filepath string, shaType ...int) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadCsvFile">ReadCsvFile</span>
|
||||
|
||||
<p>读取csv文件内容到切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ReadCsvFile(filepath string) ([][]string, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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]]
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteCsvFile">WriteCsvFile</span>
|
||||
|
||||
<p>向csv文件写入内容。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func WriteCsvFile(filepath string, records [][]string, append bool) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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:
|
||||
// <nil>
|
||||
// [[Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
|
||||
|
||||
<p>将map切片写入csv文件中。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
// filepath: CSV文件路径。
|
||||
// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。
|
||||
// appendToExistingFile: 是否为追加写模式。
|
||||
// delimiter: CSV文件分割符。
|
||||
// headers: CSV文件表头顺序(需要与map key保持一致),不指定时按字母排序。
|
||||
func WriteMapsToCsv(filepath string, records []map[string]interface{}, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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]interface{}{
|
||||
{"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]]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
||||
|
||||
<p>将bytes写入文件。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func WriteBytesToFile(filepath string, content []byte) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="WriteStringToFile">WriteStringToFile</span>
|
||||
|
||||
<p>将字符串写入文件。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func WriteStringToFile(filepath string, content string, append bool) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ReadFile">ReadFile</span>
|
||||
|
||||
<p>读取文件或者URL</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;"></span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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
|
||||
}
|
||||
```
|
||||
300
docs/formatter.md
Normal file
300
docs/formatter.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Formatter
|
||||
|
||||
formatter contains some functions for data formatting.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/formatter/formatter.go](https://github.com/duke-git/lancet/blob/v1/formatter/formatter.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/formatter"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [Comma](#Comma)
|
||||
- [Pretty](#Pretty)
|
||||
- [PrettyToWriter](#PrettyToWriter)
|
||||
- [DecimalBytes](#DecimalBytes)
|
||||
- [BinaryBytes](#BinaryBytes)
|
||||
- [ParseDecimalBytes](#ParseDecimalBytes)
|
||||
- [ParseBinaryBytes](#ParseBinaryBytes)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Comma">Comma</span>
|
||||
|
||||
<p>Add comma to number by every 3 numbers from right. ahead by symbol char.
|
||||
Param should be number or numberic string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Comma(value interface{}, prefixSymbol string) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pretty">Pretty</span>
|
||||
|
||||
<p>Pretty data to JSON string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Pretty(v interface{}) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PrettyToWriter">PrettyToWriter</span>
|
||||
|
||||
<p>Pretty encode data to writer.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func PrettyToWriter(v interface{}, out io.Writer) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// }
|
||||
//
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DecimalBytes">DecimalBytes</span>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DecimalBytes(size float64, precision ...int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BinaryBytes">BinaryBytes</span>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func BinaryBytes(size float64, precision ...int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseDecimalBytes">ParseDecimalBytes</span>
|
||||
|
||||
<p>Returns the human readable bytes size string into the amount it represents(base 1000).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ParseDecimalBytes(size string) (uint64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseBinaryBytes">ParseBinaryBytes</span>
|
||||
|
||||
<p>Returns the human readable bytes size string into the amount it represents(base 1024).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ParseBinaryBytes(size string) (uint64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
299
docs/formatter_zh-CN.md
Normal file
299
docs/formatter_zh-CN.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Formatter
|
||||
|
||||
formatter 格式化器包含一些数据格式化处理方法。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/formatter/formatter.go](https://github.com/duke-git/lancet/blob/v1/formatter/formatter.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/formatter"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Comma](#Comma)
|
||||
- [Pretty](#Pretty)
|
||||
- [PrettyToWriter](#PrettyToWriter)
|
||||
- [DecimalBytes](#DecimalBytes)
|
||||
- [BinaryBytes](#BinaryBytes)
|
||||
- [ParseDecimalBytes](#ParseDecimalBytes)
|
||||
- [ParseBinaryBytes](#ParseBinaryBytes)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="Comma">Comma</span>
|
||||
|
||||
<p>用逗号每隔3位分割数字/字符串,签名添加符号。参数必须是数字或者可以转为数字的字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Comma(v interface{}, prefixSymbol string) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pretty">Pretty</span>
|
||||
|
||||
<p>返回pretty JSON字符串.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Pretty(v interface{}) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PrettyToWriter">PrettyToWriter</span>
|
||||
|
||||
<p>Pretty encode数据到writer。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func PrettyToWriter(v interface{}, out io.Writer) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
// }
|
||||
//
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DecimalBytes">DecimalBytes</span>
|
||||
|
||||
<p>返回十进制标准(以1000为基数)下的可读字节单位字符串。precision参数指定小数点后的位数,默认为4。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DecimalBytes(size float64, precision ...int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BinaryBytes">BinaryBytes</span>
|
||||
|
||||
<p>返回binary标准(以1024为基数)下的可读字节单位字符串。precision参数指定小数点后的位数,默认为4。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BinaryBytes(size float64, precision ...int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseDecimalBytes">ParseDecimalBytes</span>
|
||||
|
||||
<p>将字节单位字符串转换成其所表示的字节数(以1000为基数)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ParseDecimalBytes(size string) (uint64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseBinaryBytes">ParseBinaryBytes</span>
|
||||
|
||||
<p>将字节单位字符串转换成其所表示的字节数(以1024为基数)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ParseBinaryBytes(size string) (uint64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
493
docs/function.md
Normal file
493
docs/function.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# Function
|
||||
|
||||
Package function can control the flow of function execution and support part of functional programming.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/function/function.go](https://github.com/duke-git/lancet/blob/v1/function/function.go)
|
||||
[https://github.com/duke-git/lancet/blob/v1/function/watcher.go](https://github.com/duke-git/lancet/blob/v1/function/watcher.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [After](#After)
|
||||
- [Before](#Before)
|
||||
- [Curry](#Curry)
|
||||
- [Compose](#Compose)
|
||||
- [Debounce](#Debounce)
|
||||
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||
- [Throttle](#Throttle)
|
||||
- [Delay](#Delay)
|
||||
- [Pipeline](#Pipeline)
|
||||
- [Schedule](#Schedule)
|
||||
- [Watcher](#Watcher)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="After">After</span>
|
||||
|
||||
<p>Creates a function that invokes given func once it's called n or more times.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Before">Before</span>
|
||||
|
||||
<p>creates a function that invokes func once it's called less than n times.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Curry">Curry</span>
|
||||
|
||||
<p>Make a curry function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Fn func(...interface{}) interface{}
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Compose">Compose</span>
|
||||
|
||||
<p>Compose the function list from right to left, then return the composed function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounced">Debounced</span>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Debounced(fn func(), duration time.Duration) func()
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounce">Debounce</span>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Delay">Delay</span>
|
||||
|
||||
<p>Invoke function after delayed time.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{})
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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")
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Throttle">Throttle</span>
|
||||
|
||||
<p>Creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Throttle(fn func(), interval time.Duration) func()
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Schedule">Schedule</span>
|
||||
|
||||
<p>Invoke function every duration time, until close the returned bool chan.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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) //[* * * * *]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pipeline">Pipeline</span>
|
||||
|
||||
<p>Pipeline takes a list of functions and returns a function whose param will be passed into
|
||||
the functions one by one.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Pipeline(funcs ...func(interface{}) interface{}) func(interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addOne := func(x interface{}) interface{} {
|
||||
return x.(int) + 1
|
||||
}
|
||||
double := func(x interface{}) interface{} {
|
||||
return 2 * x.(int)
|
||||
}
|
||||
square := func(x interface{}) interface{} {
|
||||
return x.(int) * x.(int)
|
||||
}
|
||||
|
||||
f := function.Pipeline(addOne, double, square)
|
||||
|
||||
result := fn(2)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 36
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Watcher">Watcher</span>
|
||||
|
||||
<p>Watcher is used for record code excution time. can start/stop/reset the watch timer. get the elapsed time of function execution.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
492
docs/function_zh-CN.md
Normal file
492
docs/function_zh-CN.md
Normal file
@@ -0,0 +1,492 @@
|
||||
# Function
|
||||
|
||||
function 函数包控制函数执行流程,包含部分函数式编程。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/function/function.go](https://github.com/duke-git/lancet/blob/v1/function/function.go)
|
||||
[https://github.com/duke-git/lancet/blob/v1/function/watcher.go](https://github.com/duke-git/lancet/blob/v1/function/watcher.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [After](#After)
|
||||
- [Before](#Before)
|
||||
- [Curry](#Curry)
|
||||
- [Compose](#Compose)
|
||||
- [Debounce](#Debounce)
|
||||
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||
- [Throttle](#Throttle)
|
||||
- [Delay](#Delay)
|
||||
- [Pipeline](#Pipeline)
|
||||
- [Schedule](#Schedule)
|
||||
- [Watcher](#Watcher)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="After">After</span>
|
||||
|
||||
<p>创建一个函数,当他被调用n或更多次之后将马上触发fn</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Before">Before</span>
|
||||
|
||||
<p>创建一个函数,调用次数不超过n次,之后再调用这个函数,将返回一次最后调用fn的结果</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Curry">Curry</span>
|
||||
|
||||
<p>创建一个柯里化的函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Fn func(...interface{}) interface{}
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Compose">Compose</span>
|
||||
|
||||
<p>从右至左组合函数列表fnList, 返回组合后的函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounce">Debounce</span>
|
||||
|
||||
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounced">Debounced</span>
|
||||
|
||||
<p>创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Debounced(fn func(), duration time.Duration) func()
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Throttle">Throttle</span>
|
||||
|
||||
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Throttle(fn func(), interval time.Duration) func()
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Delay">Delay</span>
|
||||
|
||||
<p>延迟delay时间后调用函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{})
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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")
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Schedule">Schedule</span>
|
||||
|
||||
<p>每次持续时间调用函数,直到关闭返回的 bool chan</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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) //[* * * * *]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pipeline">Pipeline</span>
|
||||
|
||||
<p>管道执行多个函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Pipeline(funcs ...func(interface{}) interface{}) func(interface{}) interface{}
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addOne := func(x interface{}) interface{} {
|
||||
return x.(int) + 1
|
||||
}
|
||||
double := func(x interface{}) interface{} {
|
||||
return 2 * x.(int)
|
||||
}
|
||||
square := func(x interface{}) interface{} {
|
||||
return x.(int) * x.(int)
|
||||
}
|
||||
|
||||
f := function.Pipeline(addOne, double, square)
|
||||
|
||||
result := fn(2)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 36
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Watcher">Watcher</span>
|
||||
|
||||
<p>Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。 </p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
728
docs/mathutil.md
Normal file
728
docs/mathutil.md
Normal file
@@ -0,0 +1,728 @@
|
||||
# Mathutil
|
||||
|
||||
Package mathutil implements some functions for math calculation.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/v1/mathutil/mathutil.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/mathutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [Exponent](#Exponent)
|
||||
- [Fibonacci](#Fibonacci)
|
||||
- [Factorial](#Factorial)
|
||||
- [Percent](#Percent)
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [CeilToFloat](#CeilToFloat)
|
||||
- [CeilToString](#CeilToString)
|
||||
- [FloorToFloat](#FloorToFloat)
|
||||
- [FloorToString](#FloorToString)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
- [RadianToAngle](#RadianToAngle)
|
||||
- [PointDistance](#PointDistance)
|
||||
- [IsPrime](#IsPrime)
|
||||
- [GCD](#GCD)
|
||||
- [LCM](#LCM)
|
||||
- [Cos](#Cos)
|
||||
- [Sin](#Sin)
|
||||
- [Log](#Log)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Exponent">Exponent</span>
|
||||
|
||||
<p>Calculate x to the nth power.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Exponent(x, n int64) int64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Fibonacci">Fibonacci</span>
|
||||
|
||||
<p>Calculate the nth number of fibonacci sequence.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Fibonacci(first, second, n int) int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Factorial">Factorial</span>
|
||||
|
||||
<p>Calculate the factorial of x.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Factorial(x uint) uint
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Percent">Percent</span>
|
||||
|
||||
<p>calculate the percentage of val to total, retain n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Percent(val, total float64, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RoundToFloat">RoundToFloat</span>
|
||||
|
||||
<p>Round float up to n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RoundToFloat(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RoundToString">RoundToString</span>
|
||||
|
||||
<p>Round float up to n decimal places. will return string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RoundToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TruncRound">TruncRound</span>
|
||||
|
||||
<p>Round float off n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func TruncRound(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToFloat">CeilToFloat</span>
|
||||
|
||||
<p>Round float up n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToFloat(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToString">CeilToString</span>
|
||||
|
||||
<p>Round float up n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToFloat">FloorToFloat</span>
|
||||
|
||||
<p>Round float down n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToString">FloorToString</span>
|
||||
|
||||
<p>Round float down n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AngleToRadian">AngleToRadian</span>
|
||||
|
||||
<p>Converts angle value to radian value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func AngleToRadian(angle float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RadianToAngle">RadianToAngle</span>
|
||||
|
||||
<p>Converts radian value to angle value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RadianToAngle(radian float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PointDistance">PointDistance</span>
|
||||
|
||||
<p>Caculates two points distance.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func PointDistance(x1, y1, x2, y2 float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.PointDistance(1, 1, 4, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPrime">IsPrime</span>
|
||||
|
||||
<p>Checks if number is prime number.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsPrime(n int) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GCD">GCD</span>
|
||||
|
||||
<p>Return greatest common divisor (GCD) of integers.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GCD(integers ...int) int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LCM">LCM</span>
|
||||
|
||||
<p>Return Least Common Multiple (LCM) of integers.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func LCM(integers ...int) int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Cos">Cos</span>
|
||||
|
||||
<p>Returns the cosine of the radian argument.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Cos(radian float64, precision ...int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Sin">Sin</span>
|
||||
|
||||
<p>Returns the sine of the radian argument.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Sin(radian float64, precision ...int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Log">Log</span>
|
||||
|
||||
<p>Returns the logarithm of base n.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Log(n, base float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
732
docs/mathutil_zh-CN.md
Normal file
732
docs/mathutil_zh-CN.md
Normal file
@@ -0,0 +1,732 @@
|
||||
# Mathutil
|
||||
|
||||
mathutil 包实现了一些数学计算的函数.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/mathutil/mathutil.go](https://github.com/duke-git/lancet/blob/v1/mathutil/mathutil.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/mathutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Exponent](#Exponent)
|
||||
- [Fibonacci](#Fibonacci)
|
||||
- [Factorial](#Factorial)
|
||||
- [Percent](#Percent)
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [CeilToFloat](#CeilToFloat)
|
||||
- [CeilToString](#CeilToString)
|
||||
- [FloorToFloat](#FloorToFloat)
|
||||
- [FloorToString](#FloorToString)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
- [RadianToAngle](#RadianToAngle)
|
||||
- [PointDistance](#PointDistance)
|
||||
- [IsPrime](#IsPrime)
|
||||
- [GCD](#GCD)
|
||||
- [LCM](#LCM)
|
||||
- [Cos](#Cos)
|
||||
- [Sin](#Sin)
|
||||
- [Log](#Log)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Exponent">Exponent</span>
|
||||
|
||||
<p>指数计算(x的n次方)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Exponent(x, n int64) int64
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Fibonacci">Fibonacci</span>
|
||||
|
||||
<p>计算斐波那契数列的第n个数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Fibonacci(first, second, n int) int
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Factorial">Factorial</span>
|
||||
|
||||
<p>计算阶乘</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Factorial(x uint) uint
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Percent">Percent</span>
|
||||
|
||||
<p>计算百分比,保留n位小数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Percent(val, total float64, n int) float64
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RoundToFloat">RoundToFloat</span>
|
||||
|
||||
<p>四舍五入,保留n位小数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RoundToFloat(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RoundToString">RoundToString</span>
|
||||
|
||||
<p>四舍五入,保留n位小数,返回字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RoundToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TruncRound">TruncRound</span>
|
||||
|
||||
<p>截短n位小数(不进行四舍五入)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TruncRound(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToFloat">CeilToFloat</span>
|
||||
|
||||
<p>向上舍入(进一法),保留n位小数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToFloat(x float64, n int) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToString">CeilToString</span>
|
||||
|
||||
<p>向上舍入(进一法),保留n位小数,返回字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToFloat">FloorToFloat</span>
|
||||
|
||||
<p>向下舍入(去尾法),保留n位小数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToString">FloorToString</span>
|
||||
|
||||
<p>向下舍入(去尾法),保留n位小数,返回字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString(x float64, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AngleToRadian">AngleToRadian</span>
|
||||
|
||||
<p>将角度值转为弧度值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func AngleToRadian(angle float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RadianToAngle">RadianToAngle</span>
|
||||
|
||||
<p>将弧度值转为角度值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RadianToAngle(radian float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PointDistance">PointDistance</span>
|
||||
|
||||
<p>计算两个坐标点的距离</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func PointDistance(x1, y1, x2, y2 float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.PointDistance(1, 1, 4, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPrime">IsPrime</span>
|
||||
|
||||
<p>判断质数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsPrime(n int) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="GCD">GCD</span>
|
||||
|
||||
<p>计算整数最大公约数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GCD(integers ...int) int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LCM">LCM</span>
|
||||
|
||||
<p>计算整数最小公倍数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LCM(integers ...int) int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Cos">Cos</span>
|
||||
|
||||
<p>计算弧度的余弦值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Cos(radian float64, precision ...int) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Sin">Sin</span>
|
||||
|
||||
<p>计算弧度的正弦值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Sin(radian float64, precision ...int) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Log">Log</span>
|
||||
|
||||
<p>计算以base为底n的对数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Log(n, base float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
991
docs/netutil.md
Normal file
991
docs/netutil.md
Normal file
@@ -0,0 +1,991 @@
|
||||
# Netutil
|
||||
|
||||
Package netutil contains functions to get net information and send http request.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/net.go](https://github.com/duke-git/lancet/blob/v1/netutil/net.go)
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/http_client.go](https://github.com/duke-git/lancet/blob/v1/netutil/http_client.go)
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/http.go](https://github.com/duke-git/lancet/blob/v1/netutil/http.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 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)
|
||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||
- [HttpPut<sup>Deprecated</sup>](#HttpPut)
|
||||
- [HttpPatch<sup>Deprecated</sup>](#HttpPatch)
|
||||
- [ParseHttpResponse](#ParseHttpResponse)
|
||||
- [UploadFile](#UploadFile)
|
||||
- [DownloadFile](#DownloadFile)
|
||||
- [IsPingConnected](#IsPingConnected)
|
||||
- [IsTelnetConnected](#IsTelnetConnected)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="ConvertMapToQueryString">ConvertMapToQueryString</span>
|
||||
|
||||
<p>Convert map to url query string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EncodeUrl">EncodeUrl</span>
|
||||
|
||||
<p>Encode url query string values.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func EncodeUrl(urlStr string) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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) //http://www.lancet.com?a=1&b=%5B2%5D
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetInternalIp">GetInternalIp</span>
|
||||
|
||||
<p>Get internal ip information.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetInternalIp() string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetIps">GetIps</span>
|
||||
|
||||
<p>Get all ipv4 list.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetIps() []string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ips := netutil.GetIps()
|
||||
fmt.Println(ips) //[192.168.1.9]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetMacAddrs">GetMacAddrs</span>
|
||||
|
||||
<p>Get all mac addresses list.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetMacAddrs() []string {
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addrs := netutil.GetMacAddrs()
|
||||
fmt.Println(addrs)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetPublicIpInfo">GetPublicIpInfo</span>
|
||||
|
||||
<p>Get public ip information.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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"`
|
||||
}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetRequestPublicIp">GetRequestPublicIp</span>
|
||||
|
||||
<p>Get http request public ip.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetRequestPublicIp(req *http.Request) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ip := "36.112.24.10"
|
||||
|
||||
request1 := http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"X-Forwarded-For": {ip},
|
||||
},
|
||||
}
|
||||
publicIp1 := netutil.GetRequestPublicIp(&request1)
|
||||
fmt.Println(publicIp1) //36.112.24.10
|
||||
|
||||
request2 := http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"X-Real-Ip": {ip},
|
||||
},
|
||||
}
|
||||
publicIp2 := netutil.GetRequestPublicIp(&request2)
|
||||
fmt.Println(publicIp2) //36.112.24.10
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPublicIP">IsPublicIP</span>
|
||||
|
||||
<p>Checks if a ip is public or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsPublicIP(IP net.IP) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsInternalIP">IsInternalIP</span>
|
||||
|
||||
<p>Checks if an ip is intranet or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsInternalIP(IP net.IP) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ip1 := net.ParseIP("127.0.0.1")
|
||||
ip2 := net.ParseIP("36.112.24.10")
|
||||
|
||||
fmt.Println(netutil.IsInternalIP(ip1)) //true
|
||||
fmt.Println(netutil.IsInternalIP(ip2)) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpRequest">HttpRequest</span>
|
||||
|
||||
<p>HttpRequest is a struct used to abstract HTTP request entity.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type HttpRequest struct {
|
||||
RawURL string
|
||||
Method string
|
||||
Headers http.Header
|
||||
QueryParams url.Values
|
||||
FormData url.Values
|
||||
Body []byte
|
||||
}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/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,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpClient">HttpClient</span>
|
||||
|
||||
<p>HttpClient is a struct used to send HTTP request. It can be instanced with some configurations or none config.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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
|
||||
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
httpClientCfg := netutil.HttpClientConfig{
|
||||
SSLEnabled: true,
|
||||
HandshakeTimeout:10 * time.Second
|
||||
}
|
||||
httpClient := netutil.NewHttpClientWithConfig(&httpClientCfg)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SendRequest">SendRequest</span>
|
||||
|
||||
<p>Use HttpClient to send HTTP request.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
|
||||
fmt.Println(todo.Id) //1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DecodeResponse">DecodeResponse</span>
|
||||
|
||||
<p>Decode http response into target object.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (client *HttpClient) DecodeResponse(resp *http.Response, target interface{}) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
|
||||
fmt.Println(todo.Id) //1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="StructToUrlValues">StructToUrlValues</span>
|
||||
|
||||
<p>Convert struct to url values, only convert the field which is exported and has `json` tag.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func StructToUrlValues(targetStruct interface{}) url.Values
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpGet">HttpGet</span>
|
||||
|
||||
<p>Send http get request. (Deprecated: use SendRequest for replacement)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPost">HttpPost</span>
|
||||
|
||||
<p>Send http post request. (Deprecated: use SendRequest for replacement)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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/x-www-form-urlencoded",
|
||||
// "Content-Type": "multipart/form-data",
|
||||
}
|
||||
|
||||
postData := url.Values{}
|
||||
postData.Add("userId", "1")
|
||||
postData.Add("title", "TestToDo")
|
||||
|
||||
// postData := make(map[string]string)
|
||||
// postData["userId"] = "1"
|
||||
// postData["title"] = "title"
|
||||
|
||||
resp, err := netutil.HttpPost(apiUrl, header, nil, postData)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPut">HttpPut</span>
|
||||
|
||||
<p>Send http put request. (Deprecated: use SendRequest for replacement)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpDelete">HttpDelete</span>
|
||||
|
||||
<p>Send http delete request. (Deprecated: use SendRequest for replacement)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPatch">HttpPatch</span>
|
||||
|
||||
<p>Send http patch request. (Deprecated: use SendRequest for replacement)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseHttpResponse">ParseHttpResponse</span>
|
||||
|
||||
<p>Decode http response to specified interface.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UploadFile">UploadFile</span>
|
||||
|
||||
<p>upload the file to a server.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func UploadFile(filepath string, server string) (bool, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ok, err := netutil.UploadFile("./a.jpg", "http://www.xxx.com/bucket/test")
|
||||
|
||||
fmt.Println(ok)
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DownloadFile">DownloadFile</span>
|
||||
|
||||
<p>download the file exist in url to a local file.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DownloadFile(filepath string, url string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := DownloadFile("./lancet_logo.jpg", "https://picx.zhimg.com/v2-fc82a4199749de9cfb71e32e54f489d3_720w.jpg?source=172ae18b")
|
||||
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPingConnected">IsPingConnected</span>
|
||||
|
||||
<p>checks if can ping the specified host or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsPingConnected(host string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := netutil.IsPingConnected("www.baidu.com")
|
||||
result2 := netutil.IsPingConnected("www.!@#&&&.com")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsTelnetConnected">IsTelnetConnected</span>
|
||||
|
||||
<p>Checks if can telnet the specified host or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsTelnetConnected(host string, port string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
990
docs/netutil_zh-CN.md
Normal file
990
docs/netutil_zh-CN.md
Normal file
@@ -0,0 +1,990 @@
|
||||
# Netutil
|
||||
|
||||
netutil 网络包支持获取 ip 地址,发送 http 请求。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/net.go](https://github.com/duke-git/lancet/blob/v1/netutil/net.go)
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/http_client.go](https://github.com/duke-git/lancet/blob/v1/netutil/http_client.go)
|
||||
[https://github.com/duke-git/lancet/blob/v1/netutil/http.go](https://github.com/duke-git/lancet/blob/v1/netutil/http.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [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)
|
||||
- [HttpGet<sup>Deprecated</sup>](#HttpGet)
|
||||
- [HttpDelete<sup>Deprecated</sup>](#HttpDelete)
|
||||
- [HttpPost<sup>Deprecated</sup>](#HttpPost)
|
||||
- [HttpPut<sup>Deprecated</sup>](#HttpPut)
|
||||
- [HttpPatch<sup>Deprecated</sup>](#HttpPatch)
|
||||
- [ParseHttpResponse](#ParseHttpResponse)
|
||||
- [UploadFile](#UploadFile)
|
||||
- [DownloadFile](#DownloadFile)
|
||||
- [IsPingConnected](#IsPingConnected)
|
||||
- [IsTelnetConnected](#IsTelnetConnected)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="ConvertMapToQueryString">ConvertMapToQueryString</span>
|
||||
|
||||
<p>将map转换成http查询字符串.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EncodeUrl">EncodeUrl</span>
|
||||
|
||||
<p>编码url query string的值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func EncodeUrl(urlStr string) (string, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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) //http://www.lancet.com?a=1&b=%5B2%5D
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetInternalIp">GetInternalIp</span>
|
||||
|
||||
<p>获取内部ip</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GetInternalIp() string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetIps">GetIps</span>
|
||||
|
||||
<p>获取ipv4地址列表</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GetIps() []string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ips := netutil.GetIps()
|
||||
fmt.Println(ips) //[192.168.1.9]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetMacAddrs">GetMacAddrs</span>
|
||||
|
||||
<p>获取mac地址列</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GetMacAddrs() []string {
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addrs := netutil.GetMacAddrs()
|
||||
fmt.Println(addrs)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetPublicIpInfo">GetPublicIpInfo</span>
|
||||
|
||||
<p>获取公网ip信息</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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"`
|
||||
}
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetRequestPublicIp">GetRequestPublicIp</span>
|
||||
|
||||
<p>获取http请求ip</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GetRequestPublicIp(req *http.Request) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ip := "36.112.24.10"
|
||||
|
||||
request1 := http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"X-Forwarded-For": {ip},
|
||||
},
|
||||
}
|
||||
publicIp1 := netutil.GetRequestPublicIp(&request1)
|
||||
fmt.Println(publicIp1) //36.112.24.10
|
||||
|
||||
request2 := http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"X-Real-Ip": {ip},
|
||||
},
|
||||
}
|
||||
publicIp2 := netutil.GetRequestPublicIp(&request2)
|
||||
fmt.Println(publicIp2) //36.112.24.10
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPublicIP">IsPublicIP</span>
|
||||
|
||||
<p>判断ip是否是公共ip</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsPublicIP(IP net.IP) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsInternalIP">IsInternalIP</span>
|
||||
|
||||
<p>判断ip是否是局域网ip.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsInternalIP(IP net.IP) bool
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ip1 := net.ParseIP("127.0.0.1")
|
||||
ip2 := net.ParseIP("36.112.24.10")
|
||||
|
||||
fmt.Println(netutil.IsInternalIP(ip1)) //true
|
||||
fmt.Println(netutil.IsInternalIP(ip2)) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpRequest">HttpRequest</span>
|
||||
|
||||
<p>HttpRequest用于抽象HTTP请求实体的结构</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type HttpRequest struct {
|
||||
RawURL string
|
||||
Method string
|
||||
Headers http.Header
|
||||
QueryParams url.Values
|
||||
FormData url.Values
|
||||
Body []byte
|
||||
}
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"github.com/duke-git/lancet/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,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpClient">HttpClient</span>
|
||||
|
||||
<p>HttpClient是用于发送HTTP请求的结构体。它可以用一些配置参数或无配置实例化.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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
|
||||
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
httpClientCfg := netutil.HttpClientConfig{
|
||||
SSLEnabled: true,
|
||||
HandshakeTimeout:10 * time.Second
|
||||
}
|
||||
httpClient := netutil.NewHttpClientWithConfig(&httpClientCfg)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SendRequest">SendRequest</span>
|
||||
|
||||
<p>HttpClient发送http请求</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
|
||||
fmt.Println(todo.Id) //1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DecodeResponse">DecodeResponse</span>
|
||||
|
||||
<p>解析http响应体到目标结构体</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (client *HttpClient) DecodeResponse(resp *http.Response, target interface{}) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"github.com/duke-git/lancet/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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
|
||||
fmt.Println(todo.Id) //1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="StructToUrlValues">StructToUrlValues</span>
|
||||
|
||||
<p>将结构体转为url values, 仅转化结构体导出字段并且包含`json` tag.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func StructToUrlValues(targetStruct interface{}) url.Values
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpGet">HttpGet</span>
|
||||
|
||||
<p>发送http get请求。(已废弃: 使用SendRequest)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPost">HttpPost</span>
|
||||
|
||||
<p>发送http post请求。(已废弃: 使用SendRequest)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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/x-www-form-urlencoded",
|
||||
// "Content-Type": "multipart/form-data",
|
||||
}
|
||||
|
||||
postData := url.Values{}
|
||||
postData.Add("userId", "1")
|
||||
postData.Add("title", "TestToDo")
|
||||
|
||||
// postData := make(map[string]string)
|
||||
// postData["userId"] = "1"
|
||||
// postData["title"] = "title"
|
||||
|
||||
resp, err := netutil.HttpPost(apiUrl, header, nil, postData)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(body)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPut">HttpPut</span>
|
||||
|
||||
<p>发送http put请求。(已废弃: 使用SendRequest)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpDelete">HttpDelete</span>
|
||||
|
||||
<p>发送http delete请求。(已废弃: 使用SendRequest)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HttpPatch">HttpPatch</span>
|
||||
|
||||
<p>发送http patch请求。(已废弃: 使用SendRequest)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParseHttpResponse">ParseHttpResponse</span>
|
||||
|
||||
<p>将http请求响应解码成特定struct值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UploadFile">UploadFile</span>
|
||||
|
||||
<p>将文件上传指定的server地址。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func UploadFile(filepath string, server string) (bool, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ok, err := netutil.UploadFile("./a.jpg", "http://www.xxx.com/bucket/test")
|
||||
|
||||
fmt.Println(ok)
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DownloadFile">DownloadFile</span>
|
||||
|
||||
<p>从指定的server地址下载文件。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DownloadFile(filepath string, url string) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := DownloadFile("./lancet_logo.jpg", "https://picx.zhimg.com/v2-fc82a4199749de9cfb71e32e54f489d3_720w.jpg?source=172ae18b")
|
||||
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsPingConnected">IsPingConnected</span>
|
||||
|
||||
<p>检查能否ping通主机。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsPingConnected(host string) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := netutil.IsPingConnected("www.baidu.com")
|
||||
result2 := netutil.IsPingConnected("www.!@#&&&.com")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsTelnetConnected">IsTelnetConnected</span>
|
||||
|
||||
<p>检查能否telnet到主机。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsTelnetConnected(host string, port string) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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
|
||||
}
|
||||
```
|
||||
439
docs/random.md
Normal file
439
docs/random.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# Random
|
||||
|
||||
Package random implements some basic functions to generate random int and string.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/random/random.go](https://github.com/duke-git/lancet/blob/v1/random/random.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [RandBytes](#RandBytes)
|
||||
- [RandInt](#RandInt)
|
||||
- [RandString](#RandString)
|
||||
- [RandUpper](#RandUpper)
|
||||
- [RandLower](#RandLower)
|
||||
- [RandNumeral](#RandNumeral)
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
- [UUIdV4](#UUIdV4)
|
||||
- [RandFloat](#RandFloat)
|
||||
- [RandFloats](#RandFloats)
|
||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||
- [RandIntSlice](#RandIntSlice)
|
||||
- [RandStringSlice](#RandStringSlice)
|
||||
- [RandBool](#RandBool)
|
||||
- [RandBoolSlice](#RandBoolSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="RandBytes">RandBytes</span>
|
||||
|
||||
<p>Generate random byte slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randBytes := random.RandBytes(4)
|
||||
fmt.Println(randBytes)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandInt">RandInt</span>
|
||||
|
||||
<p>Generate random int between min and max, may contain min, not max.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandInt(min, max int) int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rInt := random.RandInt(1, 10)
|
||||
fmt.Println(rInt)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandString">RandString</span>
|
||||
|
||||
<p>Generate random given length string. only contains letter (a-zA-Z)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandString(length int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr) //pGWsze
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUpper">RandUpper</span>
|
||||
|
||||
<p>Generate a random upper case string</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandUpper(length int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr) //PACWGF
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandLower">RandLower</span>
|
||||
|
||||
<p>Generate a random lower case string</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandLower(length int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandLower(6)
|
||||
fmt.Println(randStr) //siqbew
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandNumeral">RandNumeral</span>
|
||||
|
||||
<p>Generate a random numeral string</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandNumeral(length int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandNumeral(6)
|
||||
fmt.Println(randStr) //035172
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandNumeralOrLetter">RandNumeralOrLetter</span>
|
||||
|
||||
<p>generate a random numeral or letter string</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandNumeralOrLetter(length int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandNumeralOrLetter(6)
|
||||
fmt.Println(randStr) //0aW7cQ
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UUIdV4">UUIdV4</span>
|
||||
|
||||
<p>Generate a random UUID of version 4 according to RFC 4122.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func UUIdV4() (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uuid, err := random.UUIdV4()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(uuid)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandFloat">RandFloat</span>
|
||||
|
||||
<p>Generate a random float64 number between [min, max) with specific precision.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandFloat(min, max float64, precision int) float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zbD_tuobJtr)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
floatNumber := random.RandFloat(1.0, 5.0, 2)
|
||||
fmt.Println(floatNumber) //2.14 (random number)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandFloats">RandFloats</span>
|
||||
|
||||
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandFloats(length int, min, max float64, precision int) []float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
|
||||
<p>Generate a slice of random int of length n that do not repeat.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(n, min, max int) []int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := RandUniqueIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[0 4 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandIntSlice">RandIntSlice</span>
|
||||
|
||||
<p>Generate a slice of random int. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[1 4 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandStringSlice">RandStringSlice</span>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||
fmt.Println(strs)
|
||||
|
||||
// output random string slice like below:
|
||||
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBool">RandBool</span>
|
||||
|
||||
<p>Generate a random boolean value (true or false).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandBool() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBool()
|
||||
fmt.Println(result) // true or false (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||
|
||||
<p>Generates a random boolean slice of specified length.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandBoolSlice(length int) []bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBoolSlice(2)
|
||||
fmt.Println(result) // [true false] (random)
|
||||
}
|
||||
```
|
||||
439
docs/random_zh-CN.md
Normal file
439
docs/random_zh-CN.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# Random
|
||||
|
||||
random 随机数生成器包,可以生成随机[]bytes, int, string。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/random/random.go](https://github.com/duke-git/lancet/blob/v1/random/random.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [RandBytes](#RandBytes)
|
||||
- [RandInt](#RandInt)
|
||||
- [RandString](#RandString)
|
||||
- [RandUpper](#RandUpper)
|
||||
- [RandLower](#RandLower)
|
||||
- [RandNumeral](#RandNumeral)
|
||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||
- [UUIdV4](#UUIdV4)
|
||||
- [RandFloat](#RandFloat)
|
||||
- [RandFloats](#RandFloats)
|
||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||
- [RandIntSlice](#RandIntSlice)
|
||||
- [RandStringSlice](#RandStringSlice)
|
||||
- [RandBool](#RandBool)
|
||||
- [RandBoolSlice](#RandBoolSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="RandBytes">RandBytes</span>
|
||||
|
||||
<p>生成随机字节切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randBytes := random.RandBytes(4)
|
||||
fmt.Println(randBytes)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandInt">RandInt</span>
|
||||
|
||||
<p>生成随机int, 范围[min, max)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandInt(min, max int) int
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rInt := random.RandInt(1, 10)
|
||||
fmt.Println(rInt)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandString">RandString</span>
|
||||
|
||||
<p>生成给定长度的随机字符串,只包含字母(a-zA-Z)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandString(length int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr) //pGWsze
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUpper">RandUpper</span>
|
||||
|
||||
<p>生成给定长度的随机大写字母字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandUpper(length int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr) //PACWGF
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandLower">RandLower</span>
|
||||
|
||||
<p>生成给定长度的随机小写字母字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandLower(length int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandLower(6)
|
||||
fmt.Println(randStr) //siqbew
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandNumeral">RandNumeral</span>
|
||||
|
||||
<p>生成给定长度的随机数字字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandNumeral(length int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandNumeral(6)
|
||||
fmt.Println(randStr) //035172
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandNumeralOrLetter">RandNumeralOrLetter</span>
|
||||
|
||||
<p>生成给定长度的随机字符串(数字+字母)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandNumeralOrLetter(length int) string
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandNumeralOrLetter(6)
|
||||
fmt.Println(randStr) //0aW7cQ
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="UUIdV4">UUIdV4</span>
|
||||
|
||||
<p>生成UUID v4字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func UUIdV4() (string, error)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uuid, err := random.UUIdV4()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(uuid)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandFloat">RandFloat</span>
|
||||
|
||||
<p>Generate a random float64 number between [min, max) with specific precision.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandFloat(min, max float64, precision int) float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zbD_tuobJtr)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
floatNumber := random.RandFloat(1.0, 5.0, 2)
|
||||
fmt.Println(floatNumber) //2.14 (random number)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandFloats">RandFloats</span>
|
||||
|
||||
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RandFloats(length int, min, max float64, precision int) []float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||
|
||||
<p>生成一个不重复的长度为n的随机int切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandUniqueIntSlice(n, min, max int) []int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := RandUniqueIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[0 4 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandIntSlice">RandIntSlice</span>
|
||||
|
||||
<p>生成一个特定长度的随机int切片,数值范围[min, max)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandIntSlice(length, min, max int) []int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandIntSlice(5, 0, 10)
|
||||
fmt.Println(result) //[1 2 7 1 5] (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandStringSlice">RandStringSlice</span>
|
||||
|
||||
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||
fmt.Println(strs)
|
||||
|
||||
// output random string slice like below:
|
||||
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBool">RandBool</span>
|
||||
|
||||
<p>生成随机bool值(true or false)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandBool() bool
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBool()
|
||||
fmt.Println(result) // true or false (random)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||
|
||||
<p>生成特定长度的随机bool slice。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RandBoolSlice(length int) []bool
|
||||
```
|
||||
|
||||
<b>实例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := random.RandBoolSlice(2)
|
||||
fmt.Println(result) // [true false] (random)
|
||||
}
|
||||
```
|
||||
239
docs/retry.md
Normal file
239
docs/retry.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Retry
|
||||
|
||||
Package retry is for executing a function repeatedly until it was successful or canceled by the context.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/retry/retry.go](https://github.com/duke-git/lancet/blob/v1/retry/retry.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/retry"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [Context](#Context)
|
||||
- [Retry](#Retry)
|
||||
- [RetryFunc](#RetryFunc)
|
||||
- [RetryDuration](#RetryDuration)
|
||||
- [RetryTimes](#RetryTimes)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Context">Context</span>
|
||||
|
||||
<p>Set retry context config, can cancel the retry with context.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Context(ctx context.Context)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryFunc">RetryFunc</span>
|
||||
|
||||
<p>Function that retry executes.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type RetryFunc func() error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryTimes">RetryTimes</span>
|
||||
|
||||
<p>Set times of retry. Default times is 5.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RetryTimes(n uint)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryDuration">RetryDuration</span>
|
||||
|
||||
<p>Set duration of retries. Default duration is 3 second.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RetryDuration(d time.Duration)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Retry">Retry</span>
|
||||
|
||||
<p>Executes the retryFunc repeatedly until it was successful or canceled by the context.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Retry(retryFunc RetryFunc, opts ...Option) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
239
docs/retry_zh-CN.md
Normal file
239
docs/retry_zh-CN.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Retry
|
||||
|
||||
retry 重试执行函数直到函数运行成功或被 context cancel。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/retry/retry.go](https://github.com/duke-git/lancet/blob/v1/retry/retry.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/retry"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Context](#Context)
|
||||
- [Retry](#Retry)
|
||||
- [RetryFunc](#RetryFunc)
|
||||
- [RetryDuration](#RetryDuration)
|
||||
- [RetryTimes](#RetryTimes)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Document 文档
|
||||
|
||||
### <span id="Context">Context</span>
|
||||
|
||||
<p>设置重试context参数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Context(ctx context.Context)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryFunc">RetryFunc</span>
|
||||
|
||||
<p>被重试执行的函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type RetryFunc func() error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryTimes">RetryTimes</span>
|
||||
|
||||
<p>设置重试次数,默认5</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RetryTimes(n uint)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryDuration">RetryDuration</span>
|
||||
|
||||
<p>设置重试间隔时间,默认3秒</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RetryDuration(d time.Duration)
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Retry">Retry</span>
|
||||
|
||||
<p>重试执行函数retryFunc,直到函数运行成功,或被context停止</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Retry(retryFunc RetryFunc, opts ...Option) error
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
1208
docs/slice.md
Normal file
1208
docs/slice.md
Normal file
File diff suppressed because it is too large
Load Diff
1205
docs/slice_zh-CN.md
Normal file
1205
docs/slice_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
1575
docs/strutil.md
Normal file
1575
docs/strutil.md
Normal file
File diff suppressed because it is too large
Load Diff
1609
docs/strutil_zh-CN.md
Normal file
1609
docs/strutil_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
261
docs/system.md
Normal file
261
docs/system.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# System
|
||||
|
||||
Package system contains some functions about os, runtime, shell command.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/system/os.go](https://github.com/duke-git/lancet/blob/v1/system/os.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
|
||||
- [IsWindows](#IsWindows)
|
||||
- [IsLinux](#IsLinux)
|
||||
- [IsMac](#IsMac)
|
||||
- [GetOsEnv](#GetOsEnv)
|
||||
- [SetOsEnv](#SetOsEnv)
|
||||
- [RemoveOsEnv](#RemoveOsEnv)
|
||||
- [CompareOsEnv](#CompareOsEnv)
|
||||
- [ExecCommand](#ExecCommand)
|
||||
- [GetOsBits](#GetOsBits)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="IsWindows">IsWindows</span>
|
||||
|
||||
<p>Check if current os is windows.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsWindows() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsWindows := system.IsWindows()
|
||||
fmt.Println(isOsWindows)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsLinux">IsLinux</span>
|
||||
|
||||
<p>Check if current os is linux.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsLinux() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsLinux := system.IsLinux()
|
||||
fmt.Println(isOsLinux)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsMac">IsMac</span>
|
||||
|
||||
<p>Check if current os is macos.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsMac() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsMac := system.IsMac
|
||||
fmt.Println(isOsMac)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetOsEnv">GetOsEnv</span>
|
||||
|
||||
<p>Gets the value of the environment variable named by the key.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetOsEnv(key string) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fooEnv := system.GetOsEnv("foo")
|
||||
fmt.Println(fooEnv)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SetOsEnv">SetOsEnv</span>
|
||||
|
||||
<p>Sets the value of the environment variable named by the key.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func SetOsEnv(key, value string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := system.SetOsEnv("foo", "foo_value")
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveOsEnv">RemoveOsEnv</span>
|
||||
|
||||
<p>Remove a single environment variable.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveOsEnv(key string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := system.RemoveOsEnv("foo")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CompareOsEnv">CompareOsEnv</span>
|
||||
|
||||
<p>Get env named by the key and compare it with comparedEnv.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CompareOsEnv(key, comparedEnv string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ExecCommand">ExecCommand</span>
|
||||
|
||||
<p>use shell /bin/bash -c(linux) or cmd (windows) to execute command.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type (
|
||||
Option func(*exec.Cmd)
|
||||
)
|
||||
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
out, errout, err := system.ExecCommand("ls", system.WithForeground())
|
||||
fmt.Println(out)
|
||||
fmt.Println(errout)
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetOsBits">GetOsBits</span>
|
||||
|
||||
<p>获取当前操作系统位数,返回32或64</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GetOsBits() int
|
||||
```
|
||||
|
||||
<b>例子:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
osBit := system.GetOsBits()
|
||||
fmt.Println(osBit)
|
||||
}
|
||||
```
|
||||
261
docs/system_zh-CN.md
Normal file
261
docs/system_zh-CN.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# System
|
||||
|
||||
system 包含 os, runtime, shell command 相关函数。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
[https://github.com/duke-git/lancet/blob/v1/system/os.go](https://github.com/duke-git/lancet/blob/v1/system/os.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [IsWindows](#IsWindows)
|
||||
- [IsLinux](#IsLinux)
|
||||
- [IsMac](#IsMac)
|
||||
- [GetOsEnv](#GetOsEnv)
|
||||
- [SetOsEnv](#SetOsEnv)
|
||||
- [RemoveOsEnv](#RemoveOsEnv)
|
||||
- [CompareOsEnv](#CompareOsEnv)
|
||||
- [ExecCommand](#ExecCommand)
|
||||
- [GetOsBits](#GetOsBits)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation 文档
|
||||
|
||||
### <span id="IsWindows">IsWindows</span>
|
||||
|
||||
<p>检查当前操作系统是否是windows</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsWindows() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsWindows := system.IsWindows()
|
||||
fmt.Println(isOsWindows)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsLinux">IsLinux</span>
|
||||
|
||||
<p>检查当前操作系统是否是linux</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsLinux() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsLinux := system.IsLinux()
|
||||
fmt.Println(isOsLinux)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsMac">IsMac</span>
|
||||
|
||||
<p>检查当前操作系统是否是macos</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsMac() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isOsMac := system.IsMac
|
||||
fmt.Println(isOsMac)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetOsEnv">GetOsEnv</span>
|
||||
|
||||
<p>获取key命名的环境变量的值</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetOsEnv(key string) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fooEnv := system.GetOsEnv("foo")
|
||||
fmt.Println(fooEnv)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SetOsEnv">SetOsEnv</span>
|
||||
|
||||
<p>设置由key命名的环境变量的值</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func SetOsEnv(key, value string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := system.SetOsEnv("foo", "foo_value")
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RemoveOsEnv">RemoveOsEnv</span>
|
||||
|
||||
<p>删除单个环境变量</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveOsEnv(key string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := system.RemoveOsEnv("foo")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CompareOsEnv">CompareOsEnv</span>
|
||||
|
||||
<p>获取key命名的环境变量值并与compareEnv进行比较</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CompareOsEnv(key, comparedEnv string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ExecCommand">ExecCommand</span>
|
||||
|
||||
<p>使用shell /bin/bash -c(linux) 或 cmd (windows) 执行shell命令</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type (
|
||||
Option func(*exec.Cmd)
|
||||
)
|
||||
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
out, errout, err := system.ExecCommand("ls", system.WithForeground())
|
||||
fmt.Println(out)
|
||||
fmt.Println(errout)
|
||||
fmt.Println(err)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GetOsBits">GetOsBits</span>
|
||||
|
||||
<p>Get current os bits, 32bit or 64bit. return 32 or 64</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GetOsBits() int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
osBit := system.GetOsBits()
|
||||
fmt.Println(osBit)
|
||||
}
|
||||
```
|
||||
1335
docs/validator.md
Normal file
1335
docs/validator.md
Normal file
File diff suppressed because it is too large
Load Diff
1346
docs/validator_zh-CN.md
Normal file
1346
docs/validator_zh-CN.md
Normal file
File diff suppressed because it is too large
Load Diff
638
fileutil/file.go
638
fileutil/file.go
@@ -5,11 +5,28 @@
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
// IsExist checks if a file or directory exists
|
||||
@@ -35,6 +52,11 @@ func CreateFile(path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDir create directory in absolute path. param `absPath` like /a/, /a/b/
|
||||
func CreateDir(absPath string) error {
|
||||
return os.MkdirAll(absPath, os.ModePerm)
|
||||
}
|
||||
|
||||
// IsDir checks if the path is directory or not
|
||||
func IsDir(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
@@ -76,7 +98,7 @@ func CopyFile(srcFilePath string, dstFilePath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
//ClearFile write empty string to path file
|
||||
// ClearFile write empty string to path file
|
||||
func ClearFile(path string) error {
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
@@ -88,7 +110,7 @@ func ClearFile(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
//ReadFileToString return string of file content
|
||||
// ReadFileToString return string of file content
|
||||
func ReadFileToString(path string) (string, error) {
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -148,3 +170,615 @@ func ListFileNames(path string) ([]string, error) {
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Zip create zip file, fpath could be a single file or a directory
|
||||
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
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
archive := zip.NewWriter(zipFile)
|
||||
defer archive.Close()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/")
|
||||
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
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
|
||||
func UnZip(zipFile string, destPath string) error {
|
||||
zipReader, err := zip.OpenReader(zipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipReader.Close()
|
||||
|
||||
for _, f := range zipReader.File {
|
||||
//issue#62: fix ZipSlip bug
|
||||
path, err := safeFilepathJoin(destPath, f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
} else {
|
||||
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inFile, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inFile.Close()
|
||||
|
||||
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
_, err = io.Copy(outFile, inFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
func IsLink(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.Mode()&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
// FileMode return file's mode and permission
|
||||
func FileMode(path string) (fs.FileMode, error) {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return fi.Mode(), nil
|
||||
}
|
||||
|
||||
// MiMeType return file mime type
|
||||
// param `file` should be string(file path) or *os.File
|
||||
func MiMeType(file interface{}) string {
|
||||
var mediatype string
|
||||
|
||||
readBuffer := func(f *os.File) ([]byte, error) {
|
||||
buffer := make([]byte, 512)
|
||||
_, err := f.Read(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
if filePath, ok := file.(string); ok {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return mediatype
|
||||
}
|
||||
buffer, err := readBuffer(f)
|
||||
if err != nil {
|
||||
return mediatype
|
||||
}
|
||||
return http.DetectContentType(buffer)
|
||||
}
|
||||
|
||||
if f, ok := file.(*os.File); ok {
|
||||
buffer, err := readBuffer(f)
|
||||
if err != nil {
|
||||
return mediatype
|
||||
}
|
||||
return http.DetectContentType(buffer)
|
||||
}
|
||||
return mediatype
|
||||
}
|
||||
|
||||
// CurrentPath return current absolute path.
|
||||
func CurrentPath() string {
|
||||
var absPath string
|
||||
_, filename, _, ok := runtime.Caller(1)
|
||||
if ok {
|
||||
absPath = filepath.Dir(filename)
|
||||
}
|
||||
|
||||
return absPath
|
||||
}
|
||||
|
||||
// IsZipFile checks if file is zip or not.
|
||||
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"))
|
||||
}
|
||||
|
||||
// FileSize returns file size in bytes.
|
||||
func FileSize(path string) (int64, error) {
|
||||
f, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.Size(), nil
|
||||
}
|
||||
|
||||
// MTime returns file modified time.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
// 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]interface{}, 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 {
|
||||
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 {
|
||||
var row []string
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// WriteStringToFile write string to target file.
|
||||
func WriteStringToFile(filepath string, content string, append bool) 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()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteBytesToFile write bytes to target file.
|
||||
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.
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// ChunkRead 从文件的指定偏移读取块并返回块内所有行
|
||||
func ChunkRead(f *os.File, offset int64, size int, bufPool *sync.Pool) []string {
|
||||
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
|
||||
n, err := f.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
||||
if err != nil && err != io.EOF {
|
||||
log.Fatal(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
|
||||
}
|
||||
|
||||
// 并行读取文件并将每个块的行发送到指定通道
|
||||
// filePath 文件路径
|
||||
// ChunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||
// MaxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
||||
// linesCh用于接收返回结果的通道。
|
||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, ChunkSizeMB, MaxGoroutine int) {
|
||||
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 {
|
||||
log.Fatalf("failed to open file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get file info: %v", 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 {
|
||||
linesCh <- ChunkRead(f, chunkOffset, ChunkSize, &bufPool)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// 等待所有解析完成后关闭行通道
|
||||
wg.Wait()
|
||||
close(linesCh)
|
||||
}
|
||||
|
||||
@@ -1,138 +1,451 @@
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestIsExist(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsExist")
|
||||
|
||||
cases := []string{"./", "./file.go", "./a.txt"}
|
||||
expected := []bool{true, true, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := IsExist(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsExist", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual := IsExist(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCreateFile")
|
||||
|
||||
f := "./text.txt"
|
||||
if CreateFile(f) {
|
||||
file, err := os.Open(f)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error: "+err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
if file.Name() != f {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, file.Name())
|
||||
t.FailNow()
|
||||
}
|
||||
assert.IsNil(err)
|
||||
assert.Equal(f, file.Name())
|
||||
} else {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error")
|
||||
t.FailNow()
|
||||
}
|
||||
os.Remove(f)
|
||||
}
|
||||
|
||||
func TestCreateDir(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCreateDir")
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
dirPath := pwd + "/a/"
|
||||
err = CreateDir(dirPath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(true, IsExist(dirPath))
|
||||
os.Remove(dirPath)
|
||||
assert.Equal(false, IsExist(dirPath))
|
||||
}
|
||||
|
||||
func TestIsDir(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsDir")
|
||||
|
||||
cases := []string{"./", "./a.txt"}
|
||||
expected := []bool{true, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := IsDir(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsDir", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual := IsDir(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRemoveFile")
|
||||
f := "./text.txt"
|
||||
if CreateFile(f) {
|
||||
if !IsExist(f) {
|
||||
CreateFile(f)
|
||||
err := RemoveFile(f)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "RemoveFile", f, f, err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
utils.LogFailedTestInfo(t, "RemoveFile", f, f, "create file error")
|
||||
t.FailNow()
|
||||
assert.IsNil(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCopyFile")
|
||||
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
dstFile := "./text_copy.txt"
|
||||
destFile := "./text_copy.txt"
|
||||
|
||||
err := CopyFile(srcFile, dstFile)
|
||||
err := CopyFile(srcFile, destFile)
|
||||
if err != nil {
|
||||
file, err := os.Open(dstFile)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, "create file error: "+err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
if file.Name() != dstFile {
|
||||
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, file.Name())
|
||||
t.FailNow()
|
||||
}
|
||||
file, err := os.Open(destFile)
|
||||
assert.IsNil(err)
|
||||
assert.Equal(destFile, file.Name())
|
||||
}
|
||||
os.Remove(srcFile)
|
||||
os.Remove(destFile)
|
||||
}
|
||||
|
||||
func TestListFileNames(t *testing.T) {
|
||||
filesInCurrentPath, err := ListFileNames("../datetime/")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
expected := []string{"datetime.go", "datetime_test.go"}
|
||||
if !reflect.DeepEqual(filesInCurrentPath, expected) {
|
||||
utils.LogFailedTestInfo(t, "ToChar", "./", expected, filesInCurrentPath)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestListFileNames")
|
||||
|
||||
filesInPath, err := ListFileNames("../datetime/")
|
||||
assert.IsNil(err)
|
||||
|
||||
expected := []string{"conversion.go", "conversion_test.go", "datetime.go", "datetime_test.go"}
|
||||
assert.Equal(expected, filesInPath)
|
||||
}
|
||||
|
||||
func TestReadFileToString(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestReadFileToString")
|
||||
|
||||
path := "./text.txt"
|
||||
CreateFile(path)
|
||||
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
f.WriteString("hello world")
|
||||
|
||||
res, _ := ReadFileToString(path)
|
||||
if res != "hello world" {
|
||||
utils.LogFailedTestInfo(t, "ReadFileToString", path, "hello world", res)
|
||||
}
|
||||
content, _ := ReadFileToString(path)
|
||||
assert.Equal("hello world", content)
|
||||
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
func TestClearFile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestClearFile")
|
||||
|
||||
path := "./text.txt"
|
||||
CreateFile(path)
|
||||
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
|
||||
f.WriteString("hello world")
|
||||
|
||||
CreateFile(path)
|
||||
err := ClearFile(path)
|
||||
assert.IsNil(err)
|
||||
|
||||
res, _ := ReadFileToString(path)
|
||||
if res != "" {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", path, "", res)
|
||||
}
|
||||
content, _ := ReadFileToString(path)
|
||||
assert.Equal("", content)
|
||||
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
func TestReadFileByLine(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestReadFileByLine")
|
||||
|
||||
path := "./text.txt"
|
||||
CreateFile(path)
|
||||
|
||||
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
f.WriteString("hello\nworld")
|
||||
|
||||
expected := []string{"hello", "world"}
|
||||
res, _ := ReadFileByLine(path)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "ReadFileByLine", path, expected, res)
|
||||
}
|
||||
actual, _ := ReadFileByLine(path)
|
||||
assert.Equal(expected, actual)
|
||||
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
func TestZipAndUnZip(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestZipAndUnZip")
|
||||
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer file.Close()
|
||||
file.WriteString("hello\nworld")
|
||||
|
||||
zipFile := "./text.zip"
|
||||
err := Zip(srcFile, zipFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
unZipPath := "./unzip"
|
||||
err = UnZip(zipFile, unZipPath)
|
||||
assert.IsNil(err)
|
||||
|
||||
unZipFile := "./unzip/text.txt"
|
||||
assert.Equal(true, IsExist(unZipFile))
|
||||
|
||||
os.Remove(srcFile)
|
||||
os.Remove(zipFile)
|
||||
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 TestFileMode(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFileMode")
|
||||
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
mode, err := FileMode(srcFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log(mode)
|
||||
|
||||
os.Remove(srcFile)
|
||||
}
|
||||
|
||||
func TestIsLink(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsLink")
|
||||
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
linkFile := "./text.link"
|
||||
if !IsExist(linkFile) {
|
||||
_ = os.Symlink(srcFile, linkFile)
|
||||
}
|
||||
assert.Equal(true, IsLink(linkFile))
|
||||
|
||||
assert.Equal(false, IsLink("./file.go"))
|
||||
|
||||
os.Remove(srcFile)
|
||||
os.Remove(linkFile)
|
||||
}
|
||||
|
||||
func TestMiMeType(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMiMeType")
|
||||
|
||||
f, _ := os.Open("./file.go")
|
||||
assert.Equal("text/plain; charset=utf-8", MiMeType(f))
|
||||
assert.Equal("text/plain; charset=utf-8", MiMeType("./file.go"))
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
// 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")
|
||||
t.Log(content)
|
||||
|
||||
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", content[0][0])
|
||||
}
|
||||
|
||||
func TestWriteMapsToCsv(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWriteMapsToCSV")
|
||||
|
||||
csvFilePath := "./testdata/test4.csv"
|
||||
records := []map[string]interface{}{
|
||||
{"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()
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
err = WriteStringToFile(filepath, "hello", false)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
content1, err := ReadFileToString(filepath)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
err = WriteStringToFile(filepath, " world", true)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
content2, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
assert.Equal("hello", content1)
|
||||
assert.Equal("hello world", string(content2))
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
3
fileutil/testdata/demo.csv
vendored
Normal file
3
fileutil/testdata/demo.csv
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Bob, 12, male
|
||||
Duke, 14, male
|
||||
Lucy, 16, female
|
||||
|
BIN
fileutil/testdata/file.go.zip
vendored
Normal file
BIN
fileutil/testdata/file.go.zip
vendored
Normal file
Binary file not shown.
1
fileutil/testdata/test.txt
vendored
Normal file
1
fileutil/testdata/test.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
this is a test file.
|
||||
3
fileutil/testdata/test1.csv
vendored
Normal file
3
fileutil/testdata/test1.csv
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Lili,22,female
|
||||
Jim,21,male
|
||||
|
||||
|
3
fileutil/testdata/test4.csv
vendored
Normal file
3
fileutil/testdata/test4.csv
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Name;Age;Gender
|
||||
Lili;22;female
|
||||
Jim;21;male
|
||||
|
@@ -4,14 +4,227 @@
|
||||
// Package formatter implements some functions to format string, struct.
|
||||
package formatter
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
// 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:]
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
// 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"
|
||||
func Comma(value interface{}, prefixSymbol string) string {
|
||||
numString := convertor.ToString(value)
|
||||
|
||||
_, err := strconv.ParseFloat(numString, 64)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return symbol + commaString(s)
|
||||
|
||||
isNegative := strings.HasPrefix(numString, "-")
|
||||
if isNegative {
|
||||
numString = numString[1:]
|
||||
}
|
||||
|
||||
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.
|
||||
func Pretty(v interface{}) (string, error) {
|
||||
out, err := json.MarshalIndent(v, "", " ")
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
// PrettyToWriter pretty encode data to writer.
|
||||
func PrettyToWriter(v interface{}, out io.Writer) error {
|
||||
enc := json.NewEncoder(out)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
p := 5
|
||||
if len(precision) > 0 {
|
||||
p = precision[0] + 1
|
||||
}
|
||||
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
|
||||
|
||||
return fmt.Sprintf("%.*g%s", p, size, 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.
|
||||
func BinaryBytes(size float64, precision ...int) string {
|
||||
p := 5
|
||||
if len(precision) > 0 {
|
||||
p = precision[0] + 1
|
||||
}
|
||||
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
|
||||
|
||||
return fmt.Sprintf("%.*g%s", p, size, 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]
|
||||
}
|
||||
|
||||
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
|
||||
// ParseDecimalBytes("42 MB") -> 42000000, nil
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,28 +1,164 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestComma(t *testing.T) {
|
||||
comma(t, "", "", "")
|
||||
comma(t, "aa", "", "")
|
||||
comma(t, "aa.a", "", "")
|
||||
comma(t, []int{1}, "", "")
|
||||
comma(t, "123", "", "123")
|
||||
comma(t, "12345", "", "12,345")
|
||||
comma(t, 12345, "", "12,345")
|
||||
comma(t, 12345, "$", "$12,345")
|
||||
comma(t, 12345, "¥", "¥12,345")
|
||||
comma(t, 12345.6789, "", "12,345.6789")
|
||||
assert := internal.NewAssert(t, "TestComma")
|
||||
|
||||
assert.Equal("", Comma("", ""))
|
||||
assert.Equal("", Comma("aa", ""))
|
||||
assert.Equal("", Comma("aa.a", ""))
|
||||
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 comma(t *testing.T, test interface{}, symbol string, expected interface{}) {
|
||||
res := Comma(test, symbol)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Comma", test, expected, res)
|
||||
t.FailNow()
|
||||
func TestPretty(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPretty")
|
||||
|
||||
cases := []interface{}{
|
||||
"",
|
||||
"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)
|
||||
|
||||
t.Log("result -> ", result)
|
||||
assert.Equal(expects[i], result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrettyToWriter(t *testing.T) {
|
||||
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())
|
||||
}
|
||||
|
||||
func TestDecimalBytes(t *testing.T) {
|
||||
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)))
|
||||
}
|
||||
|
||||
func TestBinaryBytes(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestParseDecimalBytes(t *testing.T) {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
@@ -2,20 +2,23 @@
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package function implements some functions for control the function execution and some is for functional programming.
|
||||
|
||||
package function
|
||||
|
||||
import (
|
||||
"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 {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
|
||||
return func(args ...interface{}) []reflect.Value {
|
||||
n--
|
||||
if n < 1 {
|
||||
return invokeFunc(fn, args...)
|
||||
return unsafeInvokeFunc(fn, args...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -23,11 +26,12 @@ 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 {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
var res []reflect.Value
|
||||
|
||||
return func(args ...interface{}) []reflect.Value {
|
||||
if n > 0 {
|
||||
res = invokeFunc(fn, args...)
|
||||
res = unsafeInvokeFunc(fn, args...)
|
||||
}
|
||||
if n <= 0 {
|
||||
fn = nil
|
||||
@@ -37,9 +41,10 @@ func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// Fn is for curry function which is func(...interface{}) interface{}
|
||||
type Fn func(...interface{}) interface{}
|
||||
|
||||
// Curry make a curryed function
|
||||
// Curry make a curry function
|
||||
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} {
|
||||
return func(values ...interface{}) interface{} {
|
||||
v := append([]interface{}{i}, values...)
|
||||
@@ -63,17 +68,111 @@ func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) in
|
||||
|
||||
// Delay make the function execution after delayed time
|
||||
func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
|
||||
time.Sleep(delay)
|
||||
invokeFunc(fn, args...)
|
||||
}
|
||||
|
||||
// Schedule invoke function every duration time, util close the returned bool chan
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
|
||||
quit := make(chan bool)
|
||||
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
||||
// Deprecated: Use Debounce function instead.
|
||||
func Debounced(fn func(), duration time.Duration) func() {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
|
||||
timer := time.NewTimer(duration)
|
||||
timer.Stop()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
invokeFunc(fn, args...)
|
||||
select {
|
||||
case <-timer.C:
|
||||
go fn()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return func() { timer.Reset(duration) }
|
||||
}
|
||||
|
||||
// Debounce creates a debounced version of the provided function.
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
|
||||
var (
|
||||
timer *time.Timer
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
debouncedFn = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
timer = time.AfterFunc(delay, func() {
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
cancelFn = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
return debouncedFn, cancelFn
|
||||
}
|
||||
|
||||
// Throttle creates a throttled version of the provided function.
|
||||
// The returned function guarantees that it will only be invoked at most once per interval.
|
||||
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 chan
|
||||
func Schedule(d time.Duration, fn interface{}, args ...interface{}) 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 <-quit:
|
||||
@@ -84,3 +183,15 @@ 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.
|
||||
func Pipeline(funcs ...func(interface{}) interface{}) func(interface{}) interface{} {
|
||||
return func(arg interface{}) (result interface{}) {
|
||||
result = arg
|
||||
for _, fn := range funcs {
|
||||
result = fn(result)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,15 @@ func invokeFunc(fn interface{}, args ...interface{}) []reflect.Value {
|
||||
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 {
|
||||
@@ -21,3 +30,10 @@ func functionValue(function interface{}) reflect.Value {
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
@@ -32,6 +34,8 @@ func TestAfter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBefore")
|
||||
|
||||
arr := []string{"a", "b", "c", "d", "e"}
|
||||
f := Before(3, func(i int) int {
|
||||
return i
|
||||
@@ -39,8 +43,7 @@ func TestBefore(t *testing.T) {
|
||||
|
||||
var res []int64
|
||||
type cb func(args ...interface{}) []reflect.Value
|
||||
appendStr := func(i int, s string, fn cb) {
|
||||
fmt.Printf("appendStr: arr[%d] is %s \n", i, s)
|
||||
appendStr := func(i int, _ string, fn cb) {
|
||||
v := fn(i)
|
||||
res = append(res, v[0].Int())
|
||||
}
|
||||
@@ -49,28 +52,26 @@ func TestBefore(t *testing.T) {
|
||||
appendStr(i, arr[i], f)
|
||||
}
|
||||
|
||||
expect := []int64{0, 1, 2, 2, 2}
|
||||
if !reflect.DeepEqual(expect, res) {
|
||||
t.FailNow()
|
||||
}
|
||||
expected := []int64{0, 1, 2, 2, 2}
|
||||
assert.Equal(expected, res)
|
||||
}
|
||||
|
||||
func TestCurry(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
add1 := addCurry.Curry(1)
|
||||
v := add1(2)
|
||||
if v != 3 {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(3, add1(2))
|
||||
}
|
||||
|
||||
func TestCompose(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCompose")
|
||||
|
||||
toUpper := func(a ...interface{}) interface{} {
|
||||
return strings.ToUpper(a[0].(string))
|
||||
}
|
||||
@@ -78,27 +79,47 @@ func TestCompose(t *testing.T) {
|
||||
return strings.ToLower(a[0].(string))
|
||||
}
|
||||
|
||||
expect := toUpper(toLower("aBCde"))
|
||||
expected := toUpper(toLower("aBCde"))
|
||||
cf := Compose(toUpper, toLower)
|
||||
res := cf("aBCde")
|
||||
|
||||
if res != expect {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(expected, res)
|
||||
}
|
||||
|
||||
func TestDelay(t *testing.T) {
|
||||
var print = func(s string) {
|
||||
fmt.Println(s)
|
||||
t.Log(s)
|
||||
}
|
||||
Delay(2*time.Second, print, "test delay")
|
||||
}
|
||||
|
||||
func TestDebounced(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDebounced")
|
||||
|
||||
count := 0
|
||||
add := func() {
|
||||
count++
|
||||
}
|
||||
|
||||
debouncedAdd := Debounced(add, 50*time.Microsecond)
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
debouncedAdd()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.Equal(1, count)
|
||||
|
||||
debouncedAdd()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.Equal(2, count)
|
||||
}
|
||||
|
||||
func TestSchedule(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSchedule")
|
||||
|
||||
var res []string
|
||||
appendStr := func(s string) {
|
||||
fmt.Println(s)
|
||||
res = append(res, s)
|
||||
}
|
||||
|
||||
@@ -106,9 +127,166 @@ func TestSchedule(t *testing.T) {
|
||||
time.Sleep(5 * time.Second)
|
||||
close(stop)
|
||||
|
||||
expect := []string{"*", "*", "*", "*", "*"}
|
||||
if !reflect.DeepEqual(expect, res) {
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println("done")
|
||||
expected := []string{"*", "*", "*", "*", "*"}
|
||||
assert.Equal(expected, res)
|
||||
}
|
||||
|
||||
func TestPipeline(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPipeline")
|
||||
|
||||
addOne := func(x interface{}) interface{} {
|
||||
return x.(int) + 1
|
||||
}
|
||||
double := func(x interface{}) interface{} {
|
||||
return 2 * x.(int)
|
||||
}
|
||||
square := func(x interface{}) interface{} {
|
||||
return x.(int) * x.(int)
|
||||
}
|
||||
|
||||
f := Pipeline(addOne, double, square)
|
||||
|
||||
assert.Equal(36, f(2))
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
37
function/watcher.go
Normal file
37
function/watcher.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package function
|
||||
|
||||
import "time"
|
||||
|
||||
// Watcher is used for record code excution time
|
||||
type Watcher struct {
|
||||
startTime int64
|
||||
stopTime int64
|
||||
excuting bool
|
||||
}
|
||||
|
||||
// Start the watch timer.
|
||||
func (w *Watcher) Start() {
|
||||
w.startTime = time.Now().UnixNano()
|
||||
w.excuting = true
|
||||
}
|
||||
|
||||
// Stop the watch timer.
|
||||
func (w *Watcher) Stop() {
|
||||
w.stopTime = time.Now().UnixNano()
|
||||
w.excuting = false
|
||||
}
|
||||
|
||||
// GetElapsedTime get excute elapsed time.
|
||||
func (w *Watcher) GetElapsedTime() time.Duration {
|
||||
if w.excuting {
|
||||
return time.Duration(time.Now().UnixNano() - w.startTime)
|
||||
}
|
||||
return time.Duration(w.stopTime - w.startTime)
|
||||
}
|
||||
|
||||
// Reset the watch timer.
|
||||
func (w *Watcher) Reset() {
|
||||
w.startTime = 0
|
||||
w.stopTime = 0
|
||||
w.excuting = false
|
||||
}
|
||||
37
function/watcher_test.go
Normal file
37
function/watcher_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestWatcher(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWatcher")
|
||||
|
||||
w := &Watcher{}
|
||||
w.Start()
|
||||
|
||||
longRunningTask()
|
||||
|
||||
assert.Equal(true, w.excuting)
|
||||
|
||||
w.Stop()
|
||||
|
||||
eapsedTime := w.GetElapsedTime().Milliseconds()
|
||||
t.Log("Elapsed Time (milsecond)", eapsedTime)
|
||||
|
||||
assert.Equal(false, w.excuting)
|
||||
|
||||
w.Reset()
|
||||
|
||||
assert.Equal(int64(0), w.startTime)
|
||||
assert.Equal(int64(0), w.stopTime)
|
||||
}
|
||||
|
||||
func longRunningTask() {
|
||||
var slice []int64
|
||||
for i := 0; i < 10000000; i++ {
|
||||
slice = append(slice, int64(i))
|
||||
}
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -1,3 +1,5 @@
|
||||
module github.com/duke-git/lancet
|
||||
|
||||
go 1.16
|
||||
|
||||
require golang.org/x/text v0.5.0
|
||||
|
||||
25
go.sum
Normal file
25
go.sum
Normal file
@@ -0,0 +1,25 @@
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
218
internal/assert.go
Normal file
218
internal/assert.go
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package internal is for internal use.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
compareNotEqual int = iota - 2
|
||||
compareLess
|
||||
compareEqual
|
||||
compareGreater
|
||||
)
|
||||
|
||||
// Assert is a simple implementation of assertion, only for internal usage
|
||||
type Assert struct {
|
||||
T *testing.T
|
||||
CaseName string
|
||||
}
|
||||
|
||||
// NewAssert return instance of Assert
|
||||
func NewAssert(t *testing.T, caseName string) *Assert {
|
||||
return &Assert{T: t, CaseName: caseName}
|
||||
}
|
||||
|
||||
// Equal check if expected is equal with actual
|
||||
func (a *Assert) Equal(expected, actual interface{}) {
|
||||
if compare(expected, actual) != compareEqual {
|
||||
makeTestFailed(a.T, a.CaseName, expected, 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 interface{}) {
|
||||
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)
|
||||
}
|
||||
|
||||
// NotEqual check if expected is not equal with actual
|
||||
func (a *Assert) NotEqual(expected, actual interface{}) {
|
||||
if compare(expected, actual) == compareEqual {
|
||||
expectedInfo := fmt.Sprintf("not %v", expected)
|
||||
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Greater check if expected is greate than actual
|
||||
func (a *Assert) Greater(expected, actual interface{}) {
|
||||
if compare(expected, actual) != compareGreater {
|
||||
expectedInfo := fmt.Sprintf("> %v", expected)
|
||||
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterOrEqual check if expected is greate than or equal with actual
|
||||
func (a *Assert) GreaterOrEqual(expected, actual interface{}) {
|
||||
isGreatOrEqual := compare(expected, actual) == compareGreater || compare(expected, actual) == compareEqual
|
||||
if !isGreatOrEqual {
|
||||
expectedInfo := fmt.Sprintf(">= %v", expected)
|
||||
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Less check if expected is less than actual
|
||||
func (a *Assert) Less(expected, actual interface{}) {
|
||||
if compare(expected, actual) != compareLess {
|
||||
expectedInfo := fmt.Sprintf("< %v", expected)
|
||||
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// LessOrEqual check if expected is less than or equal with actual
|
||||
func (a *Assert) LessOrEqual(expected, actual interface{}) {
|
||||
isLessOrEqual := compare(expected, actual) == compareLess || compare(expected, actual) == compareEqual
|
||||
if !isLessOrEqual {
|
||||
expectedInfo := fmt.Sprintf("<= %v", expected)
|
||||
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil check if value is nil
|
||||
func (a *Assert) IsNil(value interface{}) {
|
||||
if value != nil {
|
||||
makeTestFailed(a.T, a.CaseName, nil, value)
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotNil check if value is not nil
|
||||
func (a *Assert) IsNotNil(value interface{}) {
|
||||
if value == nil {
|
||||
makeTestFailed(a.T, a.CaseName, "not nil", value)
|
||||
}
|
||||
}
|
||||
|
||||
// compare x and y return :
|
||||
// x > y -> 1, x < y -> -1, x == y -> 0, x != y -> -2
|
||||
func compare(x, y interface{}) int {
|
||||
vx := reflect.ValueOf(x)
|
||||
vy := reflect.ValueOf(y)
|
||||
|
||||
if vx.Type() != vy.Type() {
|
||||
return compareNotEqual
|
||||
}
|
||||
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
xInt := vx.Int()
|
||||
yInt := vy.Int()
|
||||
if xInt > yInt {
|
||||
return compareGreater
|
||||
}
|
||||
if xInt == yInt {
|
||||
return compareEqual
|
||||
}
|
||||
if xInt < yInt {
|
||||
return compareLess
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
xUint := vx.Uint()
|
||||
yUint := vy.Uint()
|
||||
if xUint > yUint {
|
||||
return compareGreater
|
||||
}
|
||||
if xUint == yUint {
|
||||
return compareEqual
|
||||
}
|
||||
if xUint < yUint {
|
||||
return compareLess
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
xFloat := vx.Float()
|
||||
yFloat := vy.Float()
|
||||
if xFloat > yFloat {
|
||||
return compareGreater
|
||||
}
|
||||
if xFloat == yFloat {
|
||||
return compareEqual
|
||||
}
|
||||
if xFloat < yFloat {
|
||||
return compareLess
|
||||
}
|
||||
case reflect.String:
|
||||
xString := vx.String()
|
||||
yString := vy.String()
|
||||
if xString > yString {
|
||||
return compareGreater
|
||||
}
|
||||
if xString == yString {
|
||||
return compareEqual
|
||||
}
|
||||
if xString < yString {
|
||||
return compareLess
|
||||
}
|
||||
default:
|
||||
if reflect.DeepEqual(x, y) {
|
||||
return compareEqual
|
||||
}
|
||||
return compareNotEqual
|
||||
}
|
||||
|
||||
return compareNotEqual
|
||||
|
||||
}
|
||||
|
||||
// logFailedInfo make test failed and log error info
|
||||
func makeTestFailed(t *testing.T, caseName string, expected, actual interface{}) {
|
||||
_, 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)
|
||||
t.FailNow()
|
||||
}
|
||||
50
internal/assert_test.go
Normal file
50
internal/assert_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
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
|
||||
var uInt16 uint16
|
||||
var uInt32 uint32
|
||||
var uInt64 uint64
|
||||
assert.NotEqual(uInt1, uInt8)
|
||||
assert.NotEqual(uInt8, uInt16)
|
||||
assert.NotEqual(uInt16, uInt32)
|
||||
assert.NotEqual(uInt32, uInt64)
|
||||
|
||||
assert.Equal(uInt1, uInt2)
|
||||
|
||||
uInt1 = 1
|
||||
uInt2 = 2
|
||||
assert.Less(uInt1, uInt2)
|
||||
|
||||
assert.Greater(1, 0)
|
||||
assert.GreaterOrEqual(1, 1)
|
||||
assert.Less(0, 1)
|
||||
assert.LessOrEqual(0, 0)
|
||||
|
||||
assert.Equal(0.1, 0.1)
|
||||
assert.Greater(1.1, 0.1)
|
||||
assert.Less(0.1, 1.1)
|
||||
|
||||
assert.Equal("abc", "abc")
|
||||
assert.NotEqual("abc", "abd")
|
||||
assert.Less("abc", "abd")
|
||||
assert.Greater("abd", "abc")
|
||||
|
||||
assert.Equal([]int{1, 2, 3}, []int{1, 2, 3})
|
||||
assert.NotEqual([]int{1, 2, 3}, []int{1, 2})
|
||||
|
||||
assert.IsNil(nil)
|
||||
assert.IsNotNil("abc")
|
||||
|
||||
}
|
||||
224
mathutil/mathutil.go
Normal file
224
mathutil/mathutil.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package mathutil implements some functions for math calculation.
|
||||
package mathutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Exponent calculate x^n
|
||||
func Exponent(x, n int64) int64 {
|
||||
if n == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
t := Exponent(x, n/2)
|
||||
|
||||
if n%2 == 1 {
|
||||
return t * t * x
|
||||
}
|
||||
|
||||
return t * t
|
||||
}
|
||||
|
||||
// Fibonacci calculate fibonacci number before n
|
||||
func Fibonacci(first, second, n int) int {
|
||||
if n <= 0 {
|
||||
return 0
|
||||
}
|
||||
if n < 3 {
|
||||
return 1
|
||||
} else if n == 3 {
|
||||
return first + second
|
||||
} else {
|
||||
return Fibonacci(second, first+second, n-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Factorial calculate x!
|
||||
func Factorial(x uint) uint {
|
||||
var f uint = 1
|
||||
for ; x > 1; x-- {
|
||||
f *= x
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Percent calculate the percentage of val to total
|
||||
func Percent(val, total float64, n int) float64 {
|
||||
if total == 0 {
|
||||
return float64(0)
|
||||
}
|
||||
tmp := val / total * 100
|
||||
res := RoundToFloat(tmp, n)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// RoundToString round up to n decimal places
|
||||
func RoundToString(x float64, 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
|
||||
}
|
||||
|
||||
// RoundToFloat round up to n decimal places
|
||||
func RoundToFloat(x float64, n int) float64 {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= tmp
|
||||
x = math.Round(x)
|
||||
return x / tmp
|
||||
}
|
||||
|
||||
// TruncRound round off n decimal places
|
||||
func TruncRound(x float64, n int) float64 {
|
||||
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
|
||||
temp := strings.Split(floatStr, ".")
|
||||
var newFloat string
|
||||
if len(temp) < 2 || n >= len(temp[1]) {
|
||||
newFloat = floatStr
|
||||
} else {
|
||||
newFloat = temp[0] + "." + temp[1][:n]
|
||||
}
|
||||
res, _ := strconv.ParseFloat(newFloat, 64)
|
||||
return res
|
||||
}
|
||||
|
||||
// FloorToFloat round down to n decimal places.
|
||||
func FloorToFloat(x float64, n int) float64 {
|
||||
return math.Floor(x*math.Pow(10, float64(n))) / math.Pow(10.0, float64(n))
|
||||
}
|
||||
|
||||
// FloorToString round down to n decimal places.
|
||||
func FloorToString(x float64, n int) string {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= tmp
|
||||
x = math.Floor(x)
|
||||
res := strconv.FormatFloat(x/tmp, 'f', n, 64)
|
||||
return res
|
||||
}
|
||||
|
||||
// CeilToFloat round up to n decimal places.
|
||||
func CeilToFloat(x float64, n int) float64 {
|
||||
pow10n := math.Pow10(n)
|
||||
return math.Ceil(x*pow10n) / pow10n
|
||||
}
|
||||
|
||||
// CeilToString round up to n decimal places.
|
||||
func CeilToString(x float64, n int) string {
|
||||
multiplier := math.Pow(10, float64(n))
|
||||
rounded := math.Ceil(x*multiplier) / multiplier
|
||||
|
||||
return fmt.Sprintf("%.*f", n, rounded)
|
||||
}
|
||||
|
||||
// AngleToRadian converts angle value to radian value.
|
||||
func AngleToRadian(angle float64) float64 {
|
||||
radian := angle * (math.Pi / 180)
|
||||
return radian
|
||||
}
|
||||
|
||||
// RadianToAngle converts radian value to angle value.
|
||||
func RadianToAngle(radian float64) float64 {
|
||||
angle := radian * (180 / math.Pi)
|
||||
return angle
|
||||
}
|
||||
|
||||
// PointDistance get two points distance.
|
||||
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)
|
||||
}
|
||||
|
||||
// IsPrimes checks if number is prime number.
|
||||
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.
|
||||
func GCD(integers ...int) int {
|
||||
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(a, b int) int {
|
||||
if b == 0 {
|
||||
return a
|
||||
}
|
||||
|
||||
return gcd(b, a%b)
|
||||
}
|
||||
|
||||
// LCM return Least Common Multiple (LCM) of integers.
|
||||
func LCM(integers ...int) int {
|
||||
result := integers[0]
|
||||
|
||||
for k := range integers {
|
||||
result = lcm(integers[k], result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// find Least Common Multiple (LCM) via GCD.
|
||||
func lcm(a, b int) int {
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
// Cos returns the sine of the radian argument.
|
||||
func Sin(radian float64, precision ...int) float64 {
|
||||
return Cos((math.Pi / 2) - radian)
|
||||
}
|
||||
|
||||
// Log returns the logarithm of base n.
|
||||
func Log(n, base float64) float64 {
|
||||
return math.Log(n) / math.Log(base)
|
||||
}
|
||||
225
mathutil/mathutil_test.go
Normal file
225
mathutil/mathutil_test.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package mathutil
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestExponent(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestExponent")
|
||||
|
||||
assert.Equal(int64(1), Exponent(10, 0))
|
||||
assert.Equal(int64(10), Exponent(10, 1))
|
||||
assert.Equal(int64(100), Exponent(10, 2))
|
||||
}
|
||||
|
||||
func TestFibonacci(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFibonacci")
|
||||
|
||||
assert.Equal(0, Fibonacci(1, 1, 0))
|
||||
assert.Equal(1, Fibonacci(1, 1, 1))
|
||||
assert.Equal(1, Fibonacci(1, 1, 2))
|
||||
assert.Equal(5, Fibonacci(1, 1, 5))
|
||||
}
|
||||
|
||||
func TestFactorial(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFactorial")
|
||||
|
||||
assert.Equal(uint(1), Factorial(0))
|
||||
assert.Equal(uint(1), Factorial(1))
|
||||
assert.Equal(uint(2), Factorial(2))
|
||||
assert.Equal(uint(6), Factorial(3))
|
||||
}
|
||||
|
||||
func TestPercent(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPercent")
|
||||
|
||||
assert.Equal(float64(50), Percent(1, 2, 2))
|
||||
assert.Equal(float64(33.33), Percent(0.1, 0.3, 2))
|
||||
}
|
||||
|
||||
func TestRoundToFloat(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestRoundToString(t *testing.T) {
|
||||
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")
|
||||
}
|
||||
|
||||
func TestTruncRound(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestAngleToRadian(t *testing.T) {
|
||||
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) {
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestPointDistance")
|
||||
|
||||
result1 := PointDistance(1, 1, 4, 5)
|
||||
|
||||
assert.Equal(float64(5), result1)
|
||||
}
|
||||
|
||||
func TestIsPrime(t *testing.T) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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 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(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("0.15", CeilToString(float64(1)/float64(7), 2))
|
||||
}
|
||||
376
netutil/http.go
Normal file
376
netutil/http.go
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package netutil implements some basic functions to send http request and get ip info.
|
||||
// Note:
|
||||
// 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 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"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/slice"
|
||||
)
|
||||
|
||||
// HttpGet send get http request
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) {
|
||||
return doHttpRequest(http.MethodGet, url, params...)
|
||||
}
|
||||
|
||||
// HttpPost send post http request
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) {
|
||||
return doHttpRequest(http.MethodPost, url, params...)
|
||||
}
|
||||
|
||||
// HttpPut send put http request
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) {
|
||||
return doHttpRequest(http.MethodPut, url, params...)
|
||||
}
|
||||
|
||||
// HttpDelete send delete http request
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) {
|
||||
return doHttpRequest(http.MethodDelete, url, params...)
|
||||
}
|
||||
|
||||
// HttpPatch send patch http request
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
|
||||
return doHttpRequest(http.MethodPatch, url, params...)
|
||||
}
|
||||
|
||||
// ParseHttpResponse decode http response to specified interface
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error {
|
||||
if resp == nil {
|
||||
return errors.New("InvalidResp")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return json.NewDecoder(resp.Body).Decode(obj)
|
||||
}
|
||||
|
||||
// ConvertMapToQueryString convert map to sorted url query string
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string {
|
||||
if param == nil {
|
||||
return ""
|
||||
}
|
||||
var keys []string
|
||||
for key := range param {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var build strings.Builder
|
||||
for i, v := range keys {
|
||||
build.WriteString(v)
|
||||
build.WriteString("=")
|
||||
build.WriteString(fmt.Sprintf("%v", param[v]))
|
||||
if i != len(keys)-1 {
|
||||
build.WriteString("&")
|
||||
}
|
||||
}
|
||||
return build.String()
|
||||
}
|
||||
|
||||
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 {
|
||||
SSLEnabled bool
|
||||
TLSConfig *tls.Config
|
||||
Compressed bool
|
||||
HandshakeTimeout time.Duration
|
||||
ResponseTimeout time.Duration
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// defaultHttpClientConfig defalut client config
|
||||
var defaultHttpClientConfig = &HttpClientConfig{
|
||||
Compressed: false,
|
||||
HandshakeTimeout: 20 * time.Second,
|
||||
ResponseTimeout: 40 * time.Second,
|
||||
}
|
||||
|
||||
// HttpClient is used for sending http request
|
||||
type HttpClient struct {
|
||||
*http.Client
|
||||
TLS *tls.Config
|
||||
Request *http.Request
|
||||
Config HttpClientConfig
|
||||
}
|
||||
|
||||
// NewHttpClient make a HttpClient instance
|
||||
func NewHttpClient() *HttpClient {
|
||||
client := &HttpClient{
|
||||
Client: &http.Client{
|
||||
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
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// SendRequest send http request
|
||||
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 err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.setTLS(rawUrl)
|
||||
client.setHeader(req, request.Headers)
|
||||
client.setQueryParam(req, rawUrl, request.QueryParams)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// DecodeResponse decode response into target object
|
||||
func (client *HttpClient) DecodeResponse(resp *http.Response, target interface{}) 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 rquest 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
|
||||
}
|
||||
|
||||
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
|
||||
func StructToUrlValues(targetStruct interface{}) url.Values {
|
||||
rv := reflect.ValueOf(targetStruct)
|
||||
rt := reflect.TypeOf(targetStruct)
|
||||
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
}
|
||||
if rt.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", targetStruct))
|
||||
}
|
||||
|
||||
result := url.Values{}
|
||||
|
||||
fieldNum := rt.NumField()
|
||||
pattern := `^[A-Z]`
|
||||
regex := regexp.MustCompile(pattern)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
name := rt.Field(i).Name
|
||||
tag := rt.Field(i).Tag.Get("json")
|
||||
if regex.MatchString(name) && tag != "" {
|
||||
if strings.Contains(tag, "omitempty") {
|
||||
tag = strings.Split(tag, ",")[0]
|
||||
}
|
||||
result.Add(tag, fmt.Sprintf("%v", rv.Field(i).Interface()))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
360
netutil/http_test.go
Normal file
360
netutil/http_test.go
Normal file
@@ -0,0 +1,360 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestHttpGet(t *testing.T) {
|
||||
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
resp, err := HttpGet(url, header)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPost(t *testing.T) {
|
||||
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 := HttpPost(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPostFormData(t *testing.T) {
|
||||
apiUrl := "https://jsonplaceholder.typicode.com/todos"
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
// "Content-Type": "multipart/form-data",
|
||||
}
|
||||
|
||||
postData := url.Values{}
|
||||
postData.Add("userId", "1")
|
||||
postData.Add("title", "TestToDo")
|
||||
|
||||
// postData := make(map[string]string)
|
||||
// postData["userId"] = "1"
|
||||
// postData["title"] = "title"
|
||||
|
||||
resp, err := HttpPost(apiUrl, header, nil, postData)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPut(t *testing.T) {
|
||||
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 := HttpPut(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPatch(t *testing.T) {
|
||||
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 := HttpPatch(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpDelete(t *testing.T) {
|
||||
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||
resp, err := HttpDelete(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestConvertMapToQueryString(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestConvertMapToQueryString")
|
||||
|
||||
var m = map[string]interface{}{
|
||||
"c": 3,
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
assert.Equal("a=1&b=2&c=3", ConvertMapToQueryString(m))
|
||||
}
|
||||
|
||||
func TestParseResponse(t *testing.T) {
|
||||
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
resp, err := HttpGet(url, header)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
toDoResp := &Todo{}
|
||||
err = ParseHttpResponse(resp, toDoResp)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log("response: ", toDoResp)
|
||||
}
|
||||
|
||||
func TestHttpClient_Get(t *testing.T) {
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
var todo Todo
|
||||
httpClient.DecodeResponse(resp, &todo)
|
||||
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
t.Log("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestStructToUrlValues(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
||||
|
||||
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 := StructToUrlValues(item1)
|
||||
|
||||
assert.Equal("1", queryValues1.Get("id"))
|
||||
assert.Equal("123", queryValues1.Get("userId"))
|
||||
assert.Equal("test", queryValues1.Get("name"))
|
||||
assert.Equal("", queryValues1.Get("status"))
|
||||
|
||||
item2 := TodoQuery{
|
||||
Id: 2,
|
||||
UserId: 456,
|
||||
}
|
||||
queryValues2 := StructToUrlValues(item2)
|
||||
|
||||
assert.Equal("2", queryValues2.Get("id"))
|
||||
assert.Equal("456", queryValues2.Get("userId"))
|
||||
assert.Equal("", queryValues2.Get("name"))
|
||||
}
|
||||
|
||||
func handleFileRequest(t *testing.T, w 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)
|
||||
}
|
||||
}
|
||||
188
netutil/net.go
188
netutil/net.go
@@ -1,10 +1,22 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
// GetInternalIp return internal ipv4
|
||||
@@ -47,6 +59,66 @@ func GetPublicIpInfo() (*PublicIpInfo, error) {
|
||||
return &ip, nil
|
||||
}
|
||||
|
||||
// GetRequestPublicIp return the requested public ip
|
||||
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
|
||||
}
|
||||
|
||||
// GetIps return all ipv4 of system
|
||||
func GetIps() []string {
|
||||
var ips []string
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return ips
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ipNet, isValid := addr.(*net.IPNet)
|
||||
if isValid && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
ips = append(ips, ipNet.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
// GetMacAddrs get mac address
|
||||
func GetMacAddrs() []string {
|
||||
var macAddrs []string
|
||||
|
||||
nets, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return macAddrs
|
||||
}
|
||||
|
||||
for _, net := range nets {
|
||||
macAddr := net.HardwareAddr.String()
|
||||
if len(macAddr) == 0 {
|
||||
continue
|
||||
}
|
||||
macAddrs = append(macAddrs, macAddr)
|
||||
}
|
||||
|
||||
return macAddrs
|
||||
}
|
||||
|
||||
// PublicIpInfo public ip info: country, region, isp, city, lat, lon, ip
|
||||
type PublicIpInfo struct {
|
||||
Status string `json:"status"`
|
||||
@@ -82,3 +154,119 @@ func IsPublicIP(IP net.IP) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInternalIP verify an ip is intranet or not
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
207
netutil/net_internal.go
Normal file
207
netutil/net_internal.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func doHttpRequest(method, reqUrl string, params ...interface{}) (*http.Response, error) {
|
||||
if len(reqUrl) == 0 {
|
||||
return nil, errors.New("url should be specified")
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: method,
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
err := setUrl(req, reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch len(params) {
|
||||
case 1:
|
||||
err = setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 2:
|
||||
err := setHeaderAndQueryParam(req, reqUrl, params[0], params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 3:
|
||||
|
||||
err := setHeaderAndQueryAndBody(req, reqUrl, params[0], params[1], params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 4:
|
||||
err := setHeaderAndQueryAndBody(req, reqUrl, params[0], params[1], params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err = getClient(params[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp, e := client.Do(req)
|
||||
return resp, e
|
||||
}
|
||||
|
||||
func setHeaderAndQueryParam(req *http.Request, reqUrl string, header, queryParam interface{}) error {
|
||||
err := setHeader(req, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, queryParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setHeaderAndQueryAndBody(req *http.Request, reqUrl string, header, queryParam, body interface{}) error {
|
||||
if err := setHeader(req, header); err != nil {
|
||||
return err
|
||||
} else if err = setQueryParam(req, reqUrl, queryParam); err != nil {
|
||||
return err
|
||||
} else if err = setBodyByte(req, body); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setHeader(req *http.Request, header interface{}) 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")
|
||||
}
|
||||
|
||||
if host := req.Header.Get("Host"); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUrl(req *http.Request, reqUrl string) error {
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
return nil
|
||||
}
|
||||
|
||||
func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error {
|
||||
if queryParam == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var values url.Values
|
||||
|
||||
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{}")
|
||||
}
|
||||
|
||||
// set url
|
||||
if values != nil {
|
||||
if !strings.Contains(reqUrl, "?") {
|
||||
reqUrl = reqUrl + "?" + values.Encode()
|
||||
} else {
|
||||
reqUrl = reqUrl + "&" + values.Encode()
|
||||
}
|
||||
}
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBodyByte(req *http.Request, body interface{}) 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) {
|
||||
c := http.Client{}
|
||||
if client != nil {
|
||||
switch v := client.(type) {
|
||||
case http.Client:
|
||||
c = v
|
||||
default:
|
||||
return nil, errors.New("client type should be http.Client")
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
@@ -1,31 +1,32 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestGetInternalIp(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestGetInternalIp")
|
||||
|
||||
internalIp := GetInternalIp()
|
||||
ip := net.ParseIP(internalIp)
|
||||
if ip == nil {
|
||||
utils.LogFailedTestInfo(t, "GetInternalIp", "GetInternalIp", "", ip)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.IsNotNil(ip)
|
||||
}
|
||||
|
||||
func TestGetPublicIpInfo(t *testing.T) {
|
||||
publicIpInfo, err := GetPublicIpInfo()
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Printf("public ip info is: %+v \n", *publicIpInfo)
|
||||
}
|
||||
// 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) {
|
||||
assert := internal.NewAssert(t, "TestIsPublicIP")
|
||||
|
||||
ips := []net.IP{
|
||||
net.ParseIP("127.0.0.1"),
|
||||
net.ParseIP("192.168.0.1"),
|
||||
@@ -37,11 +38,37 @@ func TestIsPublicIP(t *testing.T) {
|
||||
expected := []bool{false, false, false, false, true}
|
||||
|
||||
for i := 0; i < len(ips); i++ {
|
||||
res := IsPublicIP(ips[i])
|
||||
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsPublicIP", ips[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual := IsPublicIP(ips[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIps(t *testing.T) {
|
||||
ips := GetIps()
|
||||
t.Log(ips)
|
||||
}
|
||||
|
||||
func TestGetMacAddrs(t *testing.T) {
|
||||
macAddrs := GetMacAddrs()
|
||||
t.Log(macAddrs)
|
||||
}
|
||||
|
||||
func TestIsPingConnected(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsPingConnected")
|
||||
|
||||
result1 := IsPingConnected("www.baidu.com")
|
||||
assert.Equal(true, result1)
|
||||
|
||||
result2 := IsPingConnected("www.!@#&&&.com")
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
func TestTelnetConnected(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestTelnetConnected")
|
||||
|
||||
result1 := IsTelnetConnected("www.baidu.com", "80")
|
||||
assert.Equal(true, result1)
|
||||
|
||||
result2 := IsTelnetConnected("www.baidu.com", "123")
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package netutil implements some basic functions to send http request and get ip info.
|
||||
// Note:
|
||||
// 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[2] is post body which type should be []byte.
|
||||
// params[3] is http client which type should be http.Client.
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//HttpGet send get http request
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodGet, url, params...)
|
||||
}
|
||||
|
||||
//HttpPost send post http request
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPost, url, params...)
|
||||
}
|
||||
|
||||
//HttpPut send put http request
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPut, url, params...)
|
||||
}
|
||||
|
||||
//HttpDelete send delete http request
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodDelete, url, params...)
|
||||
}
|
||||
|
||||
// HttpPatch send patch http request
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPatch, url, params...)
|
||||
}
|
||||
|
||||
// ParseHttpResponse decode http response to specified interface
|
||||
func ParseHttpResponse(resp *http.Response, obj interface{}) error {
|
||||
if resp == nil {
|
||||
return errors.New("InvalidResp")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return json.NewDecoder(resp.Body).Decode(obj)
|
||||
}
|
||||
|
||||
// ConvertMapToQueryString convert map to sorted url query string
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string {
|
||||
if param == nil {
|
||||
return ""
|
||||
}
|
||||
var keys []string
|
||||
for key := range param {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var build strings.Builder
|
||||
for i, v := range keys {
|
||||
build.WriteString(v)
|
||||
build.WriteString("=")
|
||||
build.WriteString(fmt.Sprintf("%v", param[v]))
|
||||
if i != len(keys)-1 {
|
||||
build.WriteString("&")
|
||||
}
|
||||
}
|
||||
return build.String()
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestHttpGet(t *testing.T) {
|
||||
_, e := HttpGet("", nil)
|
||||
if e == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
url := "https://gutendex.com/books?"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := HttpGet(url, nil, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
|
||||
}
|
||||
|
||||
func TestHttpPost(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users"
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
user := User{
|
||||
"test",
|
||||
"test@test.com",
|
||||
}
|
||||
bodyParams, _ := json.Marshal(user)
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
resp, err := HttpPost(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPut(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
user := User{
|
||||
"test",
|
||||
"test@test.com",
|
||||
}
|
||||
bodyParams, _ := json.Marshal(user)
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
resp, err := HttpPut(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpDelete(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
||||
resp, err := HttpDelete(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestConvertMapToQueryString(t *testing.T) {
|
||||
var m = map[string]interface{}{
|
||||
"c": 3,
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
|
||||
expected := "a=1&b=2&c=3"
|
||||
r := ConvertMapToQueryString(m)
|
||||
if r != expected {
|
||||
utils.LogFailedTestInfo(t, "ConvertMapToQueryString", m, expected, r)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users"
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
type UserResp struct {
|
||||
Data []User `json:"data"`
|
||||
}
|
||||
resp, err := HttpGet(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
userResp := &UserResp{}
|
||||
err = ParseHttpResponse(resp, userResp)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println(userResp.Data)
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func request(method, reqUrl string, params ...interface{}) (*http.Response, error) {
|
||||
if len(reqUrl) == 0 {
|
||||
return nil, errors.New("url should be specified")
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: method,
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
switch len(params) {
|
||||
case 0:
|
||||
err := setUrl(req, reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 1:
|
||||
err := setUrl(req, reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 2:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 3:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setBodyByte(req, params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 4:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setBodyByte(req, params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err = getClient(params[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resp, e := client.Do(req)
|
||||
return resp, e
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("header params type should be http.Header or map[string]string")
|
||||
}
|
||||
}
|
||||
|
||||
if host := req.Header.Get("Host"); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func setUrl(req *http.Request, reqUrl string) error {
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
return nil
|
||||
}
|
||||
func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error {
|
||||
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{}")
|
||||
}
|
||||
}
|
||||
|
||||
// set url
|
||||
if values != nil {
|
||||
if !strings.Contains(reqUrl, "?") {
|
||||
reqUrl = reqUrl + "?" + values.Encode()
|
||||
} else {
|
||||
reqUrl = reqUrl + "&" + values.Encode()
|
||||
}
|
||||
}
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBodyByte(req *http.Request, body interface{}) error {
|
||||
if body != nil {
|
||||
var bodyByte []byte
|
||||
if body != nil {
|
||||
switch v := body.(type) {
|
||||
case []byte:
|
||||
bodyByte = v
|
||||
default:
|
||||
return errors.New("body type should be []byte")
|
||||
}
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(bodyByte))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getClient(client interface{}) (*http.Client, error) {
|
||||
c := http.Client{}
|
||||
if client != nil {
|
||||
switch v := client.(type) {
|
||||
case http.Client:
|
||||
c = v
|
||||
default:
|
||||
return nil, errors.New("client type should be http.Client")
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
250
random/random.go
250
random/random.go
@@ -6,45 +6,265 @@ package random
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duke-git/lancet/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 = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
)
|
||||
|
||||
b := make([]byte, length)
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := range b {
|
||||
b[i] = letters[r.Int63()%int64(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
var rn = rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// RandInt generate random int between min and max, maybe min, not be max
|
||||
// RandBool generates a random boolean value (true or false).
|
||||
func RandBool() bool {
|
||||
return rand.Intn(2) == 1
|
||||
}
|
||||
|
||||
// RandBoolSlice generates a random boolean slice of specified length.
|
||||
func RandBoolSlice(length int) []bool {
|
||||
if length <= 0 {
|
||||
return []bool{}
|
||||
}
|
||||
|
||||
result := make([]bool, length)
|
||||
for i := range result {
|
||||
result[i] = RandBool()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RandInt generate random int between [min, max).
|
||||
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
|
||||
|
||||
return rand.Intn(max-min) + min
|
||||
}
|
||||
|
||||
// RandBytes generate random byte slice
|
||||
// RandIntSlice generates a slice of random integers.
|
||||
// The generated integers are between min and max (exclusive).
|
||||
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
|
||||
}
|
||||
|
||||
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
|
||||
func RandUniqueIntSlice(n, min, max int) []int {
|
||||
if min > max {
|
||||
return []int{}
|
||||
}
|
||||
if n > max-min {
|
||||
n = max - min
|
||||
}
|
||||
|
||||
nums := make([]int, n)
|
||||
used := make(map[int]struct{}, n)
|
||||
for i := 0; i < n; {
|
||||
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.
|
||||
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.RoundToFloat(n, precision)
|
||||
}
|
||||
|
||||
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
|
||||
func RandFloats(n int, min, max float64, precision int) []float64 {
|
||||
nums := make([]float64, n)
|
||||
used := make(map[float64]struct{}, n)
|
||||
for i := 0; i < n; {
|
||||
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 nil
|
||||
return []byte{}
|
||||
}
|
||||
b := make([]byte, length)
|
||||
|
||||
if _, err := io.ReadFull(crand.Reader, b); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// RandString generate random alpha string of specified length.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
// RandUpper generate a random upper case string of specified length.
|
||||
func RandUpper(length int) string {
|
||||
return random(UpperLetters, length)
|
||||
}
|
||||
|
||||
// RandLower generate a random lower case string of specified length.
|
||||
func RandLower(length int) string {
|
||||
return random(LowwerLetters, length)
|
||||
}
|
||||
|
||||
// RandNumeral generate a random numeral string of specified length.
|
||||
func RandNumeral(length int) string {
|
||||
return random(Numeral, length)
|
||||
}
|
||||
|
||||
// RandNumeralOrLetter generate a random numeral or alpha string of specified length.
|
||||
func RandNumeralOrLetter(length int) string {
|
||||
return random(Numeral+Letters, length)
|
||||
}
|
||||
|
||||
// RandSymbolChar generate a random symbol char of specified length.
|
||||
// symbol chars: !@#$%^&*()_+-=[]{}|;':\",./<>?.
|
||||
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 {
|
||||
// 仿照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<<letterIdBits - 1
|
||||
// 可用次数的最大值
|
||||
letterIdMax := 63 / letterIdBits
|
||||
// 循环生成随机字符串
|
||||
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 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.
|
||||
func UUIdV4() (string, error) {
|
||||
uuid := make([]byte, 16)
|
||||
|
||||
n, err := io.ReadFull(crand.Reader, uuid)
|
||||
if n != len(uuid) || err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
uuid[8] = uuid[8]&^0xc0 | 0x80
|
||||
uuid[6] = uuid[6]&^0xf0 | 0x40
|
||||
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
||||
}
|
||||
|
||||
@@ -1,71 +1,230 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version: v1.0.0
|
||||
* @Author: dudaodong@kingsoft.com
|
||||
* @Date: 2021-11-29 11:43:28
|
||||
* @LastEditors: dudaodong@kingsoft.com
|
||||
* @LastEditTime: 2021-12-01 18:05:29
|
||||
*/
|
||||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func TestRandString(t *testing.T) {
|
||||
randStr := RandString(6)
|
||||
fmt.Println(randStr)
|
||||
pattern := `^[a-zA-Z]+$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
|
||||
if len(randStr) != 6 || !reg.MatchString(randStr) {
|
||||
utils.LogFailedTestInfo(t, "RandString", "RandString(6)", "RandString(6) should be 6 letters ", randStr)
|
||||
t.FailNow()
|
||||
}
|
||||
randStr := RandString(6)
|
||||
|
||||
assert := internal.NewAssert(t, "TestRandString")
|
||||
assert.Equal(6, len(randStr))
|
||||
assert.Equal(true, reg.MatchString(randStr))
|
||||
}
|
||||
|
||||
func TestRandUpper(t *testing.T) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
pattern := `^[0-9a-zA-Z]+$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
|
||||
randStr := RandNumeralOrLetter(10)
|
||||
t.Log(randStr)
|
||||
|
||||
assert := internal.NewAssert(t, "TestRandNumeralOrLetter")
|
||||
assert.Equal(10, len(randStr))
|
||||
assert.Equal(true, reg.MatchString(randStr))
|
||||
}
|
||||
|
||||
func TestRandInt(t *testing.T) {
|
||||
res1 := RandInt(1, 10)
|
||||
if res1 < 1 || res1 >= 10 {
|
||||
utils.LogFailedTestInfo(t, "RandInt", "RandInt(1, 10)", "RandInt(1, 10) should between [1, 10) ", res1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestRandInt")
|
||||
|
||||
res2 := RandInt(1, 1)
|
||||
if res2 != 1 {
|
||||
utils.LogFailedTestInfo(t, "RandInt", "RandInt(1, 1)", "RandInt(1, 1) should be 1 ", res2)
|
||||
t.FailNow()
|
||||
}
|
||||
r1 := RandInt(1, 10)
|
||||
assert.GreaterOrEqual(r1, 1)
|
||||
assert.Less(r1, 10)
|
||||
|
||||
res3 := RandInt(10, 1)
|
||||
if res3 < 1 || res3 >= 10 {
|
||||
utils.LogFailedTestInfo(t, "RandInt", "RandInt(10, 1)", "RandInt(10, 1) should between [1, 10) ", res3)
|
||||
t.FailNow()
|
||||
}
|
||||
r2 := RandInt(1, 1)
|
||||
assert.Equal(1, r2)
|
||||
|
||||
r3 := RandInt(10, 1)
|
||||
assert.GreaterOrEqual(r1, 1)
|
||||
assert.Less(r3, 10)
|
||||
}
|
||||
|
||||
func TestRandBytes(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRandBytes")
|
||||
|
||||
randBytes := RandBytes(4)
|
||||
if len(randBytes) != 4 {
|
||||
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(4, len(randBytes))
|
||||
|
||||
v := reflect.ValueOf(randBytes)
|
||||
et := v.Type().Elem()
|
||||
if v.Kind() != reflect.Slice || et.Kind() != reflect.Uint8 {
|
||||
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
randErr := RandBytes(0)
|
||||
if randErr != nil {
|
||||
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(0)", "RandBytes(0) should return nil", randErr)
|
||||
t.FailNow()
|
||||
}
|
||||
elemType := v.Type().Elem()
|
||||
assert.Equal(reflect.Slice, v.Kind())
|
||||
assert.Equal(reflect.Uint8, elemType.Kind())
|
||||
|
||||
assert.Equal([]byte{}, RandBytes(0))
|
||||
}
|
||||
|
||||
func TestUUIdV4(t *testing.T) {
|
||||
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) {
|
||||
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 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 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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
||||
91
retry/retry.go
Normal file
91
retry/retry.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package retry is for executing a function repeatedly until it was successful or canceled by the context.
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultRetryTimes times of retry
|
||||
DefaultRetryTimes = 5
|
||||
// DefaultRetryDuration time duration of two retries
|
||||
DefaultRetryDuration = time.Second * 3
|
||||
)
|
||||
|
||||
// RetryConfig is config for retry
|
||||
type RetryConfig struct {
|
||||
context context.Context
|
||||
retryTimes uint
|
||||
retryDuration time.Duration
|
||||
}
|
||||
|
||||
// RetryFunc is function that retry executes
|
||||
type RetryFunc func() error
|
||||
|
||||
// Option is for adding retry config
|
||||
type Option func(*RetryConfig)
|
||||
|
||||
// RetryTimes set times of retry
|
||||
func RetryTimes(n uint) Option {
|
||||
return func(rc *RetryConfig) {
|
||||
rc.retryTimes = n
|
||||
}
|
||||
}
|
||||
|
||||
// RetryDuration set duration of retries
|
||||
func RetryDuration(d time.Duration) Option {
|
||||
return func(rc *RetryConfig) {
|
||||
rc.retryDuration = d
|
||||
}
|
||||
}
|
||||
|
||||
// Context set retry context config
|
||||
func Context(ctx context.Context) Option {
|
||||
return func(rc *RetryConfig) {
|
||||
rc.context = ctx
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
func Retry(retryFunc RetryFunc, opts ...Option) error {
|
||||
config := &RetryConfig{
|
||||
retryTimes: DefaultRetryTimes,
|
||||
retryDuration: DefaultRetryDuration,
|
||||
context: context.TODO(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(config)
|
||||
}
|
||||
|
||||
var i uint
|
||||
for i < config.retryTimes {
|
||||
err := retryFunc()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-time.After(config.retryDuration):
|
||||
case <-config.context.Done():
|
||||
return errors.New("retry is cancelled")
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
funcPath := runtime.FuncForPC(reflect.ValueOf(retryFunc).Pointer()).Name()
|
||||
lastSlash := strings.LastIndex(funcPath, "/")
|
||||
funcName := funcPath[lastSlash+1:]
|
||||
|
||||
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
|
||||
}
|
||||
80
retry/retry_test.go
Normal file
80
retry/retry_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestRetryFailed(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRetryFailed")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
||||
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetrySucceeded(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRetrySucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == DefaultRetryTimes {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestSetRetryTimes(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSetRetryTimes")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50), RetryTimes(3))
|
||||
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal(3, number)
|
||||
}
|
||||
|
||||
func TestCancelRetry(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCancelRetry")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number > 3 {
|
||||
cancel()
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber,
|
||||
RetryDuration(time.Microsecond*50),
|
||||
Context(ctx),
|
||||
)
|
||||
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal(4, number)
|
||||
}
|
||||
659
slice/slice.go
659
slice/slice.go
@@ -7,16 +7,18 @@ package slice
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Contain check if the value is in the slice or not
|
||||
func Contain(slice interface{}, value interface{}) bool {
|
||||
v := reflect.ValueOf(slice)
|
||||
switch reflect.TypeOf(slice).Kind() {
|
||||
// Contain check if the value is in the iterable type or not
|
||||
func Contain(iterableType interface{}, value interface{}) bool {
|
||||
v := reflect.ValueOf(iterableType)
|
||||
|
||||
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 {
|
||||
@@ -29,14 +31,44 @@ func Contain(slice interface{}, value interface{}) bool {
|
||||
return true
|
||||
}
|
||||
case reflect.String:
|
||||
s := fmt.Sprintf("%v", slice)
|
||||
ss := fmt.Sprintf("%v", value)
|
||||
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() {
|
||||
return false
|
||||
}
|
||||
|
||||
unique := make(map[interface{}]bool)
|
||||
for i := 0; i < super.Len(); i++ {
|
||||
v := super.Index(i).Interface()
|
||||
unique[v] = true
|
||||
}
|
||||
for i := 0; i < sub.Len(); i++ {
|
||||
v := sub.Index(i).Interface()
|
||||
if !unique[v] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
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{}
|
||||
@@ -70,56 +102,202 @@ func Chunk(slice []interface{}, size int) [][]interface{} {
|
||||
return res
|
||||
}
|
||||
|
||||
// Difference creates an slice of whose element not included in the other given slice
|
||||
func Difference(slice1, slice2 interface{}) interface{} {
|
||||
v := sliceValue(slice1)
|
||||
// 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)
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vi := v.Index(i).Interface()
|
||||
if !Contain(slice2, vi) {
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
item := sv.Index(i).Interface()
|
||||
if item != nil && item != false && item != "" && item != 0 {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(sv.Index(indexes[i]))
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Difference creates an slice of whose element in slice1, not in slice2
|
||||
func Difference(slice1, slice2 interface{}) interface{} {
|
||||
sv := sliceValue(slice1)
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
item := sv.Index(i).Interface()
|
||||
if !Contain(slice2, item) {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(v.Type(), len(indexes), len(indexes))
|
||||
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(v.Index(indexes[i]))
|
||||
res.Index(i).Set(sv.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// 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 iteratee function signature should be func(index int, value interface{}) interface{}.
|
||||
func DifferenceBy(slice interface{}, comparedSlice interface{}, iteratee interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
smv := sliceValue(comparedSlice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
||||
panic("iteratee function signature should be func(" + elemType.String() + ")" + elemType.String())
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
sliceAfterMap := slice1.Interface()
|
||||
comparedSliceAfterMap := slice2.Interface()
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Equal checks if two slices are equal: the same length and all elements' order and value are equal
|
||||
func Equal(slice1, slice2 interface{}) bool {
|
||||
sv1 := sliceValue(slice1)
|
||||
sv2 := sliceValue(slice2)
|
||||
|
||||
if sv1.Type().Elem() != sv2.Type().Elem() {
|
||||
return false
|
||||
}
|
||||
|
||||
if sv1.Len() != sv2.Len() {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < sv1.Len(); i++ {
|
||||
if sv1.Index(i).Interface() != sv2.Index(i).Interface() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualWith checks if two slices are equal with comparator func
|
||||
func EqualWith(slice1, slice2 interface{}, comparator interface{}) bool {
|
||||
sv1 := sliceValue(slice1)
|
||||
sv2 := sliceValue(slice2)
|
||||
|
||||
fn := functionValue(comparator)
|
||||
// elemType1 := sv1.Type().Elem()
|
||||
// elemType2 := sv2.Type().Elem()
|
||||
// todo: check fn signature: func(a elemType1.Kind(), b elemType2.Kind()) bool
|
||||
|
||||
for i := 0; i < sv1.Len(); i++ {
|
||||
flag := fn.Call([]reflect.Value{sv1.Index(i), sv2.Index(i)})[0]
|
||||
if !flag.Bool() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 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 .
|
||||
// The iteratee 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())
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
var indexes []int
|
||||
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() {
|
||||
indexes = append(indexes, i)
|
||||
currentLength++
|
||||
}
|
||||
}
|
||||
|
||||
return len(indexes) == sv.Len()
|
||||
return currentLength == sv.Len()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// None return true if all the values in the slice mismatch the criteria
|
||||
// The iteratee function signature should be func(index int, value interface{}) bool .
|
||||
func None(slice, iteratee interface{}) bool {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
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())
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
}
|
||||
|
||||
return currentLength == sv.Len()
|
||||
}
|
||||
|
||||
// Some return true if any of the values in the list pass the predicate function.
|
||||
// The iteratee function signature should be func(index int, value interface{}) bool .
|
||||
func Some(slice, iteratee interface{}) bool {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
has := false
|
||||
@@ -134,43 +312,87 @@ func Some(slice, function interface{}) bool {
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
// The iteratee function signature should be func(index int, value interface{}) bool .
|
||||
func Filter(slice, iteratee interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
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())
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
var indexes []int
|
||||
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() {
|
||||
indexes = append(indexes, i)
|
||||
res = reflect.Append(res, sv.Index(i))
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(sv.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
// Count iterates over elements of slice, returns a count of all matched elements
|
||||
// The iteratee function signature should be func(index int, value interface{}) bool .
|
||||
func Count(slice, iteratee interface{}) int {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
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())
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
var index int
|
||||
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 counter
|
||||
}
|
||||
|
||||
// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices
|
||||
// The iteratee function signature should be func(index int, value interface{}) bool .
|
||||
func GroupBy(slice, iteratee interface{}) (interface{}, interface{}) {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
groupB := reflect.MakeSlice(sv.Type(), 0, 0)
|
||||
groupA := 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() {
|
||||
groupA = reflect.Append(groupA, sv.Index(i))
|
||||
} else {
|
||||
groupB = reflect.Append(groupB, sv.Index(i))
|
||||
}
|
||||
}
|
||||
|
||||
return groupA.Interface(), groupB.Interface()
|
||||
}
|
||||
|
||||
// Find iterates over elements of slice, returning the first one that passes a truth test on function.
|
||||
// The predicate function signature should be func(index int, value interface{}) bool .
|
||||
func Find(slice, predicate interface{}) (interface{}, bool) {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(predicate)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("predicate function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
index := -1
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
||||
if flag.Bool() {
|
||||
@@ -178,18 +400,92 @@ func Find(slice, function interface{}) interface{} {
|
||||
break
|
||||
}
|
||||
}
|
||||
return sv.Index(index).Interface()
|
||||
|
||||
if index == -1 {
|
||||
var none interface{}
|
||||
return none, false
|
||||
}
|
||||
|
||||
return sv.Index(index).Interface(), true
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
// 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, predicate interface{}) (interface{}, bool) {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
fn := functionValue(predicate)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("predicate function signature should be func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
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() {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if index == -1 {
|
||||
var none interface{}
|
||||
return none, false
|
||||
}
|
||||
|
||||
return sv.Index(index).Interface(), true
|
||||
}
|
||||
|
||||
// FlattenDeep flattens slice recursive
|
||||
func FlattenDeep(slice interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
st := sliceElemType(sv.Type())
|
||||
tmp := reflect.MakeSlice(reflect.SliceOf(st), 0, 0)
|
||||
res := flattenRecursive(sv, tmp)
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
item := value.Index(i)
|
||||
kind := item.Kind()
|
||||
|
||||
if kind == reflect.Slice {
|
||||
result = flattenRecursive(item, result)
|
||||
} else {
|
||||
result = reflect.Append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ForEach iterates over elements of slice and invokes function for each element
|
||||
// The iteratee function signature should be func(index int, value interface{}).
|
||||
func ForEach(slice, iteratee interface{}) {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
||||
panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String())
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + elemType.String())
|
||||
}
|
||||
|
||||
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 `function`.
|
||||
// The iteratee function signature should be func(index int, value interface{}) interface{}.
|
||||
func Map(slice, iteratee interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
||||
panic("iteratee function signature should be func(int, " + elemType.String() + ")" + elemType.String())
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||
@@ -252,52 +548,34 @@ func InterfaceSlice(slice interface{}) []interface{} {
|
||||
|
||||
// StringSlice convert param to slice of string.
|
||||
func StringSlice(slice interface{}) []string {
|
||||
var res []string
|
||||
|
||||
v := sliceValue(slice)
|
||||
|
||||
out := make([]string, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
res = append(res, fmt.Sprint(v.Index(i)))
|
||||
v, ok := v.Index(i).Interface().(string)
|
||||
if !ok {
|
||||
panic("invalid element type")
|
||||
}
|
||||
out[i] = v
|
||||
}
|
||||
|
||||
return res
|
||||
return out
|
||||
}
|
||||
|
||||
// IntSlice convert param to slice of int.
|
||||
func IntSlice(slice interface{}) ([]int, error) {
|
||||
var res []int
|
||||
|
||||
func IntSlice(slice interface{}) []int {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
out := make([]int, sv.Len())
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
res = append(res, v)
|
||||
default:
|
||||
return nil, errors.New("InvalidSliceElementType")
|
||||
v, ok := sv.Index(i).Interface().(int)
|
||||
if !ok {
|
||||
panic("invalid element type")
|
||||
}
|
||||
out[i] = v
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ConvertSlice convert original slice to new data type element of slice.
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} {
|
||||
sv := sliceValue(originalSlice)
|
||||
if newSliceType.Kind() != reflect.Slice {
|
||||
panic(fmt.Sprintf("Invalid newSliceType(non-slice type of type %T)", newSliceType))
|
||||
}
|
||||
|
||||
newSlice := reflect.New(newSliceType)
|
||||
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(newSlice.Pointer()))
|
||||
|
||||
var newElemSize = int(sv.Type().Elem().Size()) / int(newSliceType.Elem().Size())
|
||||
|
||||
hdr.Cap = sv.Cap() * newElemSize
|
||||
hdr.Len = sv.Len() * newElemSize
|
||||
hdr.Data = sv.Pointer()
|
||||
|
||||
return newSlice.Elem().Interface()
|
||||
return out
|
||||
}
|
||||
|
||||
// DeleteByIndex delete the element of slice from start index to end index - 1.
|
||||
@@ -322,6 +600,37 @@ func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
if n == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
svLen := sv.Len()
|
||||
|
||||
if math.Abs(float64(n)) >= float64(svLen) {
|
||||
return reflect.MakeSlice(sv.Type(), 0, 0).Interface()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), svLen+n, svLen+n)
|
||||
for i := 0; i < res.Len(); i++ {
|
||||
res.Index(i).Set(sv.Index(i))
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// 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:]...)...)
|
||||
@@ -332,7 +641,6 @@ func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
|
||||
// value is slice
|
||||
vv := reflect.ValueOf(value)
|
||||
if vv.Kind() == reflect.Slice {
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value).Elem() {
|
||||
@@ -342,7 +650,6 @@ func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// value is not slice
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
@@ -363,6 +670,7 @@ func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}
|
||||
if index < 0 || index >= v.Len() {
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
@@ -380,7 +688,7 @@ func Unique(slice interface{}) interface{} {
|
||||
}
|
||||
|
||||
var temp []interface{}
|
||||
len := 0
|
||||
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
skip := true
|
||||
@@ -392,29 +700,35 @@ func Unique(slice interface{}) interface{} {
|
||||
}
|
||||
if skip {
|
||||
temp = append(temp, v)
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), len, len)
|
||||
for i := 0; i < len; i++ {
|
||||
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 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
|
||||
// UniqueBy call iteratee func with every item of slice, then remove duplicated.
|
||||
// The iteratee function signature should be func(value interface{}) interface{}.
|
||||
func UniqueBy(slice, iteratee interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(iteratee)
|
||||
|
||||
if sv.Len() == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
val := fn.Call([]reflect.Value{sv.Index(i)})[0]
|
||||
res.Index(i).Set(val)
|
||||
}
|
||||
|
||||
return Unique(res.Interface())
|
||||
}
|
||||
|
||||
// Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
|
||||
@@ -423,21 +737,21 @@ func Union(slices ...interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
// append all slices, then unique it
|
||||
var allSlice []interface{}
|
||||
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()
|
||||
allSlice = append(allSlice, v)
|
||||
allSlices = append(allSlices, v)
|
||||
}
|
||||
}
|
||||
|
||||
sv := sliceValue(slices[0])
|
||||
res := reflect.MakeSlice(sv.Type(), len, len)
|
||||
for i := 0; i < len; i++ {
|
||||
res.Index(i).Set(reflect.ValueOf(allSlice[i]))
|
||||
res.Index(i).Set(reflect.ValueOf(allSlices[i]))
|
||||
}
|
||||
|
||||
return Unique(res.Interface())
|
||||
@@ -474,24 +788,37 @@ func Intersection(slices ...interface{}) interface{} {
|
||||
}
|
||||
|
||||
res := Reduce(slices, reduceFunc, nil)
|
||||
return Union(res)
|
||||
return Unique(res)
|
||||
}
|
||||
|
||||
// ReverseSlice return slice of element order is reversed to the given slice
|
||||
func ReverseSlice(slice interface{}) {
|
||||
v := sliceValue(slice)
|
||||
swp := reflect.Swapper(v.Interface())
|
||||
for i, j := 0, v.Len()-1; i < j; i, j = i+1, j-1 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle creates an slice of shuffled values
|
||||
func Shuffle(slice interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
length := sv.Len()
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), length, length)
|
||||
for i, v := range rand.Perm(length) {
|
||||
res.Index(i).Set(sv.Index(v))
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// SortByField return sorted slice by field
|
||||
// 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 {
|
||||
v := sliceValue(slice)
|
||||
t := v.Type().Elem()
|
||||
sv := sliceValue(slice)
|
||||
t := sv.Type().Elem()
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
@@ -507,37 +834,54 @@ func SortByField(slice interface{}, field string, sortType ...string) error {
|
||||
}
|
||||
|
||||
// Create a less function based on the field's kind.
|
||||
var less func(a, b reflect.Value) bool
|
||||
var compare func(a, b reflect.Value) bool
|
||||
switch sf.Type.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||
if len(sortType) > 0 && sortType[0] == "desc" {
|
||||
compare = func(a, b reflect.Value) bool { return a.Int() > b.Int() }
|
||||
} else {
|
||||
compare = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||
if len(sortType) > 0 && sortType[0] == "desc" {
|
||||
compare = func(a, b reflect.Value) bool { return a.Uint() > b.Uint() }
|
||||
} else {
|
||||
compare = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
less = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
|
||||
if len(sortType) > 0 && sortType[0] == "desc" {
|
||||
compare = func(a, b reflect.Value) bool { return a.Float() > b.Float() }
|
||||
} else {
|
||||
compare = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
|
||||
}
|
||||
case reflect.String:
|
||||
less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||
if len(sortType) > 0 && sortType[0] == "desc" {
|
||||
compare = func(a, b reflect.Value) bool { return a.String() > b.String() }
|
||||
} else {
|
||||
compare = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||
}
|
||||
case reflect.Bool:
|
||||
less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
|
||||
if len(sortType) > 0 && sortType[0] == "desc" {
|
||||
compare = func(a, b reflect.Value) bool { return a.Bool() && !b.Bool() }
|
||||
} else {
|
||||
compare = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("field type %s not supported", sf.Type)
|
||||
}
|
||||
|
||||
sort.Slice(slice, func(i, j int) bool {
|
||||
a := v.Index(i)
|
||||
b := v.Index(j)
|
||||
a := sv.Index(i)
|
||||
b := sv.Index(j)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
a = a.Elem()
|
||||
b = b.Elem()
|
||||
}
|
||||
a = a.FieldByIndex(sf.Index)
|
||||
b = b.FieldByIndex(sf.Index)
|
||||
return less(a, b)
|
||||
return compare(a, b)
|
||||
})
|
||||
|
||||
if sortType[0] == "desc" {
|
||||
ReverseSlice(slice)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -547,6 +891,7 @@ func Without(slice interface{}, values ...interface{}) interface{} {
|
||||
if sv.Len() == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
@@ -562,3 +907,77 @@ func Without(slice interface{}, values ...interface{}) interface{} {
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// IndexOf returns the index at which the first occurrence of a value is found in a slice or return -1
|
||||
// if the value cannot be found.
|
||||
func IndexOf(slice, value interface{}) int {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
if reflect.DeepEqual(sv.Index(i).Interface(), value) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexOf returns the index at which the last occurrence of a value is found in a slice or return -1
|
||||
// if the value cannot be found.
|
||||
func LastIndexOf(slice, value interface{}) int {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
for i := sv.Len() - 1; i > 0; i-- {
|
||||
if reflect.DeepEqual(sv.Index(i).Interface(), value) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// ToSlicePointer returns a pointer to the slices of a variable parameter transformation
|
||||
func ToSlicePointer(value ...interface{}) []*interface{} {
|
||||
out := make([]*interface{}, len(value))
|
||||
for i := range value {
|
||||
out[i] = &value[i]
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ToSlice returns a slices of a variable parameter transformation
|
||||
func ToSlice(value ...interface{}) interface{} {
|
||||
rv := reflect.ValueOf(value)
|
||||
out := reflect.MakeSlice(rv.Type(), len(value), len(value))
|
||||
for i := range value {
|
||||
out.Index(i).Set(reflect.ValueOf(value[i]))
|
||||
}
|
||||
return out.Interface()
|
||||
}
|
||||
|
||||
// AppendIfAbsent only absent append the value
|
||||
func AppendIfAbsent(slice interface{}, value interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
out := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
out.Index(i).Set(sv.Index(i))
|
||||
}
|
||||
if !Contain(slice, value) {
|
||||
out = reflect.Append(out, reflect.ValueOf(value))
|
||||
}
|
||||
return out.Interface()
|
||||
}
|
||||
|
||||
// AppendIfAbsent only absent append the value
|
||||
func Join(slice interface{}, separator string) string {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
strs := make([]string, sv.Len())
|
||||
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
strs[i] = fmt.Sprint(sv.Index(i).Interface())
|
||||
}
|
||||
|
||||
return strings.Join(strs, separator)
|
||||
}
|
||||
|
||||
@@ -52,3 +52,14 @@ func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bo
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// sliceElemType get slice element type
|
||||
func sliceElemType(reflectType reflect.Type) reflect.Type {
|
||||
for {
|
||||
if reflectType.Kind() != reflect.Slice {
|
||||
return reflectType
|
||||
}
|
||||
|
||||
reflectType = reflectType.Elem()
|
||||
}
|
||||
}
|
||||
@@ -4,96 +4,97 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestContain(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d"}
|
||||
contain(t, t1, "a", true)
|
||||
contain(t, t1, "e", false)
|
||||
assert := internal.NewAssert(t, "TestContain")
|
||||
|
||||
var t2 []string
|
||||
contain(t, t2, "1", false)
|
||||
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{}, ""))
|
||||
|
||||
m := make(map[string]int)
|
||||
m["a"] = 1
|
||||
contain(t, m, "a", true)
|
||||
contain(t, m, "b", false)
|
||||
var m = map[string]int{"a": 1}
|
||||
assert.Equal(true, Contain(m, "a"))
|
||||
assert.Equal(false, Contain(m, "b"))
|
||||
|
||||
s := "hello"
|
||||
contain(t, s, "h", true)
|
||||
contain(t, s, "s", false)
|
||||
assert.Equal(true, Contain("abc", "a"))
|
||||
assert.Equal(false, Contain("abc", "d"))
|
||||
}
|
||||
|
||||
func contain(t *testing.T, test interface{}, value interface{}, expected bool) {
|
||||
res := Contain(test, value)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Contain", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
func TestContainSubSlice(t *testing.T) {
|
||||
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"}))
|
||||
}
|
||||
|
||||
func TestChunk(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
assert := internal.NewAssert(t, "TestChunk")
|
||||
|
||||
r1 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 1, r1)
|
||||
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"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 2, r2)
|
||||
r2 := [][]interface{}{{"a", "b"}, {"c", "d"}, {"e"}}
|
||||
assert.Equal(r2, Chunk(InterfaceSlice(arr), 2))
|
||||
|
||||
r3 := [][]interface{}{
|
||||
{"a", "b", "c"},
|
||||
{"d", "e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 3, r3)
|
||||
r3 := [][]interface{}{{"a", "b", "c"}, {"d", "e"}}
|
||||
assert.Equal(r3, Chunk(InterfaceSlice(arr), 3))
|
||||
|
||||
r4 := [][]interface{}{
|
||||
{"a", "b", "c", "d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 4, r4)
|
||||
|
||||
r5 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 5, r5)
|
||||
r4 := [][]interface{}{{"a", "b", "c", "d"}, {"e"}}
|
||||
assert.Equal(r4, Chunk(InterfaceSlice(arr), 4))
|
||||
|
||||
r5 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
||||
assert.Equal(r5, Chunk(InterfaceSlice(arr), 5))
|
||||
}
|
||||
|
||||
func chunk(t *testing.T, test []interface{}, num int, expected [][]interface{}) {
|
||||
res := Chunk(test, num)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "Chunk", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
func TestCompact(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TesCompact")
|
||||
|
||||
assert.Equal([]int{}, Compact([]int{0}))
|
||||
assert.Equal([]int{1, 2, 3}, Compact([]int{0, 1, 2, 3}))
|
||||
assert.Equal([]string{}, Compact([]string{""}))
|
||||
assert.Equal([]string{" "}, Compact([]string{" "}))
|
||||
assert.Equal([]string{"a", "b", "0"}, Compact([]string{"", "a", "b", "0"}))
|
||||
assert.Equal([]bool{true, true}, Compact([]bool{false, true, true}))
|
||||
}
|
||||
|
||||
func TestConvertSlice(t *testing.T) {
|
||||
//t1 := []string{"1","2"}
|
||||
//aInt, _ := strconv.ParseInt("1", 10, 64)
|
||||
//bInt, _ := strconv.ParseInt("2", 10, 64)
|
||||
//expected :=[]int64{aInt, bInt}
|
||||
//
|
||||
//a := ConvertSlice(t1, reflect.TypeOf(expected))
|
||||
//if !reflect.DeepEqual(a, expected) {
|
||||
// utils.LogFailedTestInfo(t, "ConvertSlice", t1, expected, a)
|
||||
// t.FailNow()
|
||||
//}
|
||||
func TestConcat(t *testing.T) {
|
||||
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 TestEqual(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestEqualWith(t *testing.T) {
|
||||
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) {
|
||||
@@ -101,42 +102,44 @@ func TestEvery(t *testing.T) {
|
||||
isEven := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
res := Every(nums, isEven)
|
||||
if res != false {
|
||||
utils.LogFailedTestInfo(t, "Every", nums, false, res)
|
||||
t.FailNow()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEvery")
|
||||
assert.Equal(false, Every(nums, isEven))
|
||||
}
|
||||
|
||||
func TestNone(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 5}
|
||||
check := func(i, num int) bool {
|
||||
return num%2 == 1
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestNone")
|
||||
assert.Equal(false, None(nums, check))
|
||||
}
|
||||
|
||||
func TestSome(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 5}
|
||||
isEven := func(i, num int) bool {
|
||||
hasEven := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
res := Some(nums, isEven)
|
||||
if res != true {
|
||||
utils.LogFailedTestInfo(t, "Some", nums, true, res)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestSome")
|
||||
assert.Equal(true, Some(nums, hasEven))
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
even := func(i, num int) bool {
|
||||
isEven := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
e1 := []int{2, 4}
|
||||
r1 := Filter(nums, even)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Filter", nums, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
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},
|
||||
@@ -144,8 +147,7 @@ func TestFilter(t *testing.T) {
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
studentsOfAageGreat12 := []student{
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
@@ -153,12 +155,31 @@ func TestFilter(t *testing.T) {
|
||||
return s.age > 12
|
||||
}
|
||||
|
||||
r2 := Filter(students, filterFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc))
|
||||
}
|
||||
|
||||
func TestGroupBy(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
func TestCount(t *testing.T) {
|
||||
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))
|
||||
}
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
@@ -166,24 +187,71 @@ func TestFind(t *testing.T) {
|
||||
even := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
res := Find(nums, even)
|
||||
if res != 2 {
|
||||
utils.LogFailedTestInfo(t, "Find", nums, 2, res)
|
||||
t.FailNow()
|
||||
res, ok := Find(nums, even)
|
||||
if !ok {
|
||||
t.Fatal("found nothing")
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestFind")
|
||||
assert.Equal(2, res)
|
||||
}
|
||||
|
||||
func TestFindLast(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
even := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
res, ok := FindLast(nums, even)
|
||||
if !ok {
|
||||
t.Fatal("found nothing")
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestFindLast")
|
||||
assert.Equal(4, res)
|
||||
}
|
||||
|
||||
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 TestFlattenDeep(t *testing.T) {
|
||||
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) {
|
||||
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)
|
||||
})
|
||||
|
||||
assert := internal.NewAssert(t, "TestForEach")
|
||||
assert.Equal(expected, numbersAddTwo)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
nums := []int{1, 2, 3, 4}
|
||||
multiplyTwo := func(i, num int) int {
|
||||
return num * 2
|
||||
}
|
||||
e1 := []int{2, 4, 6, 8}
|
||||
r1 := Map(s1, multiplyTwo)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Map", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestMap")
|
||||
assert.Equal([]int{2, 4, 6, 8}, Map(nums, multiplyTwo))
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
@@ -194,8 +262,7 @@ func TestMap(t *testing.T) {
|
||||
{"b", 2},
|
||||
{"c", 3},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
studentsOfAdd10Aage := []student{
|
||||
{"a", 11},
|
||||
{"b", 12},
|
||||
{"c", 13},
|
||||
@@ -204,11 +271,8 @@ func TestMap(t *testing.T) {
|
||||
s.age += 10
|
||||
return s
|
||||
}
|
||||
r2 := Map(students, mapFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(studentsOfAdd10Aage, Map(students, mapFunc))
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
@@ -216,218 +280,163 @@ func TestReduce(t *testing.T) {
|
||||
{},
|
||||
{1},
|
||||
{1, 2, 3, 4}}
|
||||
|
||||
expected := []int{0, 1, 10}
|
||||
|
||||
f := func(i, v1, v2 int) int {
|
||||
return v1 + v2
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestReduce")
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := Reduce(cases[i], f, 0)
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "Reduce", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
actual := Reduce(cases[i], f, 0)
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, 1, 2, 3, 4, 5)
|
||||
expect := []int{1, 2, 3, 4, 5}
|
||||
intSlice(t, t1, expect)
|
||||
}
|
||||
var nums []interface{}
|
||||
nums = append(nums, 1, 2, 3)
|
||||
|
||||
func intSlice(t *testing.T, test interface{}, expected []int) {
|
||||
res, err := IntSlice(test)
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "IntSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestIntSlice")
|
||||
assert.Equal([]int{1, 2, 3}, IntSlice(nums))
|
||||
}
|
||||
|
||||
func TestStringSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, "a", "b", "c", "d", "e")
|
||||
expect := []string{"a", "b", "c", "d", "e"}
|
||||
stringSlice(t, t1, expect)
|
||||
}
|
||||
var strs []interface{}
|
||||
strs = append(strs, "a", "b", "c")
|
||||
|
||||
func stringSlice(t *testing.T, test interface{}, expected []string) {
|
||||
res := StringSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "StringSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestStringSlice")
|
||||
assert.Equal([]string{"a", "b", "c"}, StringSlice(strs))
|
||||
}
|
||||
|
||||
func TestInterfaceSlice(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
expect := []interface{}{"a", "b", "c", "d", "e"}
|
||||
interfaceSlice(t, t1, expect)
|
||||
}
|
||||
strs := []string{"a", "b", "c"}
|
||||
expect := []interface{}{"a", "b", "c"}
|
||||
|
||||
func interfaceSlice(t *testing.T, test interface{}, expected []interface{}) {
|
||||
res := InterfaceSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InterfaceSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestInterfaceSlice")
|
||||
assert.Equal(expect, InterfaceSlice(strs))
|
||||
}
|
||||
|
||||
func TestDeleteByIndex(t *testing.T) {
|
||||
origin := []string{"a", "b", "c", "d", "e"}
|
||||
assert := internal.NewAssert(t, "TestDeleteByIndex")
|
||||
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
r1 := []string{"b", "c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 0, r1)
|
||||
a1, _ := DeleteByIndex(t1, 0)
|
||||
assert.Equal(r1, a1)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
t2 := []string{"a", "b", "c", "d", "e"}
|
||||
r2 := []string{"a", "b", "c", "e"}
|
||||
deleteByIndex(t, origin, t1, 3, 0, r2)
|
||||
a2, _ := DeleteByIndex(t2, 3)
|
||||
assert.Equal(r2, a2)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r3 := []string{"a", "b", "c", "d"}
|
||||
deleteByIndex(t, origin, t1, 4, 0, r3)
|
||||
t3 := []string{"a", "b", "c", "d", "e"}
|
||||
r3 := []string{"c", "d", "e"}
|
||||
a3, _ := DeleteByIndex(t3, 0, 2)
|
||||
assert.Equal(r3, a3)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r4 := []string{"c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 2, r4)
|
||||
t4 := []string{"a", "b", "c", "d", "e"}
|
||||
r4 := []string{}
|
||||
a4, _ := DeleteByIndex(t4, 0, 5)
|
||||
assert.Equal(r4, a4)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r5 := []string{} // var r5 []string{} failed
|
||||
deleteByIndex(t, origin, t1, 0, 5, r5)
|
||||
t5 := []string{"a", "b", "c", "d", "e"}
|
||||
_, err := DeleteByIndex(t5, 1, 1)
|
||||
assert.IsNotNil(err)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r6 := []string{"a", "c", "d","e"}
|
||||
//deleteByIndex(t, origin, t1, 1, 1, r6)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r7 := []string{}
|
||||
//deleteByIndex(t, origin, t1, 0, 6, r7)
|
||||
_, err = DeleteByIndex(t5, 0, 6)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func deleteByIndex(t *testing.T, origin, test interface{}, start, end int, expected interface{}) {
|
||||
var res interface{}
|
||||
var err error
|
||||
if end != 0 {
|
||||
res, err = DeleteByIndex(test, start, end)
|
||||
} else {
|
||||
res, err = DeleteByIndex(test, start)
|
||||
}
|
||||
if err != nil {
|
||||
t.Error("DeleteByIndex Error: " + err.Error())
|
||||
}
|
||||
func TestDrop(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestInterfaceSlice")
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "DeleteByIndex", origin, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal([]int{}, Drop([]int{}, 0))
|
||||
assert.Equal([]int{}, Drop([]int{}, 1))
|
||||
assert.Equal([]int{}, Drop([]int{}, -1))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
func TestInsertByIndex(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestInsertByIndex")
|
||||
|
||||
t1 := []string{"a", "b", "c"}
|
||||
r1, _ := InsertByIndex(t1, 0, "1")
|
||||
assert.Equal([]string{"1", "a", "b", "c"}, r1)
|
||||
|
||||
r1 := []string{"1", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, "1", r1)
|
||||
r2, _ := InsertByIndex(t1, 1, "1")
|
||||
assert.Equal([]string{"a", "1", "b", "c"}, r2)
|
||||
|
||||
r2 := []string{"a", "1", "b", "c"}
|
||||
insertByIndex(t, t1, 1, "1", r2)
|
||||
r3, _ := InsertByIndex(t1, 3, "1")
|
||||
assert.Equal([]string{"a", "b", "c", "1"}, r3)
|
||||
|
||||
r3 := []string{"a", "b", "c", "1"}
|
||||
insertByIndex(t, t1, 3, "1", r3)
|
||||
r4, _ := InsertByIndex(t1, 0, []string{"1", "2", "3"})
|
||||
assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, r4)
|
||||
|
||||
r4 := []string{"1", "2", "3", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, []string{"1", "2", "3"}, r4)
|
||||
r5, _ := InsertByIndex(t1, 3, []string{"1", "2", "3"})
|
||||
assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, r5)
|
||||
|
||||
r5 := []string{"a", "1", "2", "3", "b", "c"}
|
||||
insertByIndex(t, t1, 1, []string{"1", "2", "3"}, r5)
|
||||
_, err := InsertByIndex(t1, 4, "1")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
r6 := []string{"a", "b", "1", "2", "3", "c"}
|
||||
insertByIndex(t, t1, 2, []string{"1", "2", "3"}, r6)
|
||||
|
||||
r7 := []string{"a", "b", "c", "1", "2", "3"}
|
||||
insertByIndex(t, t1, 3, []string{"1", "2", "3"}, r7)
|
||||
}
|
||||
|
||||
func insertByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := InsertByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("InsertByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InsertByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
_, err = InsertByIndex(t1, 0, 1)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestUpdateByIndex(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUpdateByIndex")
|
||||
|
||||
t1 := []string{"a", "b", "c"}
|
||||
r1 := []string{"1", "b", "c"}
|
||||
updateByIndex(t, t1, 0, "1", r1)
|
||||
r1, _ := UpdateByIndex(t1, 0, "1")
|
||||
assert.Equal([]string{"1", "b", "c"}, r1)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r2 := []string{"a", "1", "c"}
|
||||
updateByIndex(t, t1, 1, "1", r2)
|
||||
t2 := []string{"a", "b", "c"}
|
||||
r2, _ := UpdateByIndex(t2, 1, "1")
|
||||
assert.Equal([]string{"a", "1", "c"}, r2)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r3 := []string{"a", "b", "1"}
|
||||
updateByIndex(t, t1, 2, "1", r3)
|
||||
_, err := UpdateByIndex([]string{"a", "b", "c"}, 4, "1")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
}
|
||||
|
||||
func updateByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := UpdateByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("UpdateByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "UpdateByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
_, err = UpdateByIndex([]string{"a", "b", "c"}, 0, 1)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestUnique(t *testing.T) {
|
||||
t1 := []int{1, 2, 2, 3}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1 := Unique(t1)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestUnique")
|
||||
|
||||
t2 := []string{"a", "a", "b", "c"}
|
||||
e2 := []string{"a", "b", "c"}
|
||||
r2 := Unique(t2)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
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) {
|
||||
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, 0}, actual)
|
||||
}
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUnion")
|
||||
|
||||
s1 := []int{1, 3, 4, 6}
|
||||
s2 := []int{1, 2, 5, 6}
|
||||
s3 := []int{0, 4, 5, 7}
|
||||
|
||||
expected1 := []int{1, 3, 4, 6, 2, 5, 0, 7}
|
||||
res1 := Union(s1, s2, s3)
|
||||
if !reflect.DeepEqual(res1, expected1) {
|
||||
utils.LogFailedTestInfo(t, "Union", s1, expected1, res1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected2 := []int{1, 3, 4, 6}
|
||||
res2 := Union(s1)
|
||||
if !reflect.DeepEqual(res2, expected2) {
|
||||
utils.LogFailedTestInfo(t, "Union", s1, expected2, res2)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal([]int{1, 3, 4, 6, 2, 5, 0, 7}, Union(s1, s2, s3))
|
||||
assert.Equal([]int{1, 3, 4, 6, 2, 5}, Union(s1, s2))
|
||||
assert.Equal([]int{1, 3, 4, 6}, Union(s1))
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
@@ -448,45 +457,49 @@ func TestIntersection(t *testing.T) {
|
||||
Intersection(s1),
|
||||
Intersection(s1, s4),
|
||||
}
|
||||
for i := 0; i < len(res); i++ {
|
||||
if !reflect.DeepEqual(res[i], expected[i]) {
|
||||
utils.LogFailedTestInfo(t, "Intersection", "Intersection", expected[i], res[i])
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
assert := internal.NewAssert(t, "TestIntersection")
|
||||
|
||||
for i := 0; i < len(res); i++ {
|
||||
assert.Equal(res[i], expected[i])
|
||||
}
|
||||
assert.IsNil(Intersection())
|
||||
}
|
||||
|
||||
func TestReverseSlice(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIntersection")
|
||||
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
e1 := []int{5, 4, 3, 2, 1}
|
||||
ReverseSlice(s1)
|
||||
if !reflect.DeepEqual(s1, e1) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s1, e1, s1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal([]int{5, 4, 3, 2, 1}, s1)
|
||||
|
||||
s2 := []string{"a", "b", "c", "d", "e"}
|
||||
e2 := []string{"e", "d", "c", "b", "a"}
|
||||
ReverseSlice(s2)
|
||||
if !reflect.DeepEqual(s2, e2) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s2, e2, s2)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal([]string{"e", "d", "c", "b", "a"}, s2)
|
||||
}
|
||||
|
||||
func TestDifference(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDifference")
|
||||
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
s2 := []int{4, 5, 6}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1 := Difference(s1, s2)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Difference", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal([]int{1, 2, 3}, Difference(s1, s2))
|
||||
}
|
||||
|
||||
func TestSortByField(t *testing.T) {
|
||||
func TestDifferenceBy(t *testing.T) {
|
||||
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
|
||||
addOne := func(i int, v int) int {
|
||||
return v + 1
|
||||
}
|
||||
assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne))
|
||||
}
|
||||
|
||||
func TestSortByFielDesc(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSortByFielDesc")
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
@@ -497,8 +510,7 @@ func TestSortByField(t *testing.T) {
|
||||
{"c", 5},
|
||||
{"d", 6},
|
||||
}
|
||||
|
||||
sortByAge := []student{
|
||||
studentsOfSortByAge := []student{
|
||||
{"b", 15},
|
||||
{"a", 10},
|
||||
{"d", 6},
|
||||
@@ -506,24 +518,121 @@ func TestSortByField(t *testing.T) {
|
||||
}
|
||||
|
||||
err := SortByField(students, "age", "desc")
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(students, studentsOfSortByAge)
|
||||
}
|
||||
|
||||
func TestSortByFieldAsc(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSortByFieldAsc")
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
students := []student{
|
||||
{"a", 10},
|
||||
{"b", 15},
|
||||
{"c", 5},
|
||||
{"d", 6},
|
||||
}
|
||||
studentsOfSortByAge := []student{
|
||||
{"c", 5},
|
||||
{"d", 6},
|
||||
{"a", 10},
|
||||
{"b", 15},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(students, sortByAge) {
|
||||
utils.LogFailedTestInfo(t, "SortByField", students, sortByAge, students)
|
||||
t.FailNow()
|
||||
}
|
||||
err := SortByField(students, "age")
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(students, studentsOfSortByAge)
|
||||
}
|
||||
|
||||
func TestWithout(t *testing.T) {
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
expected := []int{3, 4, 5}
|
||||
res := Without(s, 1, 2)
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "Without", s, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestShuffle")
|
||||
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
res := Shuffle(s)
|
||||
t.Log("Shuffle result: ", res)
|
||||
|
||||
assert.Equal(reflect.TypeOf(s), reflect.TypeOf(res))
|
||||
|
||||
rv := reflect.ValueOf(res)
|
||||
assert.Equal(5, rv.Len())
|
||||
|
||||
assert.Equal(true, rv.Kind() == reflect.Slice)
|
||||
assert.Equal(true, rv.Type().Elem().Kind() == reflect.Int)
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
func TestIndexOf(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
arr := []string{"a", "a", "b", "c"}
|
||||
assert.Equal(0, IndexOf(arr, "a"))
|
||||
assert.Equal(-1, IndexOf(arr, "d"))
|
||||
}
|
||||
|
||||
func TestLastIndexOf(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLastIndexOf")
|
||||
|
||||
arr := []string{"a", "a", "b", "c"}
|
||||
assert.Equal(1, LastIndexOf(arr, "a"))
|
||||
assert.Equal(-1, LastIndexOf(arr, "d"))
|
||||
}
|
||||
|
||||
func TestToSlice(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToSlice")
|
||||
|
||||
str1 := "a"
|
||||
str2 := "b"
|
||||
out1 := ToSlice(str1)
|
||||
out2 := ToSlice(str1, str2)
|
||||
|
||||
assert.Equal([]string{"a"}, StringSlice(out1))
|
||||
assert.Equal([]string{"a", "b"}, StringSlice(out2))
|
||||
}
|
||||
|
||||
func TestToSlicePointer(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToSlicePointer")
|
||||
|
||||
var str1 interface{}
|
||||
str1 = "a"
|
||||
var str2 interface{}
|
||||
str2 = "b"
|
||||
|
||||
assert.Equal([]*interface{}{&str1}, ToSlicePointer(str1))
|
||||
assert.Equal([]*interface{}{&str1, &str2}, ToSlicePointer(str1, str2))
|
||||
}
|
||||
|
||||
func TestAppendIfAbsent(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToAppendIfAbsent")
|
||||
|
||||
str1 := []string{"a", "b"}
|
||||
assert.Equal([]string{"a", "b"}, AppendIfAbsent(str1, "a"))
|
||||
assert.Equal([]string{"a", "b", "c"}, AppendIfAbsent(str1, "c"))
|
||||
}
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -5,53 +5,62 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// CamelCase covert string to camelCase string.
|
||||
func CamelCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
// used in `Shuffle` function
|
||||
var rng = rand.New(rand.NewSource(int64(time.Now().UnixNano())))
|
||||
|
||||
res := ""
|
||||
blankSpace := " "
|
||||
regex, _ := regexp.Compile("[-_&]+")
|
||||
ss := regex.ReplaceAllString(s, blankSpace)
|
||||
for i, v := range strings.Split(ss, blankSpace) {
|
||||
vv := []rune(v)
|
||||
// CamelCase covert string to camelCase string.
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo11Bar"
|
||||
func CamelCase(s string) string {
|
||||
var builder strings.Builder
|
||||
|
||||
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.
|
||||
func Capitalize(s string) string {
|
||||
result := make([]rune, len(s))
|
||||
for i, v := range s {
|
||||
if i == 0 {
|
||||
result[i] = unicode.ToUpper(v)
|
||||
} else {
|
||||
result[i] = unicode.ToLower(v)
|
||||
}
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// UpperFirst converts the first character of string to upper case.
|
||||
func UpperFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
res := ""
|
||||
for i, v := range []rune(s) {
|
||||
if i == 0 {
|
||||
if v >= 97 && v <= 122 {
|
||||
v -= 32
|
||||
}
|
||||
res += string(v)
|
||||
} else {
|
||||
res += strings.ToLower(string(v))
|
||||
}
|
||||
}
|
||||
return res
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
r = unicode.ToUpper(r)
|
||||
|
||||
return string(r) + s[size:]
|
||||
}
|
||||
|
||||
// LowerFirst converts the first character of string to lower case.
|
||||
@@ -60,137 +69,103 @@ func LowerFirst(s string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
res := ""
|
||||
for i, v := range []rune(s) {
|
||||
if i == 0 {
|
||||
if v >= 65 && v <= 96 {
|
||||
v += 32
|
||||
res += string(v)
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
} else {
|
||||
res += string(v)
|
||||
}
|
||||
}
|
||||
return res
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
r = unicode.ToLower(r)
|
||||
|
||||
return string(r) + s[size:]
|
||||
}
|
||||
|
||||
// PadEnd pads string on the right side if it's shorter than size.
|
||||
// PadStart 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]
|
||||
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.
|
||||
func PadStart(source string, size int, padStr string) string {
|
||||
len1 := len(source)
|
||||
len2 := len(padStr)
|
||||
return padAtPosition(source, size, padStr, 1)
|
||||
}
|
||||
|
||||
if len1 >= size {
|
||||
return source
|
||||
}
|
||||
|
||||
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.
|
||||
func PadEnd(source string, size int, padStr string) string {
|
||||
return padAtPosition(source, size, padStr, 2)
|
||||
}
|
||||
|
||||
// KebabCase covert string to kebab-case
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo-1-1-bar"
|
||||
func KebabCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := splitIntoStrings(s, false)
|
||||
return strings.Join(result, "-")
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(res, "-")
|
||||
// UpperKebabCase covert string to upper KEBAB-CASE
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO-1-1-BAR"
|
||||
func UpperKebabCase(s string) string {
|
||||
result := splitIntoStrings(s, true)
|
||||
return strings.Join(result, "-")
|
||||
}
|
||||
|
||||
// SnakeCase covert string to snake_case
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "foo_1_1_bar"
|
||||
func SnakeCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := splitIntoStrings(s, false)
|
||||
return strings.Join(result, "_")
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(res, "_")
|
||||
// UpperSnakeCase covert string to upper SNAKE_CASE
|
||||
// non letters and numbers will be ignored
|
||||
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO_1_1_BAR"
|
||||
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
|
||||
func Before(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
i := strings.Index(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
return s
|
||||
}
|
||||
i := strings.Index(s, char)
|
||||
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// BeforeLast create substring in source string before position when char last appear
|
||||
func BeforeLast(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
return s
|
||||
}
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// After create substring in source string after position when char first appear
|
||||
func After(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
i := strings.Index(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
return s
|
||||
}
|
||||
i := strings.Index(s, char)
|
||||
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
// AfterLast create substring in source string after position when char last appear
|
||||
func AfterLast(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
if s == "" || char == "" || i == -1 {
|
||||
return s
|
||||
}
|
||||
i := strings.LastIndex(s, char)
|
||||
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
@@ -207,11 +182,498 @@ func IsString(v interface{}) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// ReverseStr return string whose char order is reversed to the given string
|
||||
func ReverseStr(s string) string {
|
||||
// Reverse return string whose char order is reversed to the given string
|
||||
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]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// Wrap a string with another string.
|
||||
func Wrap(str string, wrapWith string) string {
|
||||
if str == "" || wrapWith == "" {
|
||||
return str
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString(wrapWith)
|
||||
sb.WriteString(str)
|
||||
sb.WriteString(wrapWith)
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Unwrap a given string from anther string. will change str value
|
||||
func Unwrap(str string, wrapToken string) string {
|
||||
if str == "" || wrapToken == "" {
|
||||
return str
|
||||
}
|
||||
|
||||
firstIndex := strings.Index(str, wrapToken)
|
||||
lastIndex := strings.LastIndex(str, wrapToken)
|
||||
|
||||
if firstIndex == 0 && lastIndex > 0 && lastIndex <= len(str)-1 {
|
||||
if len(wrapToken) <= lastIndex {
|
||||
str = str[len(wrapToken):lastIndex]
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// SplitEx split a given string whether the result contains empty string
|
||||
func SplitEx(s, sep string, removeEmptyString bool) []string {
|
||||
if sep == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// SplitWords splits a string into words, word only contains alphabetic characters.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
func StringToBytes(str string) (b []byte) {
|
||||
return *(*[]byte)(unsafe.Pointer(&str))
|
||||
}
|
||||
|
||||
// BytesToString converts a byte slice to string without a memory allocation.
|
||||
func BytesToString(bytes []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&bytes))
|
||||
}
|
||||
|
||||
// IsBlank checks if a string is whitespace, empty.
|
||||
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
|
||||
}
|
||||
|
||||
// HasPrefixAny check if a string starts with any of an array of specified strings.
|
||||
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 an array of specified strings.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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)
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
func SubInBetween(str string, start string, end string) string {
|
||||
if _, after, ok := Cut(str, start); ok {
|
||||
if before, _, ok := Cut(after, end); ok {
|
||||
return before
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Cut splits the string at the first occurrence of separator.
|
||||
func Cut(str, sep string) (before, after string, found bool) {
|
||||
if i := strings.Index(str, sep); i >= 0 {
|
||||
return str[:i], str[i+len(sep):], true
|
||||
}
|
||||
return str, "", false
|
||||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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!".
|
||||
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.
|
||||
func RegexMatchAllGroups(pattern, str string) [][]string {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(str, -1)
|
||||
return matches
|
||||
}
|
||||
|
||||
167
strutil/string_internal.go
Normal file
167
strutil/string_internal.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package strutil
|
||||
|
||||
import "unicode"
|
||||
|
||||
func splitIntoStrings(s string, upperCase bool) []string {
|
||||
var runes [][]rune
|
||||
lastCharType := 0
|
||||
charType := 0
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if charType == lastCharType {
|
||||
runes[len(runes)-1] = append(runes[len(runes)-1], r)
|
||||
} else {
|
||||
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]) {
|
||||
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
|
||||
runes[i] = runes[i][:len(runes[i])-1]
|
||||
}
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func padAtPosition(str string, length int, padStr string, position int) string {
|
||||
if len(str) >= length {
|
||||
return str
|
||||
}
|
||||
|
||||
if padStr == "" {
|
||||
padStr = " "
|
||||
}
|
||||
|
||||
length = length - len(str)
|
||||
startPadLen := 0
|
||||
if position == 0 {
|
||||
startPadLen = length / 2
|
||||
} else if position == 1 {
|
||||
startPadLen = length
|
||||
}
|
||||
endPadLen := length - startPadLen
|
||||
|
||||
charLen := len(padStr)
|
||||
leftPad := ""
|
||||
cur := 0
|
||||
for cur < startPadLen {
|
||||
leftPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
}
|
||||
|
||||
cur = 0
|
||||
rightPad := ""
|
||||
for cur < endPadLen {
|
||||
rightPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
}
|
||||
|
||||
return leftPad + str + rightPad
|
||||
}
|
||||
|
||||
// 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 true
|
||||
}
|
||||
@@ -1,200 +1,744 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestCamelCase(t *testing.T) {
|
||||
camelCase(t, "foo_bar", "fooBar")
|
||||
camelCase(t, "Foo-Bar", "fooBar")
|
||||
camelCase(t, "Foo&bar", "fooBar")
|
||||
camelCase(t, "foo bar", "fooBar")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestCamelCase")
|
||||
|
||||
func camelCase(t *testing.T, test string, expected string) {
|
||||
res := CamelCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "CamelCase", test, expected, res)
|
||||
t.FailNow()
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foobar": "foobar",
|
||||
"&FOO:BAR$BAZ": "fooBarBaz",
|
||||
"fooBar": "fooBar",
|
||||
"FOObar": "foObar",
|
||||
"$foo%": "foo",
|
||||
" $#$Foo 22 bar ": "foo22Bar",
|
||||
"Foo-#1😄$_%^&*(1bar": "foo11Bar",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, CamelCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapitalize(t *testing.T) {
|
||||
capitalize(t, "foo", "Foo")
|
||||
capitalize(t, "fOO", "Foo")
|
||||
capitalize(t, "FOo", "Foo")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestCapitalize")
|
||||
|
||||
func capitalize(t *testing.T, test string, expected string) {
|
||||
res := Capitalize(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Capitalize", test, expected, res)
|
||||
t.FailNow()
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"Foo": "Foo",
|
||||
"_foo": "_foo",
|
||||
"foobar": "Foobar",
|
||||
"fooBar": "Foobar",
|
||||
"foo Bar": "Foo bar",
|
||||
"foo-bar": "Foo-bar",
|
||||
"$foo%": "$foo%",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, Capitalize(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKebabCase(t *testing.T) {
|
||||
kebabCase(t, "Foo Bar-", "foo-bar")
|
||||
kebabCase(t, "foo_Bar", "foo-bar")
|
||||
kebabCase(t, "fooBar", "foo-bar")
|
||||
kebabCase(t, "__FOO_BAR__", "f-o-o-b-a-r")
|
||||
assert := internal.NewAssert(t, "TestKebabCase")
|
||||
|
||||
cases := map[string]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 k, v := range cases {
|
||||
assert.Equal(v, KebabCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func kebabCase(t *testing.T, test string, expected string) {
|
||||
res := KebabCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "KebabCase", test, expected, res)
|
||||
t.FailNow()
|
||||
func TestUpperKebabCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUpperKebabCase")
|
||||
|
||||
cases := map[string]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 k, v := range cases {
|
||||
assert.Equal(v, UpperKebabCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnakeCase(t *testing.T) {
|
||||
snakeCase(t, "Foo Bar-", "foo_bar")
|
||||
snakeCase(t, "foo_Bar", "foo_bar")
|
||||
snakeCase(t, "fooBar", "foo_bar")
|
||||
snakeCase(t, "__FOO_BAR__", "f_o_o_b_a_r")
|
||||
snakeCase(t, "aBbc-s$@a&%_B.B^C", "a_bbc_s_a_b_b_c")
|
||||
assert := internal.NewAssert(t, "TestSnakeCase")
|
||||
|
||||
cases := map[string]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 k, v := range cases {
|
||||
assert.Equal(v, SnakeCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func snakeCase(t *testing.T, test string, expected string) {
|
||||
res := SnakeCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "SnakeCase", test, expected, res)
|
||||
t.FailNow()
|
||||
func TestUpperSnakeCase(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUpperSnakeCase")
|
||||
|
||||
cases := map[string]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 k, v := range cases {
|
||||
assert.Equal(v, UpperSnakeCase(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpperFirst(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLowerFirst")
|
||||
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo": "Foo",
|
||||
"bAR": "BAR",
|
||||
"FOo": "FOo",
|
||||
"fOo大": "FOo大",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, UpperFirst(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerFirst(t *testing.T) {
|
||||
lowerFirst(t, "foo", "foo")
|
||||
lowerFirst(t, "BAR", "bAR")
|
||||
lowerFirst(t, "FOo", "fOo")
|
||||
assert := internal.NewAssert(t, "TestLowerFirst")
|
||||
|
||||
cases := map[string]string{
|
||||
"": "",
|
||||
"foo": "foo",
|
||||
"bAR": "bAR",
|
||||
"FOo": "fOo",
|
||||
"fOo大": "fOo大",
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, LowerFirst(k))
|
||||
}
|
||||
}
|
||||
|
||||
func lowerFirst(t *testing.T, test string, expected string) {
|
||||
res := LowerFirst(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "LowerFirst", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
func TestPad(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPad")
|
||||
|
||||
assert.Equal("a ", Pad("a", 2, ""))
|
||||
assert.Equal("a", Pad("a", 1, "b"))
|
||||
assert.Equal("ab", Pad("a", 2, "b"))
|
||||
assert.Equal("mabcdm", Pad("abcd", 6, "m"))
|
||||
}
|
||||
|
||||
func TestPadEnd(t *testing.T) {
|
||||
padEnd(t, "a", 1, "b", "a")
|
||||
padEnd(t, "a", 2, "b", "ab")
|
||||
padEnd(t, "abcd", 6, "mno", "abcdmn")
|
||||
padEnd(t, "abcd", 6, "m", "abcdmm")
|
||||
padEnd(t, "abc", 6, "ab", "abcaba")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestPadEnd")
|
||||
|
||||
func padEnd(t *testing.T, source string, size int, fillString string, expected string) {
|
||||
res := PadEnd(source, size, fillString)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal("a ", PadEnd("a", 2, " "))
|
||||
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"))
|
||||
|
||||
assert.NotEqual("ba", PadEnd("a", 2, "b"))
|
||||
}
|
||||
|
||||
func TestPadStart(t *testing.T) {
|
||||
padStart(t, "a", 1, "b", "a")
|
||||
padStart(t, "a", 2, "b", "ba")
|
||||
padStart(t, "abcd", 6, "mno", "mnabcd")
|
||||
padStart(t, "abcd", 6, "m", "mmabcd")
|
||||
padStart(t, "abc", 6, "ab", "abaabc")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestPadStart")
|
||||
|
||||
func padStart(t *testing.T, source string, size int, fillString string, expected string) {
|
||||
res := PadStart(source, size, fillString)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
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"))
|
||||
|
||||
assert.NotEqual("ab", PadStart("a", 2, "b"))
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
before(t, "lancet", "", "lancet")
|
||||
before(t, "github.com/test/lancet", "/", "github.com")
|
||||
before(t, "github.com/test/lancet", "test", "github.com/")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestBefore")
|
||||
|
||||
func before(t *testing.T, source, char, expected string) {
|
||||
res := Before(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Before", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal("lancet", Before("lancet", ""))
|
||||
assert.Equal("lancet", Before("lancet", "abcdef"))
|
||||
assert.Equal("github.com", Before("github.com/test/lancet", "/"))
|
||||
assert.Equal("github.com/", Before("github.com/test/lancet", "test"))
|
||||
}
|
||||
|
||||
func TestBeforeLast(t *testing.T) {
|
||||
beforeLast(t, "lancet", "", "lancet")
|
||||
beforeLast(t, "github.com/test/lancet", "/", "github.com/test")
|
||||
beforeLast(t, "github.com/test/test/lancet", "test", "github.com/test/")
|
||||
assert := internal.NewAssert(t, "TestBeforeLast")
|
||||
|
||||
assert.Equal("lancet", BeforeLast("lancet", ""))
|
||||
|
||||
assert.Equal("lancet", BeforeLast("lancet", "abcdef"))
|
||||
assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/"))
|
||||
assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test"))
|
||||
|
||||
assert.NotEqual("github.com/", BeforeLast("github.com/test/test/lancet", "test"))
|
||||
}
|
||||
|
||||
func beforeLast(t *testing.T, source, char, expected string) {
|
||||
res := BeforeLast(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "BeforeLast", source, expected, res)
|
||||
t.FailNow()
|
||||
func TestHammingDistance(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "HammingDistance")
|
||||
|
||||
hd := func(a, b string) int {
|
||||
c, _ := HammingDistance(a, b)
|
||||
return c
|
||||
}
|
||||
|
||||
assert.Equal(0, hd(" ", " "))
|
||||
assert.Equal(1, hd(" ", "c"))
|
||||
assert.Equal(1, hd("a", "d"))
|
||||
assert.Equal(1, hd("a", " "))
|
||||
assert.Equal(1, hd("a", "f"))
|
||||
|
||||
assert.Equal(0, hd("", ""))
|
||||
assert.Equal(-1, hd("abc", "ab"))
|
||||
assert.Equal(3, hd("abc", "def"))
|
||||
assert.Equal(-1, hd("kitten", "sitting"))
|
||||
assert.Equal(1, hd("ö", "ü"))
|
||||
assert.Equal(0, hd("日本語", "日本語"))
|
||||
assert.Equal(3, hd("日本語", "語日本"))
|
||||
}
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
after(t, "lancet", "", "lancet")
|
||||
after(t, "github.com/test/lancet", "/", "test/lancet")
|
||||
after(t, "github.com/test/lancet", "test", "/lancet")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestAfter")
|
||||
|
||||
func after(t *testing.T, source, char, expected string) {
|
||||
res := After(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "After", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal("lancet", After("lancet", ""))
|
||||
assert.Equal("lancet", After("lancet", "abcdef"))
|
||||
assert.Equal("test/lancet", After("github.com/test/lancet", "/"))
|
||||
assert.Equal("/lancet", After("github.com/test/lancet", "test"))
|
||||
}
|
||||
|
||||
func TestAfterLast(t *testing.T) {
|
||||
afterLast(t, "lancet", "", "lancet")
|
||||
afterLast(t, "github.com/test/lancet", "/", "lancet")
|
||||
afterLast(t, "github.com/test/test/lancet", "test", "/lancet")
|
||||
}
|
||||
assert := internal.NewAssert(t, "TestAfterLast")
|
||||
|
||||
func afterLast(t *testing.T, source, char, expected string) {
|
||||
res := AfterLast(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "AfterLast", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal("lancet", AfterLast("lancet", ""))
|
||||
assert.Equal("lancet", AfterLast("lancet", "abcdef"))
|
||||
assert.Equal("lancet", AfterLast("github.com/test/lancet", "/"))
|
||||
assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test"))
|
||||
assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test"))
|
||||
|
||||
assert.NotEqual("/test/lancet", AfterLast("github.com/test/test/lancet", "test"))
|
||||
}
|
||||
|
||||
func TestIsString(t *testing.T) {
|
||||
isString(t, "lancet", true)
|
||||
isString(t, 1, false)
|
||||
isString(t, true, false)
|
||||
isString(t, []string{}, false)
|
||||
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{}))
|
||||
}
|
||||
|
||||
func isString(t *testing.T, test interface{}, expected bool) {
|
||||
res := IsString(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsString", test, expected, res)
|
||||
t.FailNow()
|
||||
func TestReverse(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestReverse")
|
||||
|
||||
assert.Equal("cba", Reverse("abc"))
|
||||
assert.Equal("54321", Reverse("12345"))
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWrap")
|
||||
|
||||
assert.Equal("ab", Wrap("ab", ""))
|
||||
assert.Equal("", Wrap("", "*"))
|
||||
assert.Equal("*ab*", Wrap("ab", "*"))
|
||||
assert.Equal("\"ab\"", Wrap("ab", "\""))
|
||||
assert.Equal("'ab'", Wrap("ab", "'"))
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUnwrap")
|
||||
|
||||
assert.Equal("", Unwrap("", "*"))
|
||||
assert.Equal("ab", Unwrap("ab", ""))
|
||||
assert.Equal("ab", Unwrap("ab", "*"))
|
||||
assert.Equal("*ab*", Unwrap("**ab**", "*"))
|
||||
assert.Equal("ab", Unwrap("**ab**", "**"))
|
||||
assert.Equal("ab", Unwrap("\"ab\"", "\""))
|
||||
assert.Equal("*ab", Unwrap("*ab", "*"))
|
||||
assert.Equal("ab*", Unwrap("ab*", "*"))
|
||||
assert.Equal("*", Unwrap("***", "*"))
|
||||
|
||||
assert.Equal("", Unwrap("**", "*"))
|
||||
assert.Equal("***", Unwrap("***", "**"))
|
||||
assert.Equal("**", Unwrap("**", "**"))
|
||||
}
|
||||
|
||||
func TestSplitEx(t *testing.T) {
|
||||
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 TestSplitWords(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSplitWords")
|
||||
|
||||
cases := map[string][]string{
|
||||
"a word": {"a", "word"},
|
||||
"I'am a programmer": {"I'am", "a", "programmer"},
|
||||
"Bonjour, je suis programmeur": {"Bonjour", "je", "suis", "programmeur"},
|
||||
"a -b-c' 'd'e": {"a", "b-c'", "d'e"},
|
||||
"你好,我是一名码农": nil,
|
||||
"こんにちは,私はプログラマーです": nil,
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, SplitWords(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseStr(t *testing.T) {
|
||||
reverseStr(t, "abc", "cba")
|
||||
reverseStr(t, "12345", "54321")
|
||||
func TestWordCount(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSplitWords")
|
||||
|
||||
//failed
|
||||
//reverseStr(t, "abc", "abc")
|
||||
}
|
||||
cases := map[string]int{
|
||||
"a word": 2, // {"a", "word"},
|
||||
"I'am a programmer": 3, // {"I'am", "a", "programmer"},
|
||||
"Bonjour, je suis programmeur": 4, // {"Bonjour", "je", "suis", "programmeur"},
|
||||
"a -b-c' 'd'e": 3, // {"a", "b-c'", "d'e"},
|
||||
"你好,我是一名码农": 0, // nil,
|
||||
"こんにちは,私はプログラマーです": 0, // nil,
|
||||
}
|
||||
|
||||
func reverseStr(t *testing.T, test string, expected string) {
|
||||
res := ReverseStr(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "ReverseStr", test, expected, res)
|
||||
t.FailNow()
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, WordCount(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNonPrintable(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRemoveNonPrintable")
|
||||
|
||||
assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n"))
|
||||
assert.Equal("你好😄", RemoveNonPrintable("你好😄"))
|
||||
}
|
||||
|
||||
func TestStringToBytes(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStringToBytes")
|
||||
str := "abc"
|
||||
bytes := StringToBytes(str)
|
||||
assert.Equal(reflect.DeepEqual(bytes, []byte{'a', 'b', 'c'}), true)
|
||||
}
|
||||
|
||||
func TestBytesToString(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestBytesToString")
|
||||
bytes := []byte{'a', 'b', 'c'}
|
||||
str := BytesToString(bytes)
|
||||
assert.Equal(str == "abc", true)
|
||||
}
|
||||
|
||||
func TestIsBlank(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsBlank")
|
||||
assert.Equal(IsBlank(""), true)
|
||||
assert.Equal(IsBlank("\t\v\f\n"), true)
|
||||
assert.Equal(IsBlank(" 中文"), false)
|
||||
}
|
||||
|
||||
func TestHasPrefixAny(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestHasPrefixAny")
|
||||
str := "foo bar"
|
||||
prefixes := []string{"fo", "xyz", "hello"}
|
||||
notMatches := []string{"oom", "world"}
|
||||
assert.Equal(HasPrefixAny(str, prefixes), true)
|
||||
assert.Equal(HasPrefixAny(str, notMatches), false)
|
||||
}
|
||||
|
||||
func TestHasSuffixAny(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestHasSuffixAny")
|
||||
str := "foo bar"
|
||||
suffixes := []string{"bar", "xyz", "hello"}
|
||||
notMatches := []string{"oom", "world"}
|
||||
assert.Equal(HasSuffixAny(str, suffixes), true)
|
||||
assert.Equal(HasSuffixAny(str, notMatches), false)
|
||||
}
|
||||
|
||||
func TestIndexOffset(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIndexOffset")
|
||||
str := "foo bar hello world"
|
||||
assert.Equal(IndexOffset(str, "o", 5), 12)
|
||||
assert.Equal(IndexOffset(str, "o", 0), 1)
|
||||
assert.Equal(IndexOffset(str, "d", len(str)-1), len(str)-1)
|
||||
assert.Equal(IndexOffset(str, "d", len(str)), -1)
|
||||
assert.Equal(IndexOffset(str, "f", -1), -1)
|
||||
}
|
||||
|
||||
func TestReplaceWithMap(t *testing.T) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestTrim")
|
||||
|
||||
str := "13242658976"
|
||||
|
||||
assert.Equal("13242658976", HideString(str, 0, -1, "*"))
|
||||
assert.Equal("13242658976", HideString(str, 0, 0, "*"))
|
||||
assert.Equal("****2658976", HideString(str, 0, 4, "*"))
|
||||
|
||||
assert.Equal("13242658976", HideString(str, 3, 3, "*"))
|
||||
assert.Equal("132*2658976", HideString(str, 3, 4, "*"))
|
||||
assert.Equal("132****8976", HideString(str, 3, 7, "*"))
|
||||
assert.Equal("1324265****", HideString(str, 7, 11, "*"))
|
||||
|
||||
assert.Equal("1324265****", HideString(str, 7, 100, "*"))
|
||||
assert.Equal("13242658976", HideString(str, 100, 100, "*"))
|
||||
}
|
||||
|
||||
func TestContainsAll(t *testing.T) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
assert := internal.NewAssert(t, "TestSubInBetween")
|
||||
|
||||
str := "abcde"
|
||||
|
||||
assert.Equal("", SubInBetween(str, "", ""))
|
||||
assert.Equal("ab", SubInBetween(str, "", "c"))
|
||||
assert.Equal("bc", SubInBetween(str, "a", "d"))
|
||||
assert.Equal("", SubInBetween(str, "a", ""))
|
||||
assert.Equal("", SubInBetween(str, "a", "f"))
|
||||
}
|
||||
|
||||
func TestConcat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestConcat")
|
||||
|
||||
assert.Equal("", Concat(0))
|
||||
assert.Equal("a", Concat(1, "a"))
|
||||
assert.Equal("ab", Concat(2, "a", "b"))
|
||||
assert.Equal("abc", Concat(3, "a", "b", "c"))
|
||||
assert.Equal("abc", Concat(3, "a", "", "b", "c", ""))
|
||||
assert.Equal("你好,世界!", Concat(0, "你好", ",", "", "世界!", ""))
|
||||
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
|
||||
}
|
||||
|
||||
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 TestCut(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCut")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
s string
|
||||
sep string
|
||||
before string
|
||||
after string
|
||||
found bool
|
||||
}{
|
||||
{
|
||||
name: "test with separator",
|
||||
s: "hello-world",
|
||||
sep: "-",
|
||||
before: "hello",
|
||||
after: "world",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "test without separator",
|
||||
s: "helloworld",
|
||||
sep: "-",
|
||||
before: "helloworld",
|
||||
after: "",
|
||||
found: false,
|
||||
},
|
||||
{
|
||||
name: "test empty string",
|
||||
s: "",
|
||||
sep: "-",
|
||||
before: "",
|
||||
after: "",
|
||||
found: false,
|
||||
},
|
||||
{
|
||||
name: "test separator at the beginning",
|
||||
s: "-hello",
|
||||
sep: "-",
|
||||
before: "",
|
||||
after: "hello",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "test separator at the end",
|
||||
s: "hello-",
|
||||
sep: "-",
|
||||
before: "hello",
|
||||
after: "",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "test multiple separators",
|
||||
s: "a-b-c-d",
|
||||
sep: "-",
|
||||
before: "a",
|
||||
after: "b-c-d",
|
||||
found: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
before, after, found := Cut(tt.s, tt.sep)
|
||||
assert.Equal(tt.before, before)
|
||||
assert.Equal(tt.after, after)
|
||||
assert.Equal(tt.found, found)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
|
||||
// splitWordsToLower split a string into worlds by uppercase char
|
||||
func splitWordsToLower(s string) []string {
|
||||
var res []string
|
||||
|
||||
upperIndexes := upperIndex(s)
|
||||
l := len(upperIndexes)
|
||||
if upperIndexes == nil || l == 0 {
|
||||
if s != "" {
|
||||
res = append(res, s)
|
||||
}
|
||||
return res
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
if i < l-1 {
|
||||
res = append(res, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]]))
|
||||
} else {
|
||||
res = append(res, strings.ToLower(s[upperIndexes[i]:]))
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
if len(s) > 0 && res != nil && res[0] != 0 {
|
||||
res = append([]int{0}, res...)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
127
system/os.go
Normal file
127
system/os.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package system contain some functions about os, runtime, shell command.
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/duke-git/lancet/validator"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
)
|
||||
|
||||
type (
|
||||
Option func(*exec.Cmd)
|
||||
)
|
||||
|
||||
// IsWindows check if current os is windows
|
||||
func IsWindows() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// IsLinux check if current os is linux
|
||||
func IsLinux() bool {
|
||||
return runtime.GOOS == "linux"
|
||||
}
|
||||
|
||||
// IsMac check if current os is macos
|
||||
func IsMac() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
|
||||
// GetOsEnv gets the value of the environment variable named by the key.
|
||||
func GetOsEnv(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
// SetOsEnv sets the value of the environment variable named by the key.
|
||||
func SetOsEnv(key, value string) error {
|
||||
return os.Setenv(key, value)
|
||||
}
|
||||
|
||||
// RemoveOsEnv remove a single environment variable.
|
||||
func RemoveOsEnv(key string) error {
|
||||
return os.Unsetenv(key)
|
||||
}
|
||||
|
||||
// CompareOsEnv gets env named by the key and compare it with comparedEnv
|
||||
func CompareOsEnv(key, comparedEnv string) bool {
|
||||
env := GetOsEnv(key)
|
||||
if env == "" {
|
||||
return false
|
||||
}
|
||||
return env == comparedEnv
|
||||
}
|
||||
|
||||
// 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
|
||||
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error) {
|
||||
var out bytes.Buffer
|
||||
var errOut bytes.Buffer
|
||||
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
if IsWindows() {
|
||||
cmd = exec.Command("powershell.exe", command)
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &errOut
|
||||
|
||||
err = cmd.Run()
|
||||
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
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 get this system bits 32bit or 64bit
|
||||
// return bit int (32/64)
|
||||
func GetOsBits() int {
|
||||
return 32 << (^uint(0) >> 63)
|
||||
}
|
||||
25
system/os_darwin.go
Normal file
25
system/os_darwin.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build darwin
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
||||
26
system/os_linux.go
Normal file
26
system/os_linux.go
Normal file
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user