mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
7 Commits
v2.2.1
...
8ced7e887f
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ced7e887f | |||
| e839af3ef9 | |||
| 23382b2b76 | |||
| 850800a233 | |||
| a8761eefb0 | |||
| cbf8cfdffa | |||
| 286a187942 |
@@ -493,10 +493,14 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/xS1eS2ejGew)]
|
||||
- **<big>BetweenSeconds</big>** : returns the number of seconds between two times.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BetweenSeconds)]
|
||||
[[play](https://go.dev/play/p/n3YDRyfyXJu)]
|
||||
- **<big>DayOfYear</big>** : returns which day of the year the parameter date `t` is.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#DayOfYear)]
|
||||
[[play](https://go.dev/play/p/0hjqhTwFNlH)]
|
||||
- **<big>IsWeekend</big>** : checks if passed time is weekend or not.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#IsWeekend)]
|
||||
[[play](https://go.dev/play/p/cupRM5aZOIY)]
|
||||
|
||||
|
||||
### 8. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph.
|
||||
|
||||
@@ -603,8 +607,11 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||
- **<big>WriteBytesToFile</big>** : write bytes to target file.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#WriteBytesToFile)]
|
||||
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
||||
- **<big>WriteStringToFile</big>** : write string to target file.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/fileutil.md#WriteStringToFile)]
|
||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||
|
||||
|
||||
### 10. Formatter contains some functions for data formatting.
|
||||
|
||||
@@ -814,8 +821,11 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
[[play](https://go.dev/play/p/EjcZxfY7G_g)]
|
||||
- **<big>Cos</big>** : return the cosine of the radian argument.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Cos)]
|
||||
[[play](https://go.dev/play/p/Sm89LoIfvFq)]
|
||||
- **<big>Sin</big>** : return the sine of the radian argument.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Sin)]
|
||||
[[play](https://go.dev/play/p/TWMQlMywDsP)]
|
||||
|
||||
|
||||
### 14. Netutil package contains functions to get net information and send http request.
|
||||
|
||||
@@ -1307,8 +1317,10 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/2OAjgbmAqHZ)]
|
||||
- **<big>ContainsAll</big>** : return true if target string contains all the substrings.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#ContainsAll)]
|
||||
[[play](https://go.dev/play/p/KECtK2Os4zq)]
|
||||
- **<big>ContainsAny</big>** : return true if target string contains any one of the substrings.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#ContainsAny)]
|
||||
[[play](https://go.dev/play/p/dZGSSMB3LXE)]
|
||||
- **<big>IsString</big>** : checks if the parameter value data type is string or not.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#IsString)]
|
||||
[[play](https://go.dev/play/p/IOgq7oF9ERm)]
|
||||
@@ -1392,6 +1404,8 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/ZNL6o4SkYQ7)]
|
||||
- **<big>HideString</big>** : Hide some chars in source string with param `replaceChar`.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#HideString)]
|
||||
[[play](https://go.dev/play/p/pzbaIVCTreZ)]
|
||||
|
||||
|
||||
### 21. System package contain some functions about os, runtime, shell command.
|
||||
|
||||
|
||||
@@ -495,10 +495,14 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[play](https://go.dev/play/p/xS1eS2ejGew)]
|
||||
- **<big>BetweenSeconds</big>** : 返回两个时间的间隔秒数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#BetweenSeconds)]
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#BetweenSeconds)]
|
||||
- **<big>DayOfYear</big>** : 返回参数日期是一年中的第几天。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#DayOfYear)]
|
||||
[[play](https://go.dev/play/p/0hjqhTwFNlH)]
|
||||
- **<big>IsWeekend</big>** : 判断日期是否是周末。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#IsWeekend)]
|
||||
[[play](https://go.dev/play/p/cupRM5aZOIY)]
|
||||
|
||||
|
||||
### 8. datastructure 包含一些普通的数据结构实现。例如:list, linklist, stack, queue, set, tree, graph.
|
||||
|
||||
@@ -605,8 +609,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||
- **<big>WriteBytesToFile</big>** : 将bytes写入文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#WriteBytesToFile)]
|
||||
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
||||
- **<big>WriteStringToFile</big>** : 将字符串写入文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/fileutil_zh-CN.md#WriteStringToFile)]
|
||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||
|
||||
|
||||
|
||||
### 10. formatter 格式化器包含一些数据格式化处理方法。
|
||||
|
||||
@@ -816,8 +824,11 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
[[play](https://go.dev/play/p/EjcZxfY7G_g)]
|
||||
- **<big>Cos</big>** : 计算弧度的余弦值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Cos)]
|
||||
[[play](https://go.dev/play/p/Sm89LoIfvFq)]
|
||||
- **<big>Sin</big>** : 计算弧度的正弦值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Sin)]
|
||||
[[play](https://go.dev/play/p/TWMQlMywDsP)]
|
||||
|
||||
|
||||
### 14. netutil 网络包支持获取 ip 地址,发送 http 请求。
|
||||
|
||||
@@ -1312,8 +1323,10 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/2OAjgbmAqHZ)]
|
||||
- **<big>ContainsAll</big>** : 判断字符串是否包括全部给定的子字符串切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#ContainsAll)]
|
||||
[[play](https://go.dev/play/p/KECtK2Os4zq)]
|
||||
- **<big>ContainsAny</big>** : 判断字符串是否包括给定的子字符串切片中任意一个子字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#ContainsAny)]
|
||||
[[play](https://go.dev/play/p/dZGSSMB3LXE)]
|
||||
- **<big>IsString</big>** : 判断传入参数的数据类型是否为字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#IsString)]
|
||||
[[play](https://go.dev/play/p/IOgq7oF9ERm)]
|
||||
@@ -1397,6 +1410,8 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
[[play](https://go.dev/play/p/ZNL6o4SkYQ7)]
|
||||
- **<big>HideString</big>** : 隐藏源字符串中的一些字符。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#HideString)]
|
||||
[[play](https://go.dev/play/p/pzbaIVCTreZ)]
|
||||
|
||||
|
||||
### 21. system 包含 os, runtime, shell command 的相关函数。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. eg. list, linklist, stack, queue, tree, graph.
|
||||
// Package datastructure implements some data structure. hashmap structure.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. eg. list, linklist, stack, queue, tree, graph.
|
||||
// Package datastructure implements some data structure. MaxHeap is a binary max heap.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. eg. list, linklist, stack, queue, tree, graph.
|
||||
// Package datastructure contains some data structure. list is a linear table, implemented with slice.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. eg. list, linklist, stack, queue, tree, graph.
|
||||
// Package datastructure implements some data structure.
|
||||
package datastructure
|
||||
|
||||
// LinkNode is a linkedlist node, which have a Value and Pre points to previous node, Next points to a next node of the link.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
||||
package datastructure
|
||||
|
||||
// Set is a data container, like slice, but element of set is not duplicate
|
||||
// Set is a data container, like slice, but element of set is not duplicate.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// NewSet return a instance of set
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
|
||||
package datastructure
|
||||
|
||||
import "errors"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. BSTree is binary search tree.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
|
||||
@@ -232,14 +232,14 @@ func IsLeapYear(year int) bool {
|
||||
}
|
||||
|
||||
// BetweenSeconds returns the number of seconds between two times.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/n3YDRyfyXJu
|
||||
func BetweenSeconds(t1 time.Time, t2 time.Time) int64 {
|
||||
index := t2.Unix() - t1.Unix()
|
||||
return index
|
||||
}
|
||||
|
||||
// DayOfYear returns which day of the year the parameter date `t` is.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/0hjqhTwFNlH
|
||||
func DayOfYear(t time.Time) int {
|
||||
y, m, d := t.Date()
|
||||
firstDay := time.Date(y, 1, 1, 0, 0, 0, 0, t.Location())
|
||||
@@ -249,7 +249,7 @@ func DayOfYear(t time.Time) int {
|
||||
}
|
||||
|
||||
// IsWeekend checks if passed time is weekend or not.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/cupRM5aZOIY
|
||||
func IsWeekend(t time.Time) bool {
|
||||
return time.Saturday == t.Weekday() || time.Sunday == t.Weekday()
|
||||
}
|
||||
|
||||
+2
-2
@@ -1163,8 +1163,8 @@ import (
|
||||
|
||||
func main() {
|
||||
today := time.Now()
|
||||
tomorrow := AddDay(today, 1)
|
||||
yesterday := AddDay(today, -1)
|
||||
tomorrow := datetime.AddDay(today, 1)
|
||||
yesterday := datetime.AddDay(today, -1)
|
||||
|
||||
result1 := datetime.BetweenSeconds(today, tomorrow)
|
||||
result2 := datetime.BetweenSeconds(today, yesterday)
|
||||
|
||||
@@ -1162,8 +1162,8 @@ import (
|
||||
|
||||
func main() {
|
||||
today := time.Now()
|
||||
tomorrow := AddDay(today, 1)
|
||||
yesterday := AddDay(today, -1)
|
||||
tomorrow := datetime.AddDay(today, 1)
|
||||
yesterday := datetime.AddDay(today, -1)
|
||||
|
||||
result1 := datetime.BetweenSeconds(today, tomorrow)
|
||||
result2 := datetime.BetweenSeconds(today, yesterday)
|
||||
|
||||
+72
-20
@@ -208,7 +208,11 @@ func Zip(fpath string, destPath string) error {
|
||||
archive := zip.NewWriter(zipFile)
|
||||
defer archive.Close()
|
||||
|
||||
err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
|
||||
return addFileToArchive(fpath, archive)
|
||||
}
|
||||
|
||||
func addFileToArchive(fpath string, archive *zip.Writer) error {
|
||||
err := filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,32 +228,22 @@ func Zip(fpath string, destPath string) error {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(writer, file)
|
||||
if err != nil {
|
||||
if _, err := io.Copy(writer, file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UnZip unzip the file and save it to destPath.
|
||||
@@ -302,6 +296,64 @@ func UnZip(zipFile string, destPath string) error {
|
||||
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 = addFileToArchive(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, "..") {
|
||||
@@ -402,7 +454,7 @@ func MTime(filepath string) (int64, error) {
|
||||
return f.ModTime().Unix(), nil
|
||||
}
|
||||
|
||||
// MTime returns file sha value, param `shaType` should be 1, 256 or 512.
|
||||
// Sha returns file sha value, param `shaType` should be 1, 256 or 512.
|
||||
// Play: https://go.dev/play/p/VfEEcO2MJYf
|
||||
func Sha(filepath string, shaType ...int) (string, error) {
|
||||
file, err := os.Open(filepath)
|
||||
@@ -455,7 +507,7 @@ func ReadCsvFile(filepath string) ([][]string, error) {
|
||||
}
|
||||
|
||||
// WriteStringToFile write string to target file.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/GhLS6d8lH_g
|
||||
func WriteStringToFile(filepath string, content string, append bool) error {
|
||||
flag := os.O_RDWR | os.O_CREATE
|
||||
if append {
|
||||
@@ -473,7 +525,7 @@ func WriteStringToFile(filepath string, content string, append bool) error {
|
||||
}
|
||||
|
||||
// WriteBytesToFile write bytes to target file.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/s7QlDxMj3P8
|
||||
func WriteBytesToFile(filepath string, content []byte) error {
|
||||
f, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
|
||||
@@ -224,6 +224,30 @@ func ExampleUnZip() {
|
||||
// application/octet-stream
|
||||
}
|
||||
|
||||
func ExampleZipAppendEntry() {
|
||||
zipFile := "./test.zip"
|
||||
CopyFile("./testdata/file.go.zip", zipFile)
|
||||
|
||||
ZipAppendEntry("./testdata", zipFile)
|
||||
|
||||
unZipPath := "./unzip"
|
||||
UnZip(zipFile, unZipPath)
|
||||
|
||||
fmt.Println(IsExist("./unzip/file.go"))
|
||||
fmt.Println(IsExist("./unzip/testdata/file.go.zip"))
|
||||
fmt.Println(IsExist("./unzip/testdata/test.csv"))
|
||||
fmt.Println(IsExist("./unzip/testdata/test.txt"))
|
||||
|
||||
os.Remove(zipFile)
|
||||
os.RemoveAll(unZipPath)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleIsZipFile() {
|
||||
result1 := IsZipFile("./file.go")
|
||||
result2 := IsZipFile("./testdata/file.go.zip")
|
||||
|
||||
@@ -191,6 +191,45 @@ func TestZipAndUnZip(t *testing.T) {
|
||||
os.RemoveAll(unZipPath)
|
||||
}
|
||||
|
||||
func TestZipAppendEntry(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestZipAppendEntry")
|
||||
|
||||
zipFile := "./text.zip"
|
||||
err := CopyFile("./testdata/file.go.zip", zipFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
||||
|
||||
_, err = file.WriteString("hello\nworld")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
file.Close()
|
||||
|
||||
err = ZipAppendEntry(srcFile, zipFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = ZipAppendEntry("./testdata", zipFile)
|
||||
assert.IsNil(err)
|
||||
|
||||
unZipPath := "./unzip"
|
||||
err = UnZip(zipFile, unZipPath)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, IsExist("./unzip/text.txt"))
|
||||
assert.Equal(true, IsExist("./unzip/file.go"))
|
||||
assert.Equal(true, IsExist("./unzip/testdata/file.go.zip"))
|
||||
assert.Equal(true, IsExist("./unzip/testdata/test.csv"))
|
||||
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")
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ func lcm[T constraints.Integer](a, b T) T {
|
||||
}
|
||||
|
||||
// Cos returns the cosine of the radian argument.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/Sm89LoIfvFq
|
||||
func Cos(radian float64, precision ...int) float64 {
|
||||
t := 1.0 / (2.0 * math.Pi)
|
||||
radian *= t
|
||||
@@ -319,7 +319,7 @@ func Cos(radian float64, precision ...int) float64 {
|
||||
}
|
||||
|
||||
// Cos returns the sine of the radian argument.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/TWMQlMywDsP
|
||||
func Sin(radian float64, precision ...int) float64 {
|
||||
return Cos((math.Pi / 2) - radian)
|
||||
}
|
||||
|
||||
+82
-5
@@ -19,8 +19,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -94,6 +96,7 @@ type HttpRequest struct {
|
||||
Headers http.Header
|
||||
QueryParams url.Values
|
||||
FormData url.Values
|
||||
File *File
|
||||
Body []byte
|
||||
}
|
||||
|
||||
@@ -186,7 +189,11 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
|
||||
}
|
||||
|
||||
if request.FormData != nil {
|
||||
client.setFormData(req, request.FormData)
|
||||
if request.File != nil {
|
||||
err = client.setFormData(req, request.FormData, setFile(request.File))
|
||||
} else {
|
||||
err = client.setFormData(req, request.FormData, nil)
|
||||
}
|
||||
}
|
||||
|
||||
client.Request = req
|
||||
@@ -251,10 +258,80 @@ func (client *HttpClient) setQueryParam(req *http.Request, reqUrl string, queryP
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *HttpClient) setFormData(req *http.Request, values url.Values) {
|
||||
formData := []byte(values.Encode())
|
||||
req.Body = io.NopCloser(bytes.NewReader(formData))
|
||||
req.ContentLength = int64(len(formData))
|
||||
// setFormData set http request FormData param
|
||||
func (client *HttpClient) setFormData(req *http.Request, values url.Values, setFile SetFileFunc) error {
|
||||
if setFile != nil {
|
||||
err := setFile(req, values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
formData := []byte(values.Encode())
|
||||
req.Body = io.NopCloser(bytes.NewReader(formData))
|
||||
req.ContentLength = int64(len(formData))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetFileFunc func(req *http.Request, values url.Values) error
|
||||
|
||||
// File struct is a combination of file attributes
|
||||
type File struct {
|
||||
Content []byte
|
||||
Path string
|
||||
FieldName string
|
||||
FileName string
|
||||
}
|
||||
|
||||
// setFile set parameters for http request formdata file upload
|
||||
func setFile(f *File) SetFileFunc {
|
||||
return func(req *http.Request, values url.Values) error {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
for key, vals := range values {
|
||||
for _, val := range vals {
|
||||
err := writer.WriteField(key, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.Content != nil {
|
||||
part, err := writer.CreateFormFile(f.FieldName, f.FileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
part.Write(f.Content)
|
||||
} else if f.Path != "" {
|
||||
file, err := os.Open(f.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
part, err := writer.CreateFormFile(f.FieldName, f.FileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := writer.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Body = io.NopCloser(body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.ContentLength = int64(body.Len())
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validateRequest check if a request has url, and valid method.
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -245,3 +249,108 @@ func TestStructToUrlValues(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,3 +114,27 @@ 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -123,3 +123,14 @@ func ExampleUUIdV4() {
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleRandUniqueIntSlice() {
|
||||
result := RandUniqueIntSlice(5, 0, 10)
|
||||
|
||||
if len(result) == 5 {
|
||||
fmt.Println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
}
|
||||
|
||||
@@ -103,3 +103,36 @@ func TestUUIdV4(t *testing.T) {
|
||||
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
|
||||
}
|
||||
|
||||
+3
-3
@@ -504,7 +504,7 @@ func Trim(str string, characterMask ...string) string {
|
||||
|
||||
// HideString hide some chars in source string with param `replaceChar`.
|
||||
// replace range is origin[start : end]. [start, end)
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/pzbaIVCTreZ)
|
||||
func HideString(origin string, start, end int, replaceChar string) string {
|
||||
size := len(origin)
|
||||
|
||||
@@ -530,7 +530,7 @@ func HideString(origin string, start, end int, replaceChar string) string {
|
||||
}
|
||||
|
||||
// ContainsAll return true if target string contains all the substrs.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/KECtK2Os4zq
|
||||
func ContainsAll(str string, substrs []string) bool {
|
||||
for _, v := range substrs {
|
||||
if !strings.Contains(str, v) {
|
||||
@@ -542,7 +542,7 @@ func ContainsAll(str string, substrs []string) bool {
|
||||
}
|
||||
|
||||
// ContainsAny return true if target string contains any one of the substrs.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/dZGSSMB3LXE
|
||||
func ContainsAny(str string, substrs []string) bool {
|
||||
for _, v := range substrs {
|
||||
if strings.Contains(str, v) {
|
||||
|
||||
@@ -416,22 +416,19 @@ func ExampleWordCount() {
|
||||
|
||||
result1 := WordCount("a word")
|
||||
result2 := WordCount("I'am a programmer")
|
||||
result3 := WordCount("Bonjour, je suis programmeur")
|
||||
result4 := WordCount("a -b-c' 'd'e")
|
||||
result5 := WordCount("你好,我是一名码农")
|
||||
result6 := WordCount("こんにちは,私はプログラマーです")
|
||||
result3 := WordCount("a -b-c' 'd'e")
|
||||
result4 := WordCount("你好,我是一名码农")
|
||||
result5 := WordCount("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 3
|
||||
// 0
|
||||
// 0
|
||||
|
||||
@@ -112,7 +112,6 @@ func ContainLetter(str string) bool {
|
||||
return letterRegexMatcher.MatchString(str)
|
||||
}
|
||||
|
||||
|
||||
// ContainLetter check if the string contain at least one number.
|
||||
func ContainNumber(input string) bool {
|
||||
return numberRegexMatcher.MatchString(input)
|
||||
|
||||
Reference in New Issue
Block a user