mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
15 Commits
213e2b4ead
...
8bbae69175
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bbae69175 | |||
| 840ea8f3c5 | |||
| a4e89bd7c1 | |||
| 2015d36b08 | |||
| 921f218ef7 | |||
| 0a2cc9c928 | |||
| ed93aae970 | |||
| 1671f7856a | |||
| 1008dd4956 | |||
| a254ebdc8e | |||
| 0bc11001a4 | |||
| 85d98ad915 | |||
| cb08613ac3 | |||
| bad1b05224 | |||
| 527328739a |
+1
-1
@@ -8,7 +8,7 @@ fileutil/*.link
|
|||||||
fileutil/unzip/*
|
fileutil/unzip/*
|
||||||
fileutil/tempdir/*
|
fileutil/tempdir/*
|
||||||
slice/testdata/*
|
slice/testdata/*
|
||||||
cryptor/*.pem
|
# cryptor/*.pem
|
||||||
test
|
test
|
||||||
docs/node_modules
|
docs/node_modules
|
||||||
docs/.vitepress/cache
|
docs/.vitepress/cache
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (l *LRUCache[K, V]) Get(key K) (V, bool) {
|
|||||||
|
|
||||||
node, ok := l.cache[key]
|
node, ok := l.cache[key]
|
||||||
if ok {
|
if ok {
|
||||||
l.moveToHead(node)
|
l.moveToTail(node)
|
||||||
return node.value, true
|
return node.value, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ func (l *LRUCache[K, V]) Put(key K, value V) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
node.value = value
|
node.value = value
|
||||||
l.moveToHead(node)
|
l.moveToTail(node)
|
||||||
}
|
}
|
||||||
l.length = len(l.cache)
|
l.length = len(l.cache)
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ func (l *LRUCache[K, V]) Delete(key K) bool {
|
|||||||
delete(l.cache, key)
|
delete(l.cache, key)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
l.length = len(l.cache)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K {
|
|||||||
return node.key
|
return node.key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) {
|
func (l *LRUCache[K, V]) moveToTail(node *lruNode[K, V]) {
|
||||||
if l.tail == node {
|
if l.tail == node {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,20 @@ package cryptor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/des"
|
"crypto/des"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||||
@@ -591,6 +595,7 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipherText
|
return cipherText
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,6 +629,7 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText
|
return plainText
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,3 +661,150 @@ func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte
|
|||||||
|
|
||||||
return decryptedBytes, nil
|
return decryptedBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RsaSign signs the data with RSA.
|
||||||
|
// Play: todo
|
||||||
|
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) {
|
||||||
|
privateKey, err := loadRasPrivateKey(privateKeyFileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed, err := hashData(hash, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsa.SignPKCS1v15(rand.Reader, privateKey, hash, hashed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RsaVerifySign verifies the signature of the data with RSA.
|
||||||
|
// Play: todo
|
||||||
|
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error {
|
||||||
|
publicKey, err := loadRsaPublicKey(pubKeyFileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed, err := hashData(hash, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsa.VerifyPKCS1v15(publicKey, hash, hashed, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||||
|
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
|
||||||
|
pubKeyData, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(pubKeyData)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("failed to decode PEM block containing the public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubKey *rsa.PublicKey
|
||||||
|
blockType := strings.ToUpper(block.Type)
|
||||||
|
|
||||||
|
if blockType == "RSA PUBLIC KEY" {
|
||||||
|
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
// todo: here should be a bug, should return nil, err
|
||||||
|
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
pubKey, ok = key.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to parse RSA private key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if blockType == "PUBLIC KEY" {
|
||||||
|
key, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
pubKey, ok = key.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to parse RSA private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unsupported key type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
|
||||||
|
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
|
||||||
|
priKeyData, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(priKeyData)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("failed to decode PEM block containing the private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var privateKey *rsa.PrivateKey
|
||||||
|
blockType := strings.ToUpper(block.Type)
|
||||||
|
|
||||||
|
// PKCS#1 format
|
||||||
|
if blockType == "RSA PRIVATE KEY" {
|
||||||
|
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
|
||||||
|
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
privateKey, ok = priKey.(*rsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to parse RSA private key")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unsupported key type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashData returns the hash value of the data, using the specified hash function
|
||||||
|
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
|
||||||
|
if !hash.Available() {
|
||||||
|
return nil, errors.New("unsupported hash algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashed []byte
|
||||||
|
|
||||||
|
switch hash {
|
||||||
|
case crypto.SHA224:
|
||||||
|
h := sha256.Sum224(data)
|
||||||
|
hashed = h[:]
|
||||||
|
case crypto.SHA256:
|
||||||
|
h := sha256.Sum256(data)
|
||||||
|
hashed = h[:]
|
||||||
|
case crypto.SHA384:
|
||||||
|
h := sha512.Sum384(data)
|
||||||
|
hashed = h[:]
|
||||||
|
case crypto.SHA512:
|
||||||
|
h := sha512.Sum512(data)
|
||||||
|
hashed = h[:]
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported hash algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashed, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cryptor
|
package cryptor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ func ExampleDesOfbDecrypt() {
|
|||||||
|
|
||||||
func ExampleGenerateRsaKey() {
|
func ExampleGenerateRsaKey() {
|
||||||
// Create ras private and public pem file
|
// Create ras private and public pem file
|
||||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -297,14 +298,14 @@ func ExampleGenerateRsaKey() {
|
|||||||
|
|
||||||
func ExampleRsaEncrypt() {
|
func ExampleRsaEncrypt() {
|
||||||
// Create ras private and public pem file
|
// Create ras private and public pem file
|
||||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := []byte("hello")
|
data := []byte("hello")
|
||||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
encrypted := RsaEncrypt(data, "rsa_public_example.pem")
|
||||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem")
|
||||||
|
|
||||||
fmt.Println(string(decrypted))
|
fmt.Println(string(decrypted))
|
||||||
|
|
||||||
@@ -314,14 +315,14 @@ func ExampleRsaEncrypt() {
|
|||||||
|
|
||||||
func ExampleRsaDecrypt() {
|
func ExampleRsaDecrypt() {
|
||||||
// Create ras private and public pem file
|
// Create ras private and public pem file
|
||||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := []byte("hello")
|
data := []byte("hello")
|
||||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
encrypted := RsaEncrypt(data, "rsa_public_example.pem")
|
||||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem")
|
||||||
|
|
||||||
fmt.Println(string(decrypted))
|
fmt.Println(string(decrypted))
|
||||||
|
|
||||||
@@ -536,3 +537,49 @@ func ExampleRsaEncryptOAEP() {
|
|||||||
// Output:
|
// Output:
|
||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleRsaSign() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private_example.pem"
|
||||||
|
publicKey := "./rsa_public_example.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("ok")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRsaVerifySign() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private_example.pem"
|
||||||
|
publicKey := "./rsa_public_example.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("ok")
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
|||||||
+54
-4
@@ -1,6 +1,7 @@
|
|||||||
package cryptor
|
package cryptor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -139,13 +140,13 @@ func TestDesOfbEncrypt(t *testing.T) {
|
|||||||
func TestRsaEncrypt(t *testing.T) {
|
func TestRsaEncrypt(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
err := GenerateRsaKey(4096, "./rsa_private_example.pem", "./rsa_public_example.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
data := []byte("hello world")
|
data := []byte("hello world")
|
||||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
encrypted := RsaEncrypt(data, "./rsa_public_example.pem")
|
||||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
decrypted := RsaDecrypt(encrypted, "./rsa_private_example.pem")
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestRsaEncrypt")
|
assert := internal.NewAssert(t, "TestRsaEncrypt")
|
||||||
assert.Equal(string(data), string(decrypted))
|
assert.Equal(string(data), string(decrypted))
|
||||||
@@ -170,7 +171,6 @@ func TestRsaEncryptOAEP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAesGcmEncrypt(t *testing.T) {
|
func TestAesGcmEncrypt(t *testing.T) {
|
||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
data := "hello world"
|
data := "hello world"
|
||||||
@@ -182,3 +182,53 @@ func TestAesGcmEncrypt(t *testing.T) {
|
|||||||
assert := internal.NewAssert(t, "TestAesGcmEncrypt")
|
assert := internal.NewAssert(t, "TestAesGcmEncrypt")
|
||||||
assert.Equal(data, string(decrypted))
|
assert.Equal(data, string(decrypted))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRsaSignAndVerify(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
t.Run("RSA Sign and Verify", func(t *testing.T) {
|
||||||
|
privateKey := "./rsa_private_example.pem"
|
||||||
|
publicKey := "./rsa_public_example.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RsaSign failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RsaVerifySign failed: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RSA Sign and Verify Invalid Signature", func(t *testing.T) {
|
||||||
|
publicKey := "./rsa_public_example.pem"
|
||||||
|
|
||||||
|
invalidSig := []byte("InvalidSignature")
|
||||||
|
|
||||||
|
err := RsaVerifySign(hash, data, invalidSig, publicKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("RsaVerifySign failed: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RSA Sign and Verify With Different Hash", func(t *testing.T) {
|
||||||
|
publicKey := "./rsa_public_example.pem"
|
||||||
|
privateKey := "./rsa_private_example.pem"
|
||||||
|
hashSign := crypto.SHA256
|
||||||
|
hashVerify := crypto.SHA512
|
||||||
|
|
||||||
|
signature, err := RsaSign(hashSign, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RsaSign failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hashVerify, data, signature, publicKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("RsaVerifySign failed: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN rsa private key-----
|
||||||
|
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
|
||||||
|
-----END rsa private key-----
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
-----BEGIN rsa public key-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsELqn9abxs7ZK81Ipbp5
|
||||||
|
+E/5SrOQycAalnRCES3YPOoAV3Ir+99WjEi9Ak4EYS3VrvgISSNiRx1tHDNpBlBq
|
||||||
|
GF+Mww8wAUmrcXsEndbHma/g3zHGueua9GK0l+hQJ/grkatykgD81gam/SPOMPnJ
|
||||||
|
SsoQlsTgM47ZHM35adiXqL/gDMZ70lQdB2+03WJXlvfYZWpHsXDqUkveEZbLgZST
|
||||||
|
36ax8ap+Ge2GoLlD1sNapRxMBH1PGNdzSdV7ZJmzQ3WmjwDAddLX73YE6vhR5tDA
|
||||||
|
XhrSweNPtIjUhGJsgkW9VTEQWeW9cZmFo/jYuvEDgBnZOh1DJe2BHx06Ymr8Zcgs
|
||||||
|
OBYddANsk750II8+qUnBekiJkpo0ldCK2VnsLoLM3YCf3Lk7VYuHP6KiCXyh6Fzb
|
||||||
|
Y1DTRHJRpMJYT8t5xyQis581DbRVPkcse3ig5KsQLe5XXbBBegVrQJpy1b440uZV
|
||||||
|
W2eSpStX5ZAfNVsU0ZPdI4/i6UNMEa6c7tf83S21Q0K6SEZ5PrfBJECRDxFzJWea
|
||||||
|
mwppHY8fdeItVk/cxBJ9XRyOyG9pJKMnOMFD++LzGP6uL5FKov7BSplOIMqsJzwh
|
||||||
|
4zkIjAhgbGVQZm17kg1xvF30c2fgijNEoSIuVWJzlQyQ9cmSaSmmi+SPmf/C/IcU
|
||||||
|
DF0nikERs1v58vNESvcZP80CAwEAAQ==
|
||||||
|
-----END rsa public key-----
|
||||||
@@ -70,6 +70,9 @@ import (
|
|||||||
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
|
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
|
||||||
- [RsaEncryptOAEP](#RsaEncryptOAEP)
|
- [RsaEncryptOAEP](#RsaEncryptOAEP)
|
||||||
- [RsaDecryptOAEP](#RsaDecryptOAEP)
|
- [RsaDecryptOAEP](#RsaDecryptOAEP)
|
||||||
|
- [RsaSign](#RsaSign)
|
||||||
|
- [RsaVerifySign](#RsaVerifySign)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1607,3 +1610,81 @@ func main() {
|
|||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RsaSign">RsaSign</span>
|
||||||
|
|
||||||
|
<p>应用RSA算法签名数据。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/cryptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private.pem"
|
||||||
|
publicKey := "./rsa_public.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RsaVerifySign">RsaVerifySign</span>
|
||||||
|
|
||||||
|
<p>验证数据的签名是否符合RSA算法。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/cryptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private.pem"
|
||||||
|
publicKey := "./rsa_public.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
- [ReadFile](#ReadFile)
|
- [ReadFile](#ReadFile)
|
||||||
- [ChunkRead](#ChunkRead)
|
- [ChunkRead](#ChunkRead)
|
||||||
- [ParallelChunkRead](#ParallelChunkRead)
|
- [ParallelChunkRead](#ParallelChunkRead)
|
||||||
|
- [GetExeOrDllVersion](#GetExeOrDllVersion)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -559,7 +560,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := fileutil.Zip("./test.zip", "./unzip/test.txt")
|
err := fileutil.UnZip("./test.zip", "./test.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
@@ -1077,3 +1078,33 @@ func main() {
|
|||||||
// 2
|
// 2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
|
||||||
|
|
||||||
|
<p>返回exe,dll文件版本号(仅Window平台).</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetExeOrDllVersion(filePath string) (string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(v)
|
||||||
|
// Output:
|
||||||
|
// 3.9.10.19
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -40,6 +40,7 @@ import (
|
|||||||
- [RandStringSlice](#RandStringSlice)
|
- [RandStringSlice](#RandStringSlice)
|
||||||
- [RandBool](#RandBool)
|
- [RandBool](#RandBool)
|
||||||
- [RandBoolSlice](#RandBoolSlice)
|
- [RandBoolSlice](#RandBoolSlice)
|
||||||
|
- [RandNumberOfLength](#RandNumberOfLength)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -523,3 +524,28 @@ func main() {
|
|||||||
fmt.Println(result) // [true false] (random)
|
fmt.Println(result) // [true false] (random)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### <span id="RandNumberOfLength">RandNumberOfLength</span>
|
||||||
|
|
||||||
|
<p>生成指定长度的随机数</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandNumberOfLength(len int) int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i := random.RandNumberOfLength(2)
|
||||||
|
fmt.Println(i) // 21 (random number of length 2)
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -106,6 +106,9 @@ import (
|
|||||||
- [RightPadding](#RightPadding)
|
- [RightPadding](#RightPadding)
|
||||||
- [LeftPadding](#LeftPadding)
|
- [LeftPadding](#LeftPadding)
|
||||||
- [Frequency](#Frequency)
|
- [Frequency](#Frequency)
|
||||||
|
- [JoinFunc](#JoinFunc)
|
||||||
|
- [ConcatBy](#ConcatBy)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -2985,3 +2988,80 @@ func main() {
|
|||||||
// map[a:1 b:2 c:3]
|
// map[a:1 b:2 c:3]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="JoinFunc">JoinFunc</span>
|
||||||
|
|
||||||
|
<p>将切片元素用给定的分隔符连接成一个单一的字符串。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
|
||||||
|
return strings.ToUpper(s)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// A, B, C
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ConcatBy">ConcatBy</span>
|
||||||
|
|
||||||
|
<p>将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{Name: "Alice", Age: 30},
|
||||||
|
{Name: "Bob", Age: 25},
|
||||||
|
{Name: "Charlie", Age: 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
sep := Person{Name: " | ", Age: 0}
|
||||||
|
|
||||||
|
personConnector := func(a, b Person) Person {
|
||||||
|
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := slice.ConcatBy(people, sep, personConnector)
|
||||||
|
|
||||||
|
fmt.Println(result.Name)
|
||||||
|
fmt.Println(result.Age)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Alice | Bob | Charlie
|
||||||
|
// 90
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -35,6 +35,7 @@ import (
|
|||||||
- [XError_Info](#XError_Info)
|
- [XError_Info](#XError_Info)
|
||||||
- [XError_Error](#XError_Error)
|
- [XError_Error](#XError_Error)
|
||||||
- [TryUnwrap](#TryUnwrap)
|
- [TryUnwrap](#TryUnwrap)
|
||||||
|
- [TryCatch](#TryCatch)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -167,12 +168,12 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err1 := xerror.New("error").With("level", "high")
|
err1 := xerror.New("error").With("level", "high")
|
||||||
err2 := err1.Wrap(errors.New("invalid username"))
|
err2 := err1.Wrap(errors.New("invalid username"))
|
||||||
|
|
||||||
fmt.Println(err2.Error())
|
fmt.Println(err2.Error())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// error: invalid username
|
// error: invalid username
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -489,3 +490,56 @@ func main() {
|
|||||||
// true
|
// true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="TryCatch">TryCatch</span>
|
||||||
|
|
||||||
|
<p>简单实现的java风格异常处理(try-catch-finally)。try catch不符合go错误处理风格,谨慎使用。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewTryCatch(ctx context.Context) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Do()
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/xerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
calledFinally := false
|
||||||
|
calledCatch := false
|
||||||
|
|
||||||
|
tc := xerror.NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error in try block")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
calledCatch = true
|
||||||
|
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
fmt.Println(calledCatch)
|
||||||
|
fmt.Println(calledFinally)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
+103
-23
@@ -70,6 +70,8 @@ import (
|
|||||||
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
|
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
|
||||||
- [RsaEncryptOAEP](#RsaEncryptOAEP)
|
- [RsaEncryptOAEP](#RsaEncryptOAEP)
|
||||||
- [RsaDecryptOAEP](#RsaDecryptOAEP)
|
- [RsaDecryptOAEP](#RsaDecryptOAEP)
|
||||||
|
- [RsaSign](#RsaSign)
|
||||||
|
- [RsaVerifySign](#RsaVerifySign)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1061,13 +1063,13 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
str := "hello"
|
str := "hello"
|
||||||
key := "12345"
|
key := "12345"
|
||||||
|
|
||||||
hms := cryptor.HmacSha512WithBase64(str, key)
|
hms := cryptor.HmacSha512WithBase64(str, key)
|
||||||
fmt.Println(hms)
|
fmt.Println(hms)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
|
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1124,10 +1126,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
md5Str := cryptor.Md5StringWithBase64("hello")
|
md5Str := cryptor.Md5StringWithBase64("hello")
|
||||||
fmt.Println(md5Str)
|
fmt.Println(md5Str)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// XUFAKrxLKna5cZ2REBfFkg==
|
// XUFAKrxLKna5cZ2REBfFkg==
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1153,10 +1155,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
md5Str := cryptor.Md5Byte([]byte{'a'})
|
md5Str := cryptor.Md5Byte([]byte{'a'})
|
||||||
fmt.Println(md5Str)
|
fmt.Println(md5Str)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 0cc175b9c0f1b6a831c399e269772661
|
// 0cc175b9c0f1b6a831c399e269772661
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1182,10 +1184,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
md5Str := cryptor.Md5ByteWithBase64([]byte("hello"))
|
md5Str := cryptor.Md5ByteWithBase64([]byte("hello"))
|
||||||
fmt.Println(md5Str)
|
fmt.Println(md5Str)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// XUFAKrxLKna5cZ2REBfFkg==
|
// XUFAKrxLKna5cZ2REBfFkg==
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1268,10 +1270,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result := cryptor.Sha1WithBase64("hello")
|
result := cryptor.Sha1WithBase64("hello")
|
||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
|
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1328,10 +1330,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result := cryptor.Sha256WithBase64("hello")
|
result := cryptor.Sha256WithBase64("hello")
|
||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
|
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1388,10 +1390,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result := cryptor.Sha512WithBase64("hello")
|
result := cryptor.Sha512WithBase64("hello")
|
||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
|
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1607,3 +1609,81 @@ func main() {
|
|||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RsaSign">RsaSign</span>
|
||||||
|
|
||||||
|
<p>Signs the data with RSA algorithm.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/cryptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private.pem"
|
||||||
|
publicKey := "./rsa_public.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RsaVerifySign">RsaVerifySign</span>
|
||||||
|
|
||||||
|
<p>Verifies the signature of the data with RSA algorithm.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/cryptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := []byte("This is a test data for RSA signing")
|
||||||
|
hash := crypto.SHA256
|
||||||
|
|
||||||
|
privateKey := "./rsa_private.pem"
|
||||||
|
publicKey := "./rsa_public.pem"
|
||||||
|
|
||||||
|
signature, err := RsaSign(hash, data, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RsaVerifySign(hash, data, signature, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
- [ReadFile](#ReadFile)
|
- [ReadFile](#ReadFile)
|
||||||
- [ChunkRead](#ChunkRead)
|
- [ChunkRead](#ChunkRead)
|
||||||
- [ParallelChunkRead](#ParallelChunkRead)
|
- [ParallelChunkRead](#ParallelChunkRead)
|
||||||
|
- [GetExeOrDllVersion](#GetExeOrDllVersion)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -559,7 +560,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := fileutil.Zip("./test.zip", "./unzip/test.txt")
|
err := fileutil.UnZip("./test.zip", "./test.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
@@ -1075,3 +1076,36 @@ func main() {
|
|||||||
// 2
|
// 2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
|
||||||
|
|
||||||
|
<p>Get the version of exe or dll file on windows.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetExeOrDllVersion(filePath string) (string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(v)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.9.10.19
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -40,6 +40,7 @@ import (
|
|||||||
- [RandStringSlice](#RandStringSlice)
|
- [RandStringSlice](#RandStringSlice)
|
||||||
- [RandBool](#RandBool)
|
- [RandBool](#RandBool)
|
||||||
- [RandBoolSlice](#RandBoolSlice)
|
- [RandBoolSlice](#RandBoolSlice)
|
||||||
|
- [RandNumberOfLength](#RandNumberOfLength)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -524,3 +525,29 @@ func main() {
|
|||||||
fmt.Println(result) // [true false] (random)
|
fmt.Println(result) // [true false] (random)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RandNumberOfLength">RandNumberOfLength</span>
|
||||||
|
|
||||||
|
<p>Generates a random int number of specified length.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandNumberOfLength(len int) int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Signature:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i := random.RandNumberOfLength(2)
|
||||||
|
fmt.Println(i) // 21 (random number of length 2)
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -106,6 +106,10 @@ import (
|
|||||||
- [RightPadding](#RightPadding)
|
- [RightPadding](#RightPadding)
|
||||||
- [LeftPadding](#LeftPadding)
|
- [LeftPadding](#LeftPadding)
|
||||||
- [Frequency](#Frequency)
|
- [Frequency](#Frequency)
|
||||||
|
- [JoinFunc](#JoinFunc)
|
||||||
|
- [ConcatBy](#ConcatBy)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -2980,3 +2984,80 @@ func main() {
|
|||||||
// map[a:1 b:2 c:3]
|
// map[a:1 b:2 c:3]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="JoinFunc">JoinFunc</span>
|
||||||
|
|
||||||
|
<p>Joins the slice elements into a single string with the given separator.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
|
||||||
|
return strings.ToUpper(s)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// A, B, C
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ConcatBy">ConcatBy</span>
|
||||||
|
|
||||||
|
<p>Concats the elements of a slice into a single value using the provided separator and connector function.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{Name: "Alice", Age: 30},
|
||||||
|
{Name: "Bob", Age: 25},
|
||||||
|
{Name: "Charlie", Age: 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
sep := Person{Name: " | ", Age: 0}
|
||||||
|
|
||||||
|
personConnector := func(a, b Person) Person {
|
||||||
|
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := slice.ConcatBy(people, sep, personConnector)
|
||||||
|
|
||||||
|
fmt.Println(result.Name)
|
||||||
|
fmt.Println(result.Age)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Alice | Bob | Charlie
|
||||||
|
// 90
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -35,6 +35,7 @@ import (
|
|||||||
- [XError_Info](#XError_Info)
|
- [XError_Info](#XError_Info)
|
||||||
- [XError_Error](#XError_Error)
|
- [XError_Error](#XError_Error)
|
||||||
- [TryUnwrap](#TryUnwrap)
|
- [TryUnwrap](#TryUnwrap)
|
||||||
|
- [TryCatch](#TryCatch)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -166,12 +167,12 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err1 := xerror.New("error").With("level", "high")
|
err1 := xerror.New("error").With("level", "high")
|
||||||
err2 := err1.Wrap(errors.New("invalid username"))
|
err2 := err1.Wrap(errors.New("invalid username"))
|
||||||
|
|
||||||
fmt.Println(err2.Error())
|
fmt.Println(err2.Error())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// error: invalid username
|
// error: invalid username
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -487,3 +488,56 @@ func main() {
|
|||||||
// true
|
// true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="TryCatch">TryCatch</span>
|
||||||
|
|
||||||
|
<p>Simple simulation of Java-style try-catch. It does not align with Go's error-handling philosophy. It is recommended to use it with caution.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewTryCatch(ctx context.Context) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
|
||||||
|
|
||||||
|
func (tc *TryCatch) Do()
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/xerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
calledFinally := false
|
||||||
|
calledCatch := false
|
||||||
|
|
||||||
|
tc := xerror.NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error message ")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
calledCatch = true
|
||||||
|
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
fmt.Println(calledCatch)
|
||||||
|
fmt.Println(calledFinally)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
+1
-2
@@ -14,6 +14,7 @@ import (
|
|||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -23,8 +24,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/validator"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileReader is a reader supporting offset seeking and reading one
|
// FileReader is a reader supporting offset seeking and reading one
|
||||||
|
|||||||
@@ -192,6 +192,27 @@ func ExampleListFileNames() {
|
|||||||
// [assert.go assert_test.go error_join.go]
|
// [assert.go assert_test.go error_join.go]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleMiMeType() {
|
||||||
|
fname := "./test.txt"
|
||||||
|
file, _ := os.Create(fname)
|
||||||
|
|
||||||
|
_, err := file.WriteString("hello\nworld")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, _ := os.Open(fname)
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
mimeType := MiMeType(f)
|
||||||
|
fmt.Println(mimeType)
|
||||||
|
|
||||||
|
os.Remove(fname)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// application/octet-stream
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleZip() {
|
func ExampleZip() {
|
||||||
srcFile := "./test.txt"
|
srcFile := "./test.txt"
|
||||||
CreateFile(srcFile)
|
CreateFile(srcFile)
|
||||||
@@ -214,24 +235,20 @@ func ExampleZip() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleUnZip() {
|
func ExampleUnZip() {
|
||||||
fname := "./test.txt"
|
zipFile := "./testdata/file.go.zip"
|
||||||
file, _ := os.Create(fname)
|
|
||||||
|
|
||||||
_, err := file.WriteString("hello\nworld")
|
err := UnZip(zipFile, "./testdata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f, _ := os.Open(fname)
|
exist := IsExist("./testdata/file.go")
|
||||||
defer f.Close()
|
fmt.Println(exist)
|
||||||
|
|
||||||
mimeType := MiMeType(f)
|
os.Remove("./testdata/file.go")
|
||||||
fmt.Println(mimeType)
|
|
||||||
|
|
||||||
os.Remove(fname)
|
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// application/octet-stream
|
// true
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleZipAppendEntry() {
|
func ExampleZipAppendEntry() {
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tagVS_FIXEDFILEINFO 参考结构体https://learn.microsoft.com/zh-cn/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
|
||||||
|
type tagVS_FIXEDFILEINFO struct {
|
||||||
|
Signature uint32
|
||||||
|
StructVersion uint32
|
||||||
|
FileVersionMS uint32
|
||||||
|
FileVersionLS uint32
|
||||||
|
ProductVersionMS uint32
|
||||||
|
ProductVersionLS uint32
|
||||||
|
FileFlagsMask uint32
|
||||||
|
FileFlags uint32
|
||||||
|
FileOS uint32
|
||||||
|
FileType uint32
|
||||||
|
FileSubtype uint32
|
||||||
|
FileDateMS uint32
|
||||||
|
FileDateLS uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExeOrDllVersion get the version of exe or dll file on windows.
|
||||||
|
// Play: todo
|
||||||
|
func GetExeOrDllVersion(filePath string) (string, error) {
|
||||||
|
// 加载系统dll
|
||||||
|
versionDLL := syscall.NewLazyDLL("version.dll")
|
||||||
|
getFileVersionInfoSize := versionDLL.NewProc("GetFileVersionInfoSizeW")
|
||||||
|
getFileVersionInfo := versionDLL.NewProc("GetFileVersionInfoW")
|
||||||
|
verQueryValue := versionDLL.NewProc("VerQueryValueW")
|
||||||
|
|
||||||
|
// 转换路径为UTF-16
|
||||||
|
filePathPtr, err := syscall.UTF16PtrFromString(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取version信息大小
|
||||||
|
size, _, err := getFileVersionInfoSize.Call(
|
||||||
|
uintptr(unsafe.Pointer(filePathPtr)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if size == 0 {
|
||||||
|
return "", fmt.Errorf("unable to obtain version information size: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载version信息
|
||||||
|
data := make([]byte, size)
|
||||||
|
ret, _, err := getFileVersionInfo.Call(uintptr(unsafe.Pointer(filePathPtr)), 0, size, uintptr(unsafe.Pointer(&data[0])))
|
||||||
|
if ret == 0 {
|
||||||
|
return "", fmt.Errorf("unable to obtain version information: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询version信息
|
||||||
|
var fixedInfo *tagVS_FIXEDFILEINFO
|
||||||
|
var fixedInfoLen uint32
|
||||||
|
u16, err := syscall.UTF16PtrFromString(`\`)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
|
||||||
|
}
|
||||||
|
ret, _, err = verQueryValue.Call(
|
||||||
|
uintptr(unsafe.Pointer(&data[0])),
|
||||||
|
uintptr(unsafe.Pointer(u16)),
|
||||||
|
uintptr(unsafe.Pointer(&fixedInfo)),
|
||||||
|
uintptr(unsafe.Pointer(&fixedInfoLen)),
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
return "", fmt.Errorf("unable to query version information: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换结构体
|
||||||
|
major := fixedInfo.FileVersionMS >> 16
|
||||||
|
minor := fixedInfo.FileVersionMS & 0xFFFF
|
||||||
|
build := fixedInfo.FileVersionLS >> 16
|
||||||
|
revision := fixedInfo.FileVersionLS & 0xFFFF
|
||||||
|
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", major, minor, build, revision), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestGetExeOrDllVersion(t *testing.T) {
|
||||||
|
v, err := GetExeOrDllVersion(`C:\Windows\System32\cmd.exe`)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Log(v)
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
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/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (om *OrderedMap[K, V]) Set(key K, value V) {
|
|||||||
defer om.mu.Unlock()
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
if elem, ok := om.index[key]; ok {
|
if elem, ok := om.index[key]; ok {
|
||||||
elem.Value = value
|
om.data[key] = value
|
||||||
om.order.MoveToBack(elem)
|
om.order.MoveToBack(elem)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ func TestOrderedMap_Set_Get(t *testing.T) {
|
|||||||
assert.Equal(1, val)
|
assert.Equal(1, val)
|
||||||
assert.Equal(true, ok)
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
om.Set("a", 2)
|
||||||
|
val, _ = om.Get("a")
|
||||||
|
assert.Equal(2, val)
|
||||||
|
|
||||||
val, ok = om.Get("d")
|
val, ok = om.Get("d")
|
||||||
assert.Equal(false, ok)
|
assert.Equal(false, ok)
|
||||||
assert.Equal(0, val)
|
assert.Equal(0, val)
|
||||||
|
|||||||
@@ -221,6 +221,22 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsyncSendRequest send http request with goroutine, pop response and error to channels
|
||||||
|
func (client *HttpClient) AsyncSendRequest(request *HttpRequest, respChan chan *http.Response, errChan chan error) {
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
errChan <- fmt.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resp, err := client.SendRequest(request)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
respChan <- resp
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeResponse decode response into target object.
|
// DecodeResponse decode response into target object.
|
||||||
// Play: https://go.dev/play/p/jUSgynekH7G
|
// Play: https://go.dev/play/p/jUSgynekH7G
|
||||||
func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error {
|
func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error {
|
||||||
|
|||||||
@@ -373,6 +373,45 @@ func TestSendRequestWithFilePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHttpClient_AsyncSendRequest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestHttpClient_Get")
|
||||||
|
|
||||||
|
request := &HttpRequest{
|
||||||
|
RawURL: "https://jsonplaceholder.typicode.com/todos/1",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := NewHttpClient()
|
||||||
|
respCh := make(chan *http.Response, 20)
|
||||||
|
errCh := make(chan error, 20)
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
httpClient.AsyncSendRequest(request, respCh, errCh)
|
||||||
|
}
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
select {
|
||||||
|
case resp := <-respCh:
|
||||||
|
type Todo struct {
|
||||||
|
UserId int `json:"userId"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Completed bool `json:"completed"`
|
||||||
|
}
|
||||||
|
var todo Todo
|
||||||
|
err := httpClient.DecodeResponse(resp, &todo)
|
||||||
|
if err != nil {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.Equal(1, todo.Id)
|
||||||
|
case err := <-errCh:
|
||||||
|
if err != nil {
|
||||||
|
t.Log("net error: ", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProxy(t *testing.T) {
|
func TestProxy(t *testing.T) {
|
||||||
config := &HttpClientConfig{
|
config := &HttpClientConfig{
|
||||||
HandshakeTimeout: 20 * time.Second,
|
HandshakeTimeout: 20 * time.Second,
|
||||||
|
|||||||
@@ -327,3 +327,13 @@ func UUIdV4() (string, error) {
|
|||||||
|
|
||||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandNumberOfLength 生成一个长度为len的随机数
|
||||||
|
// Play: todo
|
||||||
|
func RandNumberOfLength(len int) int {
|
||||||
|
m := int(math.Pow10(len) - 1)
|
||||||
|
i := int(math.Pow10(len - 1))
|
||||||
|
ret := rand.Intn(m-i+1) + i
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package random
|
|||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -361,3 +362,9 @@ func TestRandBoolSlice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
func TestRandNumberOfLength(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
randi := RandNumberOfLength(6)
|
||||||
|
assert := internal.NewAssert(t, "TestRandNumberOfLength")
|
||||||
|
assert.Equal(6, len(strconv.Itoa(randi)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1398,3 +1398,35 @@ func Frequency[T comparable](slice []T) map[T]int {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinFunc joins the slice elements into a single string with the given separator.
|
||||||
|
// Play: todo
|
||||||
|
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for i, v := range slice {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(sep)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprint(transform(v)))
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConcatBy concats the elements of a slice into a single value using the provided separator and connector function.
|
||||||
|
// Play: todo
|
||||||
|
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T {
|
||||||
|
var result T
|
||||||
|
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range slice {
|
||||||
|
result = connector(result, v)
|
||||||
|
if i < len(slice)-1 {
|
||||||
|
result = connector(result, sep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -195,18 +195,14 @@ func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T
|
|||||||
chunks = append(chunks, slice[i:end])
|
chunks = append(chunks, slice[i:end])
|
||||||
}
|
}
|
||||||
|
|
||||||
type resultChunk struct {
|
resultCh := make(chan resultChunk[T], numThreads)
|
||||||
index int
|
|
||||||
data []T
|
|
||||||
}
|
|
||||||
resultCh := make(chan resultChunk, numThreads)
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for i, chunk := range chunks {
|
for i, chunk := range chunks {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(index int, chunk []T) {
|
go func(index int, chunk []T) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
resultCh <- resultChunk{index, removeDuplicate(chunk, comparator)}
|
resultCh <- resultChunk[T]{index, removeDuplicate(chunk, comparator)}
|
||||||
}(i, chunk)
|
}(i, chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1261,3 +1261,42 @@ func ExampleFrequency() {
|
|||||||
// Output:
|
// Output:
|
||||||
// map[a:1 b:2 c:3]
|
// map[a:1 b:2 c:3]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleJoinFunc() {
|
||||||
|
result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
|
||||||
|
return strings.ToUpper(s)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// A, B, C
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleConcatBy() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{Name: "Alice", Age: 30},
|
||||||
|
{Name: "Bob", Age: 25},
|
||||||
|
{Name: "Charlie", Age: 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
sep := Person{Name: " | ", Age: 0}
|
||||||
|
|
||||||
|
personConnector := func(a, b Person) Person {
|
||||||
|
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ConcatBy(people, sep, personConnector)
|
||||||
|
|
||||||
|
fmt.Println(result.Name)
|
||||||
|
fmt.Println(result.Age)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Alice | Bob | Charlie
|
||||||
|
// 90
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ import (
|
|||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// resultChunk is used to store the intermediate results of UniqueByConcurrent.
|
||||||
|
// It is defined separately to be compatible with versions of go up to 1.20.
|
||||||
|
type resultChunk[T comparable] struct {
|
||||||
|
index int
|
||||||
|
data []T
|
||||||
|
}
|
||||||
|
|
||||||
// sliceValue return the reflect value of a slice
|
// sliceValue return the reflect value of a slice
|
||||||
func sliceValue(slice any) reflect.Value {
|
func sliceValue(slice any) reflect.Value {
|
||||||
v := reflect.ValueOf(slice)
|
v := reflect.ValueOf(slice)
|
||||||
|
|||||||
@@ -1816,3 +1816,81 @@ func TestFrequency(t *testing.T) {
|
|||||||
assert.Equal(expected, result)
|
assert.Equal(expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJoinFunc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestJoinFunc")
|
||||||
|
|
||||||
|
t.Run("basic case", func(t *testing.T) {
|
||||||
|
result := JoinFunc([]int{1, 2, 3}, ", ", func(i int) int {
|
||||||
|
return i * 2
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := "2, 4, 6"
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
result := JoinFunc([]int{}, ", ", func(i int) int {
|
||||||
|
return i * 2
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal("", result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single element slice", func(t *testing.T) {
|
||||||
|
result := JoinFunc([]int{1}, ", ", func(i int) int {
|
||||||
|
return i * 2
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal("2", result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("string slice", func(t *testing.T) {
|
||||||
|
result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
|
||||||
|
return strings.ToUpper(s)
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := "A, B, C"
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConcatBy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestConcatBy")
|
||||||
|
|
||||||
|
t.Run("Join strings", func(t *testing.T) {
|
||||||
|
result := ConcatBy([]string{"Hello", "World"}, ", ", func(a, b string) string {
|
||||||
|
return a + b
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := "Hello, World"
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Join Person struct", func(t *testing.T) {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{Name: "Alice", Age: 30},
|
||||||
|
{Name: "Bob", Age: 25},
|
||||||
|
{Name: "Charlie", Age: 35},
|
||||||
|
}
|
||||||
|
sep := Person{Name: " | ", Age: 0}
|
||||||
|
|
||||||
|
personConnector := func(a, b Person) Person {
|
||||||
|
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ConcatBy(people, sep, personConnector)
|
||||||
|
|
||||||
|
assert.Equal("Alice | Bob | Charlie", result.Name)
|
||||||
|
assert.Equal(90, result.Age)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package xerror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TryCatch is a struct to handle try-catch-finally block.
|
||||||
|
// This implementation is merely a simulation of Java-style try-catch and does not align with Go's error-handling philosophy. It is recommended to use it with caution.
|
||||||
|
type TryCatch struct {
|
||||||
|
ctx context.Context
|
||||||
|
tryFunc func(ctx context.Context) error
|
||||||
|
catchFunc func(ctx context.Context, err error)
|
||||||
|
finallyFunc func(ctx context.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTryCatch creates a new TryCatch instance.
|
||||||
|
func NewTryCatch(ctx context.Context) *TryCatch {
|
||||||
|
return &TryCatch{
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try sets the try function.
|
||||||
|
func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch {
|
||||||
|
tc.tryFunc = tryFunc
|
||||||
|
return tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch sets the catch function.
|
||||||
|
func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch {
|
||||||
|
tc.catchFunc = catchFunc
|
||||||
|
return tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally sets the finally function.
|
||||||
|
func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch {
|
||||||
|
tc.finallyFunc = finallyFunc
|
||||||
|
return tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do executes the try-catch-finally block.
|
||||||
|
func (tc *TryCatch) Do() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if tc.catchFunc != nil {
|
||||||
|
err := fmt.Errorf("panic: %v", r)
|
||||||
|
tc.catchFunc(tc.ctx, WrapCatchError(err, "Recovered from panic"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.finallyFunc != nil {
|
||||||
|
tc.finallyFunc(tc.ctx)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if tc.ctx.Err() != nil {
|
||||||
|
if tc.catchFunc != nil {
|
||||||
|
tc.catchFunc(tc.ctx, WrapCatchError(tc.ctx.Err(), "Context cancelled or timed out"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.tryFunc != nil {
|
||||||
|
if err := tc.tryFunc(tc.ctx); err != nil {
|
||||||
|
if tc.catchFunc != nil {
|
||||||
|
tc.catchFunc(tc.ctx, WrapCatchError(err, "Error in try block"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CatchError is an error type to handle try-catch-finally block.
|
||||||
|
type CatchError struct {
|
||||||
|
Msg string
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Cause error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error message.
|
||||||
|
func (e *CatchError) Error() string {
|
||||||
|
return fmt.Sprintf("%s at %s:%d - Cause: %v", e.Msg, e.File, e.Line, e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapCatchError wraps an error with message, file, and line.
|
||||||
|
func WrapCatchError(err error, msg string) *CatchError {
|
||||||
|
_, file, line, ok := runtime.Caller(2)
|
||||||
|
if !ok {
|
||||||
|
file = "unknown"
|
||||||
|
line = 0
|
||||||
|
}
|
||||||
|
return &CatchError{
|
||||||
|
Msg: msg,
|
||||||
|
File: file,
|
||||||
|
Line: line,
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package xerror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTryCatchSuccess(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchSuccess")
|
||||||
|
|
||||||
|
counter := 0
|
||||||
|
calledCatch := false
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
tc := NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
counter++
|
||||||
|
return nil
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
calledCatch = true
|
||||||
|
t.Errorf("Catch should not be called")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal(1, counter)
|
||||||
|
assert.Equal(false, calledCatch)
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchError")
|
||||||
|
|
||||||
|
var catchedError error
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
tc := NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
catchedError = errors.New("catched error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal("catched error", catchedError.Error())
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchPanic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchPanic")
|
||||||
|
|
||||||
|
var catchedError error
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
tc := NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
panic("panic info")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
catchedError = errors.New("catched error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal("catched error", catchedError.Error())
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchContextCancelled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchContextCancelled")
|
||||||
|
|
||||||
|
var catchedError error
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
tc := NewTryCatch(ctx)
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
catchedError = errors.New("catched error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal("catched error", catchedError.Error())
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchContextTimeout(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchContextTimeout")
|
||||||
|
|
||||||
|
var catchedError error
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 0)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tc := NewTryCatch(ctx)
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
catchedError = errors.New("catched error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal("catched error", catchedError.Error())
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchContextError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchContextError")
|
||||||
|
|
||||||
|
var catchedError error
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 0)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tc := NewTryCatch(ctx)
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
catchedError = errors.New("catched error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal("catched error", catchedError.Error())
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatchNoCatch(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestTryCatchNoCatch")
|
||||||
|
|
||||||
|
calledFinally := false
|
||||||
|
|
||||||
|
tc := NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error")
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
assert.Equal(true, calledFinally)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package xerror
|
package xerror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -61,7 +62,7 @@ func ExampleXError_StackTrace() {
|
|||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// github.com/duke-git/lancet/v2/xerror.ExampleXError_StackTrace
|
// github.com/duke-git/lancet/v2/xerror.ExampleXError_StackTrace
|
||||||
// 52
|
// 53
|
||||||
// true
|
// true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,3 +155,27 @@ func ExampleTryUnwrap() {
|
|||||||
// 42
|
// 42
|
||||||
// true
|
// true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleTryCatch() {
|
||||||
|
calledFinally := false
|
||||||
|
calledCatch := false
|
||||||
|
|
||||||
|
tc := NewTryCatch(context.Background())
|
||||||
|
|
||||||
|
tc.Try(func(ctx context.Context) error {
|
||||||
|
return errors.New("error message")
|
||||||
|
}).Catch(func(ctx context.Context, err error) {
|
||||||
|
calledCatch = true
|
||||||
|
// Error in try block at /path/xxx.go:174 - Cause: error message
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
}).Finally(func(ctx context.Context) {
|
||||||
|
calledFinally = true
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
fmt.Println(calledCatch)
|
||||||
|
fmt.Println(calledFinally)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user