mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
publish lancet
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea/*
|
||||
.vscode/*
|
||||
.DS_Store
|
||||
444
README.md
Normal file
444
README.md
Normal file
@@ -0,0 +1,444 @@
|
||||
<div align="center">
|
||||
<h1 style="width: 100%; text-align: center;">Lancet</h1>
|
||||
<p style="font-size: 18px">
|
||||
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
|
||||
</p>
|
||||
<div align="center" style="text-align: center;">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
English | [简体中文](./README_zh-CN.md)
|
||||
|
||||
</div>
|
||||
|
||||
### Feature
|
||||
|
||||
- 👏 Comprehensive, efficient and reusable.
|
||||
- 💪 100+ common go util functions, support string, slice, datetime, net, crypt...
|
||||
- 💅 Only depend on the go standard library.
|
||||
- 🌍 Unit test for exery exported function.
|
||||
|
||||
### Installation
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet
|
||||
```
|
||||
|
||||
### Usage
|
||||
Lancet organizes the code into package structure, and you need to import the corresponding package name when use it. For example, if you use string-related functions,import the strutil package like below:
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
Here takes the string function ReverseStr (reverse order string) as an example, and the strutil package needs to be imported.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "hello"
|
||||
rs := strutil.ReverseStr(s)
|
||||
fmt.Println(rs) //olleh
|
||||
}
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
|
||||
#### 1. convertor contains some functions for data convertion.
|
||||
|
||||
- Support conversion between commonly used data types.
|
||||
- Usage: import "github.com/duke-git/lancet/cryptor"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "12.3"
|
||||
f, err := convertor.ToFloat(s)
|
||||
if err != nil {
|
||||
fmt.Errorf("error is %s", err.Error())
|
||||
}
|
||||
fmt.Println(f) // 12.3
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) //convert color hex to color rgb
|
||||
func ColorRGBToHex(red, green, blue int) string //convert color rgb to color hex
|
||||
func ToBool(s string) (bool, error) //convert string to a boolean
|
||||
func ToBytes(data interface{}) ([]byte, error) //convert interface to bytes
|
||||
func ToChar(s string) []string //convert string to char slice
|
||||
func ToFloat(value interface{}) (float64, error) //convert value to float64, if input is not a float return 0.0 and error
|
||||
func ToInt(value interface{}) (int64, error) //convert value to int64, if input is not a numeric format return 0 and error
|
||||
func ToJson(value interface{}) (string, error) //convert value to a valid json string
|
||||
func ToString(value interface{}) string //convert value to string
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) //convert struct to map, only convert exported field, tag `json` should be set
|
||||
```
|
||||
|
||||
#### 2. cryptor is for data encryption and decryption.
|
||||
|
||||
- Support md5, hmac, aes, des, ras.
|
||||
- Usage: import "github.com/duke-git/lancet/cryptor"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/cryptor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key))
|
||||
decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key))
|
||||
fmt.Println(string(decrypted)) // hello
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func AesEcbEncrypt(data, key []byte) []byte //AES ECB encrypt
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte //AES ECB decrypt
|
||||
func AesCbcEncrypt(data, key []byte) []byte //AES CBC encrypt
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte //AES CBC decrypt
|
||||
func AesCtrCrypt(data, key []byte) []byte //AES CTR encrypt / decrypt
|
||||
func AesCfbEncrypt(data, key []byte) []byte //AES CFB encrypt
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte //AES CFB decrypt
|
||||
func AesOfbEncrypt(data, key []byte) []byte //AES OFB encrypt
|
||||
func AesOfbDecrypt(data, key []byte) []byte //AES OFB decrypt
|
||||
func Base64StdEncode(s string) string //base64 encode
|
||||
func Base64StdDecode(s string) string //base64 decode
|
||||
func DesCbcEncrypt(data, key []byte) []byte //DES CBC encrypt
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte //DES CBC decrypt
|
||||
func DesCtrCrypt(data, key []byte) []byte //DES CTR encrypt/decrypt
|
||||
func DesCfbEncrypt(data, key []byte) []byte //DES CFB encrypt
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte //DES CFB decrypt
|
||||
func DesOfbEncrypt(data, key []byte) []byte //DES OFB encrypt
|
||||
func DesOfbDecrypt(data, key []byte) []byte //DES OFB decrypt
|
||||
func HmacMd5(data, key string) string //get hmac md5 value
|
||||
func HmacSha1(data, key string) string //get hmac sha1 value
|
||||
func HmacSha256(data, key string) string //get hmac sha256 value
|
||||
func HmacSha512(data, key string) string //get hmac sha512 value
|
||||
func Sha1(data string) string //get sha1 value
|
||||
func Sha256(data string) string //getsha256 value
|
||||
func Sha512(data string) string //get sha512 value
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) //generate RSA pem file
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte //RSA encrypt
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA decrypt
|
||||
|
||||
```
|
||||
|
||||
#### 3. datetime parse and format datetime
|
||||
|
||||
- Parse and format datetime
|
||||
- Usage: import "github.com/duke-git/lancet/datetime"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
now := time.Now()
|
||||
s := datetime.FormatTimeToStr(now, "yyyy-mm-dd hh:mm:ss")
|
||||
fmt.Println(s) // 2021-11-24 11:16:55
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func AddDay(t time.Time, day int64) time.Time //add or sub days to time
|
||||
func AddHour(t time.Time, hour int64) time.Time //add or sub hours to time
|
||||
func AddMinute(t time.Time, minute int64) time.Time //add or sub minutes to time
|
||||
func GetNowDate() string //get current date, format is yyyy-mm-dd
|
||||
func GetNowTime() string //get current time, format is hh:mm:ss
|
||||
func GetNowDateTime() string //get current date and time, format is yyyy-mm-dd hh:mm:ss
|
||||
func GetZeroHourTimestamp() int64 //return timestamp of zero hour (timestamp of 00:00)
|
||||
func GetNightTimestamp() int64 //return timestamp of zero hour (timestamp of 23:59)
|
||||
func FormatTimeToStr(t time.Time, format string) string //convert time to string
|
||||
func FormatStrToTime(str, format string) time.Time //convert string to time
|
||||
```
|
||||
|
||||
#### 4. fileutil basic functions for file operations
|
||||
|
||||
- Basic functions for file operations.
|
||||
- Usage: import "github.com/duke-git/lancet/fileutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(fileutil.IsDir("./")) // true
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func CreateFile(path string) bool // create a file in path
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error //copy src file to dst file
|
||||
func IsExist(path string) bool //checks if a file or directory exists
|
||||
func IsDir(path string) bool //checks if the path is directy or not
|
||||
func ListFileNames(path string) ([]string, error) //return all file names in the path
|
||||
func RemoveFile(path string) error //remove the path file
|
||||
```
|
||||
|
||||
#### 5. formatter is for data format
|
||||
|
||||
- Contain some formatting function
|
||||
- Usage: import "github.com/duke-git/lancet/formatter"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/formatter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(formatter.Comma("12345", "")) // "12,345"
|
||||
fmt.Println(formatter.Comma(12345.67, "¥")) // "¥12,345.67"
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func Comma(v interface{}, symbol string) string //add comma to number by every 3 numbers from right. ahead by symbol char
|
||||
```
|
||||
|
||||
#### 6. netutil is for net process
|
||||
|
||||
- Ip and http request method.
|
||||
- Usage: import "github.com/duke-git/lancet/netutil".
|
||||
- The Http function params order:url, header, query string, body, httpclient.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "https://gutendex.com/books?"
|
||||
header := make(map[string]string)
|
||||
header["Content-Type"] = "application/json"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := netutil.HttpGet(url, header, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func GetInternalIp() string //get internal ip
|
||||
func GetPublicIpInfo() (*PublicIpInfo, error) //get public ip info: country, region, isp, city, lat, lon, ip
|
||||
func IsPublicIP(IP net.IP) bool //判断ip是否为公共ip
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) //http get request
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) //http post request
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) //http put request
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete request
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch request
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string //convert map to url query string
|
||||
```
|
||||
|
||||
#### 7. random is for rand string and int generation.
|
||||
|
||||
- Generate random string and int.
|
||||
- Usage: import "github.com/duke-git/lancet/random".
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr)
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte //generate random []byte
|
||||
func RandInt(min, max int) int //generate random int
|
||||
func RandString(length int) string //generate random string
|
||||
```
|
||||
|
||||
#### 8. slice is for process slice
|
||||
|
||||
- Contain function for process slice.
|
||||
- Usage: import "github.com/duke-git/lancet/slice"
|
||||
- Due to the unstable support of generic, most of the slice processing function parameter and return value is interface {}. After go generic is stable, the related functions will be refactored.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 4, 3, 4, 6, 7, 3}
|
||||
uniqueNums, _ := slice.IntSlice(slice.Unique(nums))
|
||||
fmt.Println(uniqueNums) //[1 4 3 6 7]
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func Contain(slice interface{}, value interface{}) bool //check if the value is in the slice or not
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} //creates an slice of elements split into groups the length of `size`.
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //convert originalSlice to newSliceType
|
||||
func Difference(slice1, slice2 interface{}) interface{} //creates an slice of whose element not included in the other given slice
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1
|
||||
func Filter(slice, function interface{}) interface{} //filter slice, function signature should be func(index int, value interface{}) bool
|
||||
func IntSlice(slice interface{}) ([]int, error) //convert value to int slice
|
||||
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //insert the element into slice at index.
|
||||
func Map(slice, function interface{}) interface{} //map lisce, function signature should be func(index int, value interface{}) interface{}
|
||||
func ReverseSlice(slice interface{}) //revere slice
|
||||
func Reduce(slice, function, zero interface{}) interface{} //reduce slice, function signature should be func(index int, value1, value2 interface{}) interface{}
|
||||
func SortByField(slice interface{}, field string, sortType ...string) error //sort struct slice by field
|
||||
func StringSlice(slice interface{}) []string //convert value to string slice
|
||||
func Unique(slice interface{}) interface{} //remove duplicate elements in slice
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index.
|
||||
```
|
||||
|
||||
#### 9. strutil is for processing string
|
||||
|
||||
- Contain functions to precess string
|
||||
- Usage: import "github.com/duke-git/lancet/strutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
camelCaseStr := strutil.CamelCase(str)
|
||||
fmt.Println(camelCaseStr) //fooBar
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func After(s, char string) string //create substring in source string after position when char first appear
|
||||
func AfterLast(s, char string) string //create substring in source string after position when char last appear
|
||||
func Before(s, char string) string //create substring in source string before position when char first appear
|
||||
func BeforeLast(s, char string) string //create substring in source string before position when char last appear
|
||||
func CamelCase(s string) string //covert string to camelCase string. "foo bar" -> "fooBar"
|
||||
func Capitalize(s string) string //convert the first character of a string to upper case, "fOO" -> "Foo"
|
||||
func IsString(v interface{}) bool //check if the value data type is string or not
|
||||
func KebabCase(s string) string //covert string to kebab-case, "foo_Bar" -> "foo-bar"
|
||||
func LowerFirst(s string) string //convert the first character of string to lower case
|
||||
func PadEnd(source string, size int, padStr string) string //pads string on the right side if it's shorter than size
|
||||
func PadStart(source string, size int, padStr string) string//pads string on the left side if it's shorter than size
|
||||
func ReverseStr(s string) string //return string whose char order is reversed to the given string
|
||||
func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar"
|
||||
```
|
||||
|
||||
#### 10. validator is for data validation
|
||||
|
||||
- Contain function for data validation.
|
||||
- Usage: import "github.com/duke-git/lancet/validator".
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
isAlpha := validator.IsAlpha(str)
|
||||
fmt.Println(isAlpha) //false
|
||||
}
|
||||
```
|
||||
|
||||
- Function list:
|
||||
|
||||
```go
|
||||
func ContainChinese(s string) bool //check if the string contain mandarin chinese
|
||||
func IsAlpha(s string) bool //checks if the string contains only letters (a-zA-Z)
|
||||
func IsBase64(base64 string) bool //check if the string is base64 string
|
||||
func IsChineseMobile(mobileNum string) bool //check if the string is chinese mobile number
|
||||
func IsChineseIdNum(id string) bool //check if the string is chinese id number
|
||||
func IsChinesePhone(phone string) bool //check if the string is chinese phone number
|
||||
func IsCreditCard(creditCart string) bool //check if the string is credit card
|
||||
func IsDns(dns string) bool //check if the string is dns
|
||||
func IsEmail(email string) bool //check if the string is a email address
|
||||
func IsEmptyString(s string) bool //check if the string is empty
|
||||
func IsFloatStr(s string) bool //check if the string can convert to a float
|
||||
func IsNumberStr(s string) bool //check if the string can convert to a number
|
||||
func IsRegexMatch(s, regex string) bool //check if the string match the regexp
|
||||
func IsIntStr(s string) bool //check if the string can convert to a integer
|
||||
func IsIp(ipstr string) bool //check if the string is a ip address
|
||||
func IsIpV4(ipstr string) bool //check if the string is a ipv4 address
|
||||
func IsIpV6(ipstr string) bool //check if the string is a ipv6 address
|
||||
func IsStrongPassword(password string, length int) bool //check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?><))
|
||||
func IsWeakPassword(password string) bool //check if the string is weak password(only letter or only number or letter + number)
|
||||
```
|
||||
445
README_zh-CN.md
Normal file
445
README_zh-CN.md
Normal file
@@ -0,0 +1,445 @@
|
||||
<div align="center">
|
||||
<h1 style="width: 100%; text-align: center;">Lancet</h1>
|
||||
<p style="font-size: 18px">
|
||||
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
|
||||
</p>
|
||||
<div align="center" style="text-align: center;">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
简体中文 | [English](./README.md)
|
||||
|
||||
</div>
|
||||
|
||||
### 特性
|
||||
|
||||
- 👏 全面、高效、可复用
|
||||
- 💪 100+常用go工具函数,支持string、slice、datetime、net、crypt...
|
||||
- 💅 只依赖go标准库
|
||||
- 🌍 所有导出函数单测试覆盖率100%
|
||||
|
||||
### 安装
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet
|
||||
```
|
||||
|
||||
### 用法
|
||||
|
||||
lancet是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入strutil包:
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/strutil"
|
||||
```
|
||||
|
||||
### 例子
|
||||
|
||||
此处以字符串工具函数ReverseStr(逆序字符串)为例,需要导入strutil包:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "hello"
|
||||
rs := strutil.ReverseStr(s)
|
||||
fmt.Println(rs) //olleh
|
||||
}
|
||||
```
|
||||
|
||||
### API文档
|
||||
|
||||
#### 1. convertor数据转换包
|
||||
|
||||
- 转换函数支持常用数据类型之间的转换
|
||||
- 导入包:import "github.com/duke-git/lancet/cryptor"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "12.3"
|
||||
f, err := convertor.ToFloat(s)
|
||||
if err != nil {
|
||||
fmt.Errorf("error is %s", err.Error())
|
||||
}
|
||||
fmt.Println(f) // 12.3
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) //颜色值16进制转rgb
|
||||
func ColorRGBToHex(red, green, blue int) string //颜色值rgb转16进制
|
||||
func ToBool(s string) (bool, error) //字符串转成Bool
|
||||
func ToBytes(data interface{}) ([]byte, error) //interface转成byte slice
|
||||
func ToChar(s string) []string //字符串转成字符slice
|
||||
func ToFloat(value interface{}) (float64, error) //interface转成float64
|
||||
func ToInt(value interface{}) (int64, error) //interface转成int64
|
||||
func ToJson(value interface{}) (string, error) //interface转成json string
|
||||
func ToString(value interface{}) string //interface转成string
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) //struct串转成map, 需要设置struct tag `json`
|
||||
```
|
||||
|
||||
#### 2. cryptor加解密包
|
||||
|
||||
- 加密函数是支持md5, hmac, aes, des, ras
|
||||
- 导入包:import "github.com/duke-git/lancet/cryptor"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/cryptor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := cryptor.AesCbcEncrypt([]byte(data), []byte(key))
|
||||
decrypted := cryptor.AesCbcDecrypt(encrypted, []byte(key))
|
||||
fmt.Println(string(decrypted)) // hello
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func AesEcbEncrypt(data, key []byte) []byte //AES ECB模式加密
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte //AES ECB模式解密
|
||||
func AesCbcEncrypt(data, key []byte) []byte //AES CBC模式加密
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte //AES CBC模式解密
|
||||
func AesCtrCrypt(data, key []byte) []byte //AES CTR模式加密/解密
|
||||
func AesCfbEncrypt(data, key []byte) []byte //AES CFB模式加密
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte //AES CFB模式解密
|
||||
func AesOfbEncrypt(data, key []byte) []byte //AES OFB模式加密
|
||||
func AesOfbDecrypt(data, key []byte) []byte //AES OFB模式解密
|
||||
func Base64StdEncode(s string) string //base64编码
|
||||
func Base64StdDecode(s string) string //base64解码
|
||||
func DesCbcEncrypt(data, key []byte) []byte //DES CBC模式加密
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte //DES CBC模式解密
|
||||
func DesCtrCrypt(data, key []byte) []byte //DES CTR模式加密/解密
|
||||
func DesCfbEncrypt(data, key []byte) []byte //DES CFB模式加密
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte //DES CFB模式解密
|
||||
func DesOfbEncrypt(data, key []byte) []byte //DES OFB模式加密
|
||||
func DesOfbDecrypt(data, key []byte) []byte //DES OFB模式解密
|
||||
func HmacMd5(data, key string) string //获取hmac md5值
|
||||
func HmacSha1(data, key string) string //获取hmac sha1值
|
||||
func HmacSha256(data, key string) string //获取hmac sha256值
|
||||
func HmacSha512(data, key string) string //获取hmac sha512值
|
||||
func Sha1(data string) string //获取sha1值
|
||||
func Sha256(data string) string //获取sha256值
|
||||
func Sha512(data string) string //获取sha512值
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) //生成RSA私钥文件
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte //RSA加密
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA解密
|
||||
|
||||
```
|
||||
|
||||
#### 3. datetime日期时间处理包
|
||||
|
||||
- 处理日期时间
|
||||
- 导入包:import "github.com/duke-git/lancet/datetime"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
now := time.Now()
|
||||
s := datetime.FormatTimeToStr(now, "yyyy-mm-dd hh:mm:ss")
|
||||
fmt.Println(s) // 2021-11-24 11:16:55
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func AddDay(t time.Time, day int64) time.Time //加减天数
|
||||
func AddHour(t time.Time, hour int64) time.Time //加减小时数
|
||||
func AddMinute(t time.Time, minute int64) time.Time //加减分钟数
|
||||
func GetNowDate() string //获取当天日期 格式yyyy-mm-dd
|
||||
func GetNowTime() string //获取当前时间 格式hh:mm:ss
|
||||
func GetNowDateTime() string //获取当前日期时间 格式yyyy-mm-dd hh:mm:ss
|
||||
func GetZeroHourTimestamp() int64 //获取当天零时时间戳(00:00)
|
||||
func GetNightTimestamp() int64 //获取当天23时时间戳(23:59)
|
||||
func FormatTimeToStr(t time.Time, format string) string //时间格式化字符串
|
||||
func FormatStrToTime(str, format string) time.Time //字符串转换成时间
|
||||
```
|
||||
|
||||
#### 4. fileutil文件处理包
|
||||
|
||||
- 文件处理常用函数
|
||||
- 导入包:import "github.com/duke-git/lancet/fileutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(fileutil.IsDir("./")) // true
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func IsExist(path string) bool //判断文件/目录是否存在
|
||||
func CreateFile(path string) bool //创建文件
|
||||
func IsDir(path string) bool //判断是否为目录
|
||||
func RemoveFile(path string) error //删除文件
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error //复制文件
|
||||
func ListFileNames(path string) ([]string, error) //列出目录下所有文件名称
|
||||
```
|
||||
|
||||
#### 5. formatter格式化处理包
|
||||
|
||||
- 格式化相关处理函数
|
||||
- 导入包:import "github.com/duke-git/lancet/formatter"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/formatter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(formatter.Comma("12345", "")) // "12,345"
|
||||
fmt.Println(formatter.Comma(12345.67, "¥")) // "¥12,345.67"
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
|
||||
```
|
||||
|
||||
#### 6. netutil网络处理包
|
||||
|
||||
- 处理ip, http请求相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/netutil"
|
||||
- http方法params参数顺序:header, query string, body, httpclient
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "https://gutendex.com/books?"
|
||||
header := make(map[string]string)
|
||||
header["Content-Type"] = "application/json"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := netutil.HttpGet(url, header, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func GetInternalIp() string //获取内部ip
|
||||
func GetPublicIpInfo() (*PublicIpInfo, error) //获取公共ip信息: country, region, isp, city, lat, lon, ip
|
||||
func IsPublicIP(IP net.IP) bool //判断ip是否为公共ip
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) //http get请求
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) //http post请求
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) //http put请求
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) //http delete请求
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) //http patch请求
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string //将map转换成url query string
|
||||
```
|
||||
|
||||
#### 7. random随机数处理包
|
||||
|
||||
- 生成和处理随机数
|
||||
- 导入包:import "github.com/duke-git/lancet/random"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/random"
|
||||
)
|
||||
|
||||
func main() {
|
||||
randStr := random.RandString(6)
|
||||
fmt.Println(randStr)
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func RandBytes(length int) []byte //生成随机[]byte
|
||||
func RandInt(min, max int) int //生成随机int
|
||||
func RandString(length int) string //生成随机string
|
||||
```
|
||||
|
||||
#### 8. slice切片操作包
|
||||
|
||||
- 切片操作相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/slice"
|
||||
- 由于go目前对范型支持不稳定,slice处理函数参数和返回值大部分为interface{}, 待范型特性稳定后,会重构相关函数
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 4, 3, 4, 6, 7, 3}
|
||||
uniqueNums, _ := slice.IntSlice(slice.Unique(nums))
|
||||
fmt.Println(uniqueNums) //[1 4 3 6 7]
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func Contain(slice interface{}, value interface{}) bool //判断slice是否包含value
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} //均分slice
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //将originalSlice转换为 newSliceType
|
||||
func Difference(slice1, slice2 interface{}) interface{} //返回
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值
|
||||
func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名:func(index int, value interface{}) bool
|
||||
func IntSlice(slice interface{}) ([]int, error) //转成int切片
|
||||
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置插入value
|
||||
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名:func(index int, value interface{}) interface{}
|
||||
func ReverseSlice(slice interface{}) //反转切片
|
||||
func Reduce(slice, function, zero interface{}) interface{} //切片reduce操作, 函数签名:func(index int, value1, value2 interface{}) interface{}
|
||||
func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序
|
||||
func StringSlice(slice interface{}) []string //转为string切片
|
||||
func Unique(slice interface{}) interface{} //去重切片
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value
|
||||
```
|
||||
|
||||
#### 9. strutil字符串处理包
|
||||
|
||||
- 字符串操作相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/strutil"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
camelCaseStr := strutil.CamelCase(str)
|
||||
fmt.Println(camelCaseStr) //fooBar
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func After(s, char string) string //截取字符串中char第一次出现之后的字符串
|
||||
func AfterLast(s, char string) string //截取字符串中char最后一次出现之后的字符串
|
||||
func Before(s, char string) string //截取字符串中char第一次出现之前的字符串
|
||||
func BeforeLast(s, char string) string //截取字符串中char最后一次出现之前的字符串
|
||||
func CamelCase(s string) string //字符串转为cameCase, "foo bar" -> "fooBar"
|
||||
func Capitalize(s string) string //字符串转为Capitalize, "fOO" -> "Foo"
|
||||
func IsString(v interface{}) bool //判断是否是字符串
|
||||
func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-bar"
|
||||
func LowerFirst(s string) string //字符串的第一个字母转为小写字母
|
||||
func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符
|
||||
func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符
|
||||
func ReverseStr(s string) string //字符串逆袭
|
||||
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
|
||||
```
|
||||
|
||||
#### 10. validator验证器包
|
||||
|
||||
- 数据校验相关函数
|
||||
- 导入包:import "github.com/duke-git/lancet/validator"
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Foo-Bar"
|
||||
isAlpha := validator.IsAlpha(str)
|
||||
fmt.Println(isAlpha) //false
|
||||
}
|
||||
```
|
||||
|
||||
- 函数列表:
|
||||
|
||||
```go
|
||||
func ContainChinese(s string) bool //判断字符串中是否含有中文字符
|
||||
func IsAlpha(s string) bool //判断字符串是否只含有字母
|
||||
func IsBase64(base64 string) bool //判断字符串是base64
|
||||
func IsChineseMobile(mobileNum string) bool //判断字符串是否是手机号
|
||||
func IsChineseIdNum(id string) bool //判断字符串是否是身份证号
|
||||
func IsChinesePhone(phone string) bool //判断字符串是否是座机电话号码
|
||||
func IsCreditCard(creditCart string) bool //判断字符串是否是信用卡
|
||||
func IsDns(dns string) bool //判断字符串是否是DNS
|
||||
func IsEmail(email string) bool //判断字符串是否是邮箱
|
||||
func IsEmptyString(s string) bool //判断字符串是否为空
|
||||
func IsFloatStr(s string) bool //判断字符串是否可以转成float
|
||||
func IsNumberStr(s string) bool //判断字符串是否可以转成数字
|
||||
func IsRegexMatch(s, regex string) bool //判断字符串是否match正则表达式
|
||||
func IsIntStr(s string) bool //判断字符串是否可以转成整数
|
||||
func IsIp(ipstr string) bool //判断字符串是否是ip
|
||||
func IsIpV4(ipstr string) bool //判断字符串是否是ipv4
|
||||
func IsIpV6(ipstr string) bool //判断字符串是否是ipv6
|
||||
func IsStrongPassword(password string, length int) bool //判断字符串是否是强密码(大小写字母+数字+特殊字符)
|
||||
func IsWeakPassword(password string) bool //判断字符串是否是弱密码(只有字母或数字)
|
||||
```
|
||||
210
convertor/convertor.go
Normal file
210
convertor/convertor.go
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package convertor implements some functions to convert data.
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ToBool convert string to a boolean
|
||||
func ToBool(s string) (bool, error) {
|
||||
return strconv.ParseBool(s)
|
||||
}
|
||||
|
||||
// ToBool convert interface to bytes
|
||||
func ToBytes(data interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ToChar convert string to char slice
|
||||
func ToChar(s string) []string {
|
||||
c := make([]string, 0)
|
||||
if len(s) == 0 {
|
||||
c = append(c, "")
|
||||
}
|
||||
for _, v := range s {
|
||||
c = append(c, string(v))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ToString convert value to string
|
||||
func ToString(value interface{}) string {
|
||||
var res string
|
||||
if value == nil {
|
||||
return res
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
res = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case float32:
|
||||
res = strconv.FormatFloat(float64(v), 'f', -1, 64)
|
||||
case int:
|
||||
res = strconv.Itoa(v)
|
||||
case uint:
|
||||
res = strconv.Itoa(int(v))
|
||||
case int8:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint8:
|
||||
res = strconv.Itoa(int(v))
|
||||
case int16:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint16:
|
||||
res = strconv.Itoa(int(v))
|
||||
case int32:
|
||||
res = strconv.Itoa(int(v))
|
||||
case uint32:
|
||||
res = strconv.Itoa(int(v))
|
||||
case int64:
|
||||
res = strconv.FormatInt(v, 10)
|
||||
case uint64:
|
||||
res = strconv.FormatUint(v, 10)
|
||||
case string:
|
||||
res = value.(string)
|
||||
case []byte:
|
||||
res = string(value.([]byte))
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
res = string(newValue)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ToJson convert value to a valid json string
|
||||
func ToJson(value interface{}) (string, error) {
|
||||
res, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
res = []byte("")
|
||||
}
|
||||
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// ToFloat convert value to a float64, if input is not a float return 0.0 and error
|
||||
func ToFloat(value interface{}) (float64, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
res := 0.0
|
||||
err := fmt.Errorf("ToInt: unvalid interface type %T", value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = float64(v.Int())
|
||||
return res, nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = float64(v.Uint())
|
||||
return res, nil
|
||||
case float32, float64:
|
||||
res = v.Float()
|
||||
return res, nil
|
||||
case string:
|
||||
res, err = strconv.ParseFloat(v.String(), 64)
|
||||
if err != nil {
|
||||
res = 0.0
|
||||
}
|
||||
return res, err
|
||||
default:
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// ToInt convert value to a int64, if input is not a numeric format return 0 and error
|
||||
func ToInt(value interface{}) (int64, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
var res int64
|
||||
err := fmt.Errorf("ToInt: invalid interface type %T", value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = v.Int()
|
||||
return res, nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = int64(v.Uint())
|
||||
return res, nil
|
||||
case float32, float64:
|
||||
res = int64(v.Float())
|
||||
return res, nil
|
||||
case string:
|
||||
res, err = strconv.ParseInt(v.String(), 0, 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
}
|
||||
return res, err
|
||||
default:
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// StructToMap convert struct to map, only convert exported struct field
|
||||
// map key is specified same as struct field tag `json` value
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
t := reflect.TypeOf(value)
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
|
||||
}
|
||||
|
||||
res := make(map[string]interface{})
|
||||
|
||||
fieldNum := t.NumField()
|
||||
pattern := `^[A-Z]`
|
||||
regex := regexp.MustCompile(pattern)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
name := t.Field(i).Name
|
||||
tag := t.Field(i).Tag.Get("json")
|
||||
if regex.MatchString(name) && tag != "" {
|
||||
//res[name] = v.Field(i).Interface()
|
||||
res[tag] = v.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ColorHexToRGB convert hex color to rgb color
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) {
|
||||
colorHex = strings.TrimPrefix(colorHex, "#")
|
||||
color64, err := strconv.ParseInt(colorHex, 16, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
color := int(color64)
|
||||
return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF
|
||||
}
|
||||
|
||||
// ColorRGBToHex convert rgb color to hex color
|
||||
func ColorRGBToHex(red, green, blue int) string {
|
||||
r := strconv.FormatInt(int64(red), 16)
|
||||
g := strconv.FormatInt(int64(green), 16)
|
||||
b := strconv.FormatInt(int64(blue), 16)
|
||||
|
||||
if len(r) == 1 {
|
||||
r = "0" + r
|
||||
}
|
||||
if len(g) == 1 {
|
||||
g = "0" + g
|
||||
}
|
||||
if len(b) == 1 {
|
||||
b = "0" + b
|
||||
}
|
||||
|
||||
return "#" + r + g + b
|
||||
}
|
||||
210
convertor/convertor_test.go
Normal file
210
convertor/convertor_test.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToChar(t *testing.T) {
|
||||
cases := []string{"", "abc", "1 2#3"}
|
||||
expected := [][]string{
|
||||
{""},
|
||||
{"a", "b", "c"},
|
||||
{"1", " ", "2", "#", "3"},
|
||||
}
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := ToChar(cases[i])
|
||||
if !reflect.DeepEqual(res, expected[i]) {
|
||||
utils.LogFailedTestInfo(t, "ToChar", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBool(t *testing.T) {
|
||||
cases := []string{"true", "True", "false", "False", "0", "1", "123"}
|
||||
expected := []bool{true, true, false, false, false, true, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToBool(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToBool", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
cases := []interface{}{
|
||||
0,
|
||||
false,
|
||||
"1",
|
||||
}
|
||||
expected := [][]byte{
|
||||
{3, 4, 0, 0},
|
||||
{3, 2, 0, 0},
|
||||
{4, 12, 0, 1, 49},
|
||||
}
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToBytes(cases[i])
|
||||
fmt.Println(res)
|
||||
if !reflect.DeepEqual(res, expected[i]) {
|
||||
utils.LogFailedTestInfo(t, "ToBytes", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToInt(t *testing.T) {
|
||||
cases := []interface{}{"123", "-123", 123, "abc", false, "111111111111111111111111111111111111111"}
|
||||
expected := []int64{123, -123, 123, 0, 0, 0}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToInt(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToInt", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFloat(t *testing.T) {
|
||||
cases := []interface{}{"", "-1", "-.11", "1.23e3", ".123e10", "abc"}
|
||||
expected := []float64{0, -1, -0.11, 1230, 0.123e10, 0}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res, _ := ToFloat(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "ToFloat", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
// basic type
|
||||
toString(t, "a1", "a1")
|
||||
toString(t, 111, "111")
|
||||
toString(t, 111.01, "111.01")
|
||||
toString(t, true, "true")
|
||||
//toString(t, 1.5+10i, "(1.5+10i)")
|
||||
|
||||
// slice
|
||||
aSlice := []int{1, 2, 3}
|
||||
toString(t, aSlice, "[1,2,3]")
|
||||
|
||||
// map
|
||||
aMap := make(map[string]int)
|
||||
aMap["a"] = 1
|
||||
aMap["b"] = 2
|
||||
aMap["c"] = 3
|
||||
|
||||
toString(t, aMap, "{\"a\":1,\"b\":2,\"c\":3}")
|
||||
|
||||
// struct
|
||||
type TestStruct struct {
|
||||
Name string
|
||||
}
|
||||
aStruct := TestStruct{Name: "TestStruct"}
|
||||
toString(t, aStruct, "{\"Name\":\"TestStruct\"}")
|
||||
}
|
||||
|
||||
func toString(t *testing.T, test interface{}, expected string) {
|
||||
res := ToString(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "ToString", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJson(t *testing.T) {
|
||||
// map
|
||||
aMap := make(map[string]int)
|
||||
aMap["a"] = 1
|
||||
aMap["b"] = 2
|
||||
aMap["c"] = 3
|
||||
|
||||
mapJson := "{\"a\":1,\"b\":2,\"c\":3}"
|
||||
r1, _ := ToJson(aMap)
|
||||
if r1 != mapJson {
|
||||
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// struct
|
||||
type TestStruct struct {
|
||||
Name string
|
||||
}
|
||||
aStruct := TestStruct{Name: "TestStruct"}
|
||||
structJson := "{\"Name\":\"TestStruct\"}"
|
||||
r2, _ := ToJson(aStruct)
|
||||
if r2 != structJson {
|
||||
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructToMap(t *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
|
||||
p1 := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
|
||||
pm1, _ := StructToMap(p1)
|
||||
m1 := make(map[string]interface{})
|
||||
m1["name"] = "test"
|
||||
//exp1["100"] = 100
|
||||
|
||||
if !reflect.DeepEqual(pm1, m1) {
|
||||
utils.LogFailedTestInfo(t, "StructToMap", p1, m1, pm1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
p2 := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
|
||||
pm2, _ := StructToMap(p1)
|
||||
m2 := make(map[string]interface{})
|
||||
m2["name"] = "test"
|
||||
m2["100"] = 100
|
||||
|
||||
if reflect.DeepEqual(pm2, m2) {
|
||||
utils.LogFailedTestInfo(t, "StructToMap", p2, m2, pm2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorHexToRGB(t *testing.T) {
|
||||
colorHex := "#003366"
|
||||
r, g, b := ColorHexToRGB(colorHex)
|
||||
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
|
||||
expected := "0,51,102"
|
||||
|
||||
if colorRGB != expected {
|
||||
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorHex, expected, colorRGB)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorRGBToHex(t *testing.T) {
|
||||
r := 0
|
||||
g := 51
|
||||
b := 102
|
||||
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
|
||||
colorHex := ColorRGBToHex(r, g, b)
|
||||
expected := "#003366"
|
||||
|
||||
if colorHex != expected {
|
||||
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorRGB, expected, colorHex)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
168
cryptor/aes.go
Normal file
168
cryptor/aes.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
// Note:
|
||||
// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
copy(plain, data)
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
encrypted := make([]byte, len(plain))
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesEcbDecrypt decrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
//
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCbcEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32
|
||||
block, _ := aes.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
data = pkcs7Padding(data, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||||
|
||||
encrypted := make([]byte, len(data))
|
||||
blockMode.CryptBlocks(encrypted, data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesEcbDecrypt decrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
blockMode.CryptBlocks(decrypted, encrypted)
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// AesCtrCrypt encrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = pkcs7Padding(data, aes.BlockSize)
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:aes.BlockSize]
|
||||
data = data[aes.BlockSize:]
|
||||
if len(data)%aes.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return decrypted
|
||||
}
|
||||
72
cryptor/aes_test.go
Normal file
72
cryptor/aes_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestAesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
|
||||
|
||||
if string(aesEcbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesEcbEncrypt/AesEcbDecrypt", data, data, string(aesEcbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
|
||||
|
||||
if string(aesCbcDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCbcEncrypt/AesCbcDecrypt", data, data, string(aesCbcDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
|
||||
|
||||
if string(aesCtrDeCrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCtrCrypt", data, data, string(aesCtrDeCrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
|
||||
|
||||
if string(aesCfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesCfbEncrypt/AesCfbDecrypt", data, data, string(aesCfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
|
||||
|
||||
if string(aesOfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "AesOfbEncrypt/AesOfbDecrypt", data, data, string(aesOfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
96
cryptor/basic.go
Normal file
96
cryptor/basic.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
// Contain base64, hmac, sha, aes, des, and rsa
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Base64StdEncode encode string with base64 encoding
|
||||
func Base64StdEncode(s string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(s))
|
||||
}
|
||||
|
||||
// Base64StdEncode decode a base64 encoded string
|
||||
func Base64StdDecode(s string) string {
|
||||
b, _ := base64.StdEncoding.DecodeString(s)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Md5Str return the md5 value of string
|
||||
func Md5String(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5File return the md5 value of file
|
||||
func Md5File(filename string) (string, error) {
|
||||
f, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
h := md5.New()
|
||||
h.Write(f)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// HmacMd5 return the hmac hash of string use md5
|
||||
func HmacMd5(data, key string) string {
|
||||
h := hmac.New(md5.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha1 return the hmac hash of string use sha1
|
||||
func HmacSha1(data, key string) string {
|
||||
h := hmac.New(sha1.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha256 return the hmac hash of string use sha256
|
||||
func HmacSha256(data, key string) string {
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha512 return the hmac hash of string use sha512
|
||||
func HmacSha512(data, key string) string {
|
||||
h := hmac.New(sha512.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha1 return the sha1 value (SHA-1 hash algorithm) of string
|
||||
func Sha1(data string) string {
|
||||
sha1 := sha1.New()
|
||||
sha1.Write([]byte(data))
|
||||
return hex.EncodeToString(sha1.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha256 return the sha256 value (SHA256 hash algorithm) of string
|
||||
func Sha256(data string) string {
|
||||
sha256 := sha256.New()
|
||||
sha256.Write([]byte(data))
|
||||
return hex.EncodeToString(sha256.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha512 return the sha512 value (SHA512 hash algorithm) of string
|
||||
func Sha512(data string) string {
|
||||
sha512 := sha512.New()
|
||||
sha512.Write([]byte(data))
|
||||
return hex.EncodeToString(sha512.Sum([]byte("")))
|
||||
}
|
||||
133
cryptor/basic_test.go
Normal file
133
cryptor/basic_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestBase64StdEncode(t *testing.T) {
|
||||
s := "hello world"
|
||||
bs := Base64StdEncode(s)
|
||||
|
||||
if bs != "aGVsbG8gd29ybGQ=" {
|
||||
utils.LogFailedTestInfo(t, "Base64StdEncode", s, "aGVsbG8gd29ybGQ=", bs)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBase64StdDecode(t *testing.T) {
|
||||
bs := "aGVsbG8gd29ybGQ="
|
||||
s := Base64StdDecode(bs)
|
||||
|
||||
if s != "hello world" {
|
||||
utils.LogFailedTestInfo(t, "Base64StdDecode", bs, "hello world=", s)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMd5String(t *testing.T) {
|
||||
s := "hello"
|
||||
smd5 := Md5String(s)
|
||||
expected := "5d41402abc4b2a76b9719d911017c592"
|
||||
|
||||
if smd5 != expected {
|
||||
utils.LogFailedTestInfo(t, "Md5String", s, expected, smd5)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMd5File(t *testing.T) {
|
||||
file, _ := os.Create("./hello.txt")
|
||||
defer file.Close()
|
||||
file.WriteString("hello\n")
|
||||
|
||||
fileMd5, err := Md5File("./hello.txt")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println(fileMd5)
|
||||
}
|
||||
|
||||
func TestHmacMd5(t *testing.T) {
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacMd5 := HmacMd5(s, key)
|
||||
expected := "5f4c9faaff0a1ad3007d9ddc06abe36d"
|
||||
|
||||
if hmacMd5 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacMd5", s, expected, hmacMd5)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestHmacSha1(t *testing.T) {
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacSha1 := HmacSha1(s, key)
|
||||
expected := "3826f812255d8683f051ee97346d1359234d5dbd"
|
||||
|
||||
if hmacSha1 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha1", s, expected, hmacSha1)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestHmacSha256(t *testing.T) {
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacSha256 := HmacSha256(s, key)
|
||||
expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8"
|
||||
|
||||
if hmacSha256 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha256", s, expected, hmacSha256)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestHmacSha512(t *testing.T) {
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacSha512 := HmacSha512(s, key)
|
||||
expected := "5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175"
|
||||
|
||||
if hmacSha512 != expected {
|
||||
utils.LogFailedTestInfo(t, "HmacSha512", s, expected, hmacSha512)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSha1(t *testing.T) {
|
||||
s := "hello world"
|
||||
sha1 := Sha1(s)
|
||||
expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
|
||||
|
||||
if sha1 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha1", s, expected, sha1)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSha256(t *testing.T) {
|
||||
s := "hello world"
|
||||
sha256 := Sha256(s)
|
||||
expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
|
||||
if sha256 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha256", s, expected, sha256)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSha512(t *testing.T) {
|
||||
s := "hello world"
|
||||
sha512 := Sha512(s)
|
||||
expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f"
|
||||
|
||||
if sha512 != expected {
|
||||
utils.LogFailedTestInfo(t, "Sha512", s, expected, sha512)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
37
cryptor/crypt_util.go
Normal file
37
cryptor/crypt_util.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package cryptor
|
||||
|
||||
import "bytes"
|
||||
|
||||
func generateAesKey(key []byte) []byte {
|
||||
genKey := make([]byte, 16)
|
||||
copy(genKey, key)
|
||||
for i := 16; i < len(key); {
|
||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
return genKey
|
||||
}
|
||||
|
||||
func generateDesKey(key []byte) []byte {
|
||||
genKey := make([]byte, 8)
|
||||
copy(genKey, key)
|
||||
for i := 8; i < len(key); {
|
||||
for j := 0; j < 8 && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
return genKey
|
||||
}
|
||||
|
||||
func pkcs7Padding(src []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(src)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(src, padText...)
|
||||
}
|
||||
|
||||
func pkcs7UnPadding(src []byte) []byte {
|
||||
length := len(src)
|
||||
unPadding := int(src[length-1])
|
||||
return src[:(length - unPadding)]
|
||||
}
|
||||
166
cryptor/des.go
Normal file
166
cryptor/des.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8
|
||||
func DesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
length := (len(data) + des.BlockSize) / des.BlockSize
|
||||
plain := make([]byte, length*des.BlockSize)
|
||||
copy(plain, data)
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
encrypted := make([]byte, len(plain))
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesEcbDecrypt decrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8
|
||||
func DesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
//
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8
|
||||
func DesCbcEncrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
data = pkcs7Padding(data, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||||
|
||||
encrypted := make([]byte, len(data))
|
||||
blockMode.CryptBlocks(encrypted, data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCbcDecrypt decrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
blockMode.CryptBlocks(decrypted, encrypted)
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// DesCtrCrypt encrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8
|
||||
func DesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
|
||||
// len(key) should be 8
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCfbDecrypt decrypt data with key use DES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 8
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func DesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = pkcs7Padding(data, des.BlockSize)
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbDecrypt decrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 8
|
||||
func DesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:des.BlockSize]
|
||||
data = data[des.BlockSize:]
|
||||
if len(data)%des.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return decrypted
|
||||
|
||||
}
|
||||
72
cryptor/des_test.go
Normal file
72
cryptor/des_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestDesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
|
||||
|
||||
if string(desEcbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesEcbEncrypt/DesEcbDecrypt", data, data, string(desEcbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
|
||||
|
||||
if string(desCbcDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCbcEncrypt/DesCbcDecrypt", data, data, string(desCbcDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
|
||||
|
||||
if string(desCtrDeCrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCtrCrypt", data, data, string(desCtrDeCrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
|
||||
|
||||
if string(desCfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesCfbEncrypt/DesCfbDecrypt", data, data, string(desCfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
|
||||
|
||||
if string(desOfbDecrypt) != data {
|
||||
utils.LogFailedTestInfo(t, "DesOfbEncrypt/DesOfbDecrypt", data, data, string(desOfbDecrypt))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
116
cryptor/rsa.go
Normal file
116
cryptor/rsa.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
)
|
||||
|
||||
// GenerateRsaKey make a rsa private key, and return key file name
|
||||
// Generated key file is `rsa_private.pem` and `rsa_public.pem` in current path
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) {
|
||||
// private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
derText := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
|
||||
block := pem.Block{
|
||||
Type: "rsa private key",
|
||||
Bytes: derText,
|
||||
}
|
||||
|
||||
//file,err := os.Create("rsa_private.pem")
|
||||
file, err := os.Create(priKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
file.Close()
|
||||
|
||||
// public key
|
||||
publicKey := privateKey.PublicKey
|
||||
|
||||
derpText, err := x509.MarshalPKIXPublicKey(&publicKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block = pem.Block{
|
||||
Type: "rsa public key",
|
||||
Bytes: derpText,
|
||||
}
|
||||
|
||||
//file,err = os.Create("rsa_public.pem")
|
||||
file, err = os.Create(pubKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// RsaEncrypt encrypt data with ras algorithm
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
|
||||
file, err := os.Open(pubKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
file.Read(buf)
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubKey := pubInterface.(*rsa.PublicKey)
|
||||
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cipherText
|
||||
}
|
||||
|
||||
// RsaDecrypt decrypt data with ras algorithm
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
|
||||
file, err := os.Open(privateKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
defer file.Close()
|
||||
file.Read(buf)
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return plainText
|
||||
}
|
||||
19
cryptor/rsa_test.go
Normal file
19
cryptor/rsa_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestRsaEncrypt(t *testing.T) {
|
||||
GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
data := []byte("hello world")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
if string(data) != string(decrypted) {
|
||||
utils.LogFailedTestInfo(t, "RsaEncrypt/RsaDecrypt", string(data), string(data), string(decrypted))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
117
datetime/datetime.go
Normal file
117
datetime/datetime.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package datetime implements some functions to format date and time.
|
||||
|
||||
// Note:
|
||||
// 1. `format` param in FormatTimeToStr function should be as flow:
|
||||
//"yyyy-mm-dd hh:mm:ss"
|
||||
//"yyyy-mm-dd hh:mm"
|
||||
//"yyyy-mm-dd hh"
|
||||
//"yyyy-mm-dd"
|
||||
//"yyyy-mm"
|
||||
//"mm-dd"
|
||||
//"dd-mm-yy hh:mm:ss"
|
||||
//"yyyy/mm/dd hh:mm:ss"
|
||||
//"yyyy/mm/dd hh:mm"
|
||||
//"yyyy/mm/dd hh"
|
||||
//"yyyy/mm/dd"
|
||||
//"yyyy/mm"
|
||||
//"mm/dd"
|
||||
//"dd/mm/yy hh:mm:ss"
|
||||
//"yyyy"
|
||||
//"mm"
|
||||
//"hh:mm:ss"
|
||||
//"mm:ss"
|
||||
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var timeFormat map[string]string
|
||||
|
||||
func init() {
|
||||
timeFormat = map[string]string{
|
||||
"yyyy-mm-dd hh:mm:ss": "2006-01-02 15:04:05",
|
||||
"yyyy-mm-dd hh:mm": "2006-01-02 15:04",
|
||||
"yyyy-mm-dd hh": "2006-01-02 15:04",
|
||||
"yyyy-mm-dd": "2006-01-02",
|
||||
"yyyy-mm": "2006-01",
|
||||
"mm-dd": "01-02",
|
||||
"dd-mm-yy hh:mm:ss": "02-01-06 15:04:05",
|
||||
"yyyy/mm/dd hh:mm:ss": "2006/01/02 15:04:05",
|
||||
"yyyy/mm/dd hh:mm": "2006/01/02 15:04",
|
||||
"yyyy/mm/dd hh": "2006/01/02 15",
|
||||
"yyyy/mm/dd": "2006/01/02",
|
||||
"yyyy/mm": "2006/01",
|
||||
"mm/dd": "01/02",
|
||||
"dd/mm/yy hh:mm:ss": "02/01/06 15:04:05",
|
||||
"yyyy": "2006",
|
||||
"mm": "01",
|
||||
"hh:mm:ss": "15:04:05",
|
||||
"mm:ss": "04:05",
|
||||
}
|
||||
}
|
||||
|
||||
// AddMinute add or sub minute to the time
|
||||
func AddMinute(t time.Time, minute int64) time.Time {
|
||||
s := strconv.FormatInt(minute, 10)
|
||||
m, _ := time.ParseDuration(s + "m")
|
||||
return t.Add(m)
|
||||
}
|
||||
|
||||
// AddHour add or sub hour to the time
|
||||
func AddHour(t time.Time, hour int64) time.Time {
|
||||
s := strconv.FormatInt(hour, 10)
|
||||
h, _ := time.ParseDuration(s + "h")
|
||||
return t.Add(h)
|
||||
}
|
||||
|
||||
// AddDay add or sub day to the time
|
||||
func AddDay(t time.Time, day int64) time.Time {
|
||||
dayHours := day * 24
|
||||
d := strconv.FormatInt(dayHours, 10)
|
||||
h, _ := time.ParseDuration(d + "h")
|
||||
return t.Add(h)
|
||||
}
|
||||
|
||||
// GetNowDate return format yyyy-mm-dd of current date
|
||||
func GetNowDate() string {
|
||||
return time.Now().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// GetNowTime return format hh-mm-ss of current time
|
||||
func GetNowTime() string {
|
||||
return time.Now().Format("15:04:05")
|
||||
}
|
||||
|
||||
// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime
|
||||
func GetNowDateTime() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00)
|
||||
func GetZeroHourTimestamp() int64 {
|
||||
ts := time.Now().Format("2006-01-02")
|
||||
t, _ := time.Parse("2006-01-02", ts)
|
||||
return t.UTC().Unix() - 8*3600
|
||||
}
|
||||
|
||||
// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59)
|
||||
func GetNightTimestamp() int64 {
|
||||
return GetZeroHourTimestamp() + 86400 - 1
|
||||
}
|
||||
|
||||
// FormatTimeToStr convert time to string
|
||||
func FormatTimeToStr(t time.Time, format string) string {
|
||||
return t.Format(timeFormat[format])
|
||||
}
|
||||
|
||||
// FormatStrToTime convert string to time
|
||||
func FormatStrToTime(str, format string) time.Time {
|
||||
t, _ := time.Parse(timeFormat[format], str)
|
||||
return t
|
||||
}
|
||||
145
datetime/datetime_test.go
Normal file
145
datetime/datetime_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAddDay(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
after2Days := AddDay(now, 2)
|
||||
diff1 := after2Days.Sub(now)
|
||||
if diff1.Hours() != 48 {
|
||||
utils.LogFailedTestInfo(t, "AddDay", now, 48, diff1.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
before2Days := AddDay(now, -2)
|
||||
diff2 := before2Days.Sub(now)
|
||||
if diff2.Hours() != -48 {
|
||||
utils.LogFailedTestInfo(t, "AddDay", now, -48, diff2.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
func TestAddHour(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
after2Hours := AddHour(now, 2)
|
||||
diff1 := after2Hours.Sub(now)
|
||||
if diff1.Hours() != 2 {
|
||||
utils.LogFailedTestInfo(t, "AddHour", now, 2, diff1.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
before2Hours := AddHour(now, -2)
|
||||
diff2 := before2Hours.Sub(now)
|
||||
if diff2.Hours() != -2 {
|
||||
utils.LogFailedTestInfo(t, "AddHour", now, -2, diff2.Hours())
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddMinute(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
after2Minutes := AddMinute(now, 2)
|
||||
diff1 := after2Minutes.Sub(now)
|
||||
if diff1.Minutes() != 2 {
|
||||
utils.LogFailedTestInfo(t, "AddMinute", now, 2, diff1.Minutes())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
before2Minutes := AddMinute(now, -2)
|
||||
diff2 := before2Minutes.Sub(now)
|
||||
if diff2.Minutes() != -2 {
|
||||
utils.LogFailedTestInfo(t, "AddMinute", now, -2, diff2.Minutes())
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNowDate(t *testing.T) {
|
||||
date := GetNowDate()
|
||||
expected := time.Now().Format("2006-01-02")
|
||||
if date != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowDate", "", expected, date)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNotTime(t *testing.T) {
|
||||
ts := GetNowTime()
|
||||
expected := time.Now().Format("15:04:05")
|
||||
if ts != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowTime", "", expected, ts)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNowDateTime(t *testing.T) {
|
||||
ts := GetNowDateTime()
|
||||
expected := time.Now().Format("2006-01-02 15:04:05")
|
||||
if ts != expected {
|
||||
utils.LogFailedTestInfo(t, "GetNowDateTime", "", expected, ts)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
//todo
|
||||
//func TestGetZeroHourTimestamp(t *testing.T) {
|
||||
// ts := GetZeroHourTimestamp()
|
||||
// expected := time.Now().UTC().Unix() - 8*3600
|
||||
// if ts != expected {
|
||||
// utils.LogFailedTestInfo(t, "GetZeroHourTimestamp", "", expected, ts)
|
||||
// t.FailNow()
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestFormatTimeToStr(t *testing.T) {
|
||||
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
|
||||
cases := []string{
|
||||
"yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd",
|
||||
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
|
||||
"hh:mm:ss", "yyyy/mm"}
|
||||
|
||||
expected := []string{
|
||||
"2021-01-02 16:04:08", "2021-01-02",
|
||||
"02-01-21 16:04:08", "2021/01/02 16:04:08",
|
||||
"16:04:08", "2021/01"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := FormatTimeToStr(datetime, cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFormatStrToTime(t *testing.T) {
|
||||
formats := []string{
|
||||
"2006-01-02 15:04:05", "2006-01-02",
|
||||
"02-01-06 15:04:05", "2006/01/02 15:04:05",
|
||||
"2006/01"}
|
||||
cases := []string{
|
||||
"yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd",
|
||||
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
|
||||
"yyyy/mm"}
|
||||
|
||||
datetimeStr := []string{
|
||||
"2021-01-02 16:04:08", "2021-01-02",
|
||||
"02-01-21 16:04:08", "2021/01/02 16:04:08",
|
||||
"2021/01"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := FormatStrToTime(datetimeStr[i], cases[i])
|
||||
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
104
fileutil/file.go
Normal file
104
fileutil/file.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package fileutil implements some basic functions for file operations
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// IsFileExists checks if a file or directory exists
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CreateFile create a file in path
|
||||
func CreateFile(path string) bool {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
// IsFileExists checks if the path is directy or not
|
||||
func IsDir(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return file.IsDir()
|
||||
}
|
||||
|
||||
// RemoveFile remove the path file
|
||||
func RemoveFile(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// CopyFile copy src file to dest file
|
||||
func CopyFile(srcFilePath string, dstFilePath string) error {
|
||||
srcFile, err := os.Open(srcFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
distFile, err := os.Create(dstFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer distFile.Close()
|
||||
|
||||
var tmp = make([]byte, 1024*4)
|
||||
for {
|
||||
n, err := srcFile.Read(tmp)
|
||||
distFile.Write(tmp[:n])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListFileNames return all file names in the path
|
||||
func ListFileNames(path string) ([]string, error) {
|
||||
if !IsExist(path) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
fs, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
sz := len(fs)
|
||||
if sz == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
res := []string{}
|
||||
for i := 0; i < sz; i++ {
|
||||
if !fs[i].IsDir() {
|
||||
res = append(res, fs[i].Name())
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
99
fileutil/file_test.go
Normal file
99
fileutil/file_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsExist(t *testing.T) {
|
||||
cases := []string{"./", "./a.txt"}
|
||||
expected := []bool{true, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := IsExist(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsExist", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFile(t *testing.T) {
|
||||
f := "./text.txt"
|
||||
if CreateFile(f) {
|
||||
file, err := os.Open(f)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error: "+err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
if file.Name() != f {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, file.Name())
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDir(t *testing.T) {
|
||||
cases := []string{"./", "./a.txt"}
|
||||
expected := []bool{true, false}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
res := IsDir(cases[i])
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsDir", cases[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFile(t *testing.T) {
|
||||
f := "./text.txt"
|
||||
if CreateFile(f) {
|
||||
err := RemoveFile(f)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "RemoveFile", f, f, err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
utils.LogFailedTestInfo(t, "RemoveFile", f, f, "create file error")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
srcFile := "./text.txt"
|
||||
CreateFile(srcFile)
|
||||
|
||||
dstFile := "./text_copy.txt"
|
||||
|
||||
err := CopyFile(srcFile, dstFile)
|
||||
if err != nil {
|
||||
file, err := os.Open(dstFile)
|
||||
if err != nil {
|
||||
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, "create file error: "+err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
if file.Name() != dstFile {
|
||||
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, file.Name())
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListFileNames(t *testing.T) {
|
||||
filesInCurrentPath, err := ListFileNames("./")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
expected := []string{"file.go", "file_test.go"}
|
||||
if !reflect.DeepEqual(filesInCurrentPath, expected) {
|
||||
utils.LogFailedTestInfo(t, "ToChar", "./", expected, filesInCurrentPath)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
17
formatter/formatter.go
Normal file
17
formatter/formatter.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package formatter implements some functions to format string, struct.
|
||||
package formatter
|
||||
|
||||
import "strings"
|
||||
|
||||
// Comma add comma to number by every 3 numbers from right. ahead by symbol char
|
||||
func Comma(v interface{}, symbol string) string {
|
||||
s := numString(v)
|
||||
dotIndex := strings.Index(s, ".")
|
||||
if dotIndex != -1 {
|
||||
return symbol + commaString(s[:dotIndex]) + s[dotIndex:]
|
||||
}
|
||||
return symbol + commaString(s)
|
||||
}
|
||||
25
formatter/formatter_test.go
Normal file
25
formatter/formatter_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComma(t *testing.T) {
|
||||
comma(t, "", "","")
|
||||
comma(t, "aa", "","")
|
||||
comma(t, "123", "","123")
|
||||
comma(t, "12345", "","12,345")
|
||||
comma(t, 12345, "","12,345")
|
||||
comma(t, 12345, "$","$12,345")
|
||||
comma(t, 12345, "¥","¥12,345")
|
||||
comma(t, 12345.6789, "","12,345.6789")
|
||||
}
|
||||
|
||||
func comma(t *testing.T, test interface{}, symbol string, expected interface{}) {
|
||||
res:= Comma(test, symbol)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Comma", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
39
formatter/formatter_util.go
Normal file
39
formatter/formatter_util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func commaString(s string) string {
|
||||
if len(s) <= 3 {
|
||||
return s
|
||||
}
|
||||
return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:])
|
||||
}
|
||||
|
||||
func numString(value interface{}) string {
|
||||
switch reflect.TypeOf(value).Kind() {
|
||||
case reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64:
|
||||
return fmt.Sprintf("%v", value)
|
||||
case reflect.String: {
|
||||
sv := fmt.Sprintf("%v", value)
|
||||
if strings.Contains(sv, ".") {
|
||||
_, err := strconv.ParseFloat(sv, 64)
|
||||
if err == nil {
|
||||
return sv
|
||||
}
|
||||
}else {
|
||||
_, err := strconv.ParseInt(sv, 10, 64)
|
||||
if err == nil {
|
||||
return sv
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
83
netutil/net.go
Normal file
83
netutil/net.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetInternalIp return internal ipv4
|
||||
func GetInternalIp() string {
|
||||
addr, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
for _, a := range addr {
|
||||
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
return ipNet.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetPublicIpInfo return public ip information
|
||||
// return the PublicIpInfo struct
|
||||
func GetPublicIpInfo() (*PublicIpInfo, error) {
|
||||
resp, err := http.Get("http://ip-api.com/json/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ip PublicIpInfo
|
||||
err = json.Unmarshal(body, &ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ip, nil
|
||||
}
|
||||
|
||||
type PublicIpInfo struct {
|
||||
Status string `json:"status"`
|
||||
Country string `json:"country"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
Region string `json:"region"`
|
||||
RegionName string `json:"regionName"`
|
||||
City string `json:"city"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
Isp string `json:"isp"`
|
||||
Org string `json:"org"`
|
||||
As string `json:"as"`
|
||||
Ip string `json:"query"`
|
||||
}
|
||||
|
||||
// IsPublicIP verify a ip is public or not
|
||||
func IsPublicIP(IP net.IP) bool {
|
||||
if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
|
||||
return false
|
||||
}
|
||||
if ip4 := IP.To4(); ip4 != nil {
|
||||
switch {
|
||||
case ip4[0] == 10:
|
||||
return false
|
||||
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
|
||||
return false
|
||||
case ip4[0] == 192 && ip4[1] == 168:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
46
netutil/net_test.go
Normal file
46
netutil/net_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetInternalIp(t *testing.T) {
|
||||
internalIp := GetInternalIp()
|
||||
ip := net.ParseIP(internalIp)
|
||||
if ip == nil {
|
||||
utils.LogFailedTestInfo(t, "GetInternalIp", "GetInternalIp", "", ip)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicIpInfo(t *testing.T) {
|
||||
publicIpInfo, err := GetPublicIpInfo()
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Printf("public ip info is: %+v \n", *publicIpInfo)
|
||||
}
|
||||
|
||||
func TestIsPublicIP(t *testing.T) {
|
||||
ips := []net.IP{
|
||||
net.ParseIP("127.0.0.1"),
|
||||
net.ParseIP("192.168.0.1"),
|
||||
net.ParseIP("10.91.210.131"),
|
||||
net.ParseIP("172.20.16.1"),
|
||||
net.ParseIP("36.112.24.10"),
|
||||
}
|
||||
|
||||
expected := []bool{false, false, false, false, true}
|
||||
|
||||
for i := 0; i < len(ips); i++ {
|
||||
res := IsPublicIP(ips[i])
|
||||
|
||||
if res != expected[i] {
|
||||
utils.LogFailedTestInfo(t, "IsPublicIP", ips[i], expected[i], res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
67
netutil/request.go
Normal file
67
netutil/request.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package netutil implements some basic functions to send http request and get ip info.
|
||||
// Note:
|
||||
// HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `url` is required.
|
||||
// HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `params` is variable, the order is:
|
||||
// params[0] is header which type should be http.Header or map[string]string,
|
||||
// params[1] is query param which type should be url.Values or map[string]interface{},
|
||||
// params[2] is post body which type should be []byte.
|
||||
// params[3] is http client which type should be http.Client.
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//HttpGet send get http request
|
||||
func HttpGet(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodGet, url, params...)
|
||||
}
|
||||
|
||||
//HttpPost send post http request
|
||||
func HttpPost(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPost, url, params...)
|
||||
}
|
||||
|
||||
//HttpPut send put http request
|
||||
func HttpPut(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPut, url, params...)
|
||||
}
|
||||
|
||||
//HttpDelete send delete http request
|
||||
func HttpDelete(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodDelete, url, params...)
|
||||
}
|
||||
|
||||
// HttpPatch send patch http request
|
||||
func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
|
||||
return request(http.MethodPatch, url, params...)
|
||||
}
|
||||
|
||||
// ConvertMapToQueryString convert map to sorted url query string
|
||||
func ConvertMapToQueryString(param map[string]interface{}) string {
|
||||
if param == nil {
|
||||
return ""
|
||||
}
|
||||
var keys []string
|
||||
for key := range param {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var build strings.Builder
|
||||
for i, v := range keys {
|
||||
build.WriteString(v)
|
||||
build.WriteString("=")
|
||||
build.WriteString(fmt.Sprintf("%v", param[v]))
|
||||
if i != len(keys)-1 {
|
||||
build.WriteString("&")
|
||||
}
|
||||
}
|
||||
return build.String()
|
||||
}
|
||||
98
netutil/request_test.go
Normal file
98
netutil/request_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHttpGet(t *testing.T) {
|
||||
url := "https://gutendex.com/books?"
|
||||
queryParams := make(map[string]interface{})
|
||||
queryParams["ids"] = "1"
|
||||
|
||||
resp, err := HttpGet(url, nil, queryParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
|
||||
}
|
||||
|
||||
func TestHttpPost(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users"
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
user := User{
|
||||
"test",
|
||||
"test@test.com",
|
||||
}
|
||||
bodyParams, _ := json.Marshal(user)
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
resp, err := HttpPost(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpPut(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
user := User{
|
||||
"test",
|
||||
"test@test.com",
|
||||
}
|
||||
bodyParams, _ := json.Marshal(user)
|
||||
header := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
resp, err := HttpPut(url, header, nil, bodyParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestHttpDelete(t *testing.T) {
|
||||
url := "http://public-api-v1.aspirantzhang.com/users/10420"
|
||||
resp, err := HttpDelete(url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.FailNow()
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println("response: ", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
func TestConvertMapToQueryString(t *testing.T) {
|
||||
var m = map[string]interface{}{
|
||||
"c": 3,
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
|
||||
expected := "a=1&b=2&c=3"
|
||||
r := ConvertMapToQueryString(m)
|
||||
if r != expected {
|
||||
utils.LogFailedTestInfo(t, "ConvertMapToQueryString", m, expected, r)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
181
netutil/request_util.go
Normal file
181
netutil/request_util.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func request(method, reqUrl string, params ...interface{}) (*http.Response, error) {
|
||||
if len(reqUrl) == 0 {
|
||||
return nil, errors.New("url should be specified")
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: method,
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
switch len(params) {
|
||||
case 0:
|
||||
err := setUrl(req, reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 1:
|
||||
err := setUrl(req, reqUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 2:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 3:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setBodyByte(req, params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 4:
|
||||
err := setHeader(req, params[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setQueryParam(req, reqUrl, params[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setBodyByte(req, params[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err = getClient(params[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resp, e := client.Do(req)
|
||||
return resp, e
|
||||
}
|
||||
|
||||
func setHeader(req *http.Request, header interface{}) error {
|
||||
if header != nil {
|
||||
switch v := header.(type) {
|
||||
case map[string]string:
|
||||
for k := range v {
|
||||
req.Header.Add(k, v[k])
|
||||
}
|
||||
case http.Header:
|
||||
for k, vv := range v {
|
||||
for _, vvv := range vv {
|
||||
req.Header.Add(k, vvv)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("header params type should be http.Header or map[string]string")
|
||||
}
|
||||
}
|
||||
|
||||
if host := req.Header.Get("Host"); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func setUrl(req *http.Request, reqUrl string) error {
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
return nil
|
||||
}
|
||||
func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error {
|
||||
var values url.Values
|
||||
if queryParam != nil {
|
||||
switch v := queryParam.(type) {
|
||||
case map[string]interface{}:
|
||||
values = url.Values{}
|
||||
for k := range v {
|
||||
values.Set(k, fmt.Sprintf("%s", v[k]))
|
||||
}
|
||||
case url.Values:
|
||||
values = v
|
||||
default:
|
||||
return errors.New("query params type should be url.Values or map[string]interface{}")
|
||||
}
|
||||
}
|
||||
|
||||
// set url
|
||||
if values != nil {
|
||||
if !strings.Contains(reqUrl, "?") {
|
||||
reqUrl = reqUrl + "?" + values.Encode()
|
||||
} else {
|
||||
reqUrl = reqUrl + "&" + values.Encode()
|
||||
}
|
||||
}
|
||||
u, err := url.Parse(reqUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.URL = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBodyByte(req *http.Request, body interface{}) error {
|
||||
if body != nil {
|
||||
var bodyByte []byte
|
||||
if body != nil {
|
||||
switch v := body.(type) {
|
||||
case []byte:
|
||||
bodyByte = v
|
||||
default:
|
||||
return errors.New("body type should be []byte")
|
||||
}
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(bodyByte))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getClient(client interface{}) (*http.Client, error) {
|
||||
c := http.Client{}
|
||||
if client != nil {
|
||||
switch v := client.(type) {
|
||||
case http.Client:
|
||||
c = v
|
||||
default:
|
||||
return nil, errors.New("client type should be http.Client")
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
50
random/random.go
Normal file
50
random/random.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license.
|
||||
|
||||
// Package random implements some basic functions to generate random int and string.
|
||||
package random
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"io"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RandString generate random string
|
||||
// see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
|
||||
func RandString(length int) string {
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
b := make([]byte, length)
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := range b {
|
||||
b[i] = letters[r.Int63()%int64(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// RandInt generate random int between min and max, maybe min, not be max
|
||||
func RandInt(min, max int) int {
|
||||
if min == max {
|
||||
return min
|
||||
}
|
||||
if max < min {
|
||||
min, max = max, min
|
||||
}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
return r.Intn(max-min) + min
|
||||
}
|
||||
|
||||
// RandBytes generate random byte slice
|
||||
func RandBytes(length int) []byte {
|
||||
if length < 1 {
|
||||
return nil
|
||||
}
|
||||
b := make([]byte, length)
|
||||
|
||||
if _, err := io.ReadFull(crand.Reader, b); err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
46
random/random_test.go
Normal file
46
random/random_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/utils"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandString(t *testing.T) {
|
||||
randStr := RandString(6)
|
||||
fmt.Println(randStr)
|
||||
pattern := `^[a-zA-Z]+$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
|
||||
if len(randStr) != 6 || !reg.MatchString(randStr) {
|
||||
utils.LogFailedTestInfo(t, "RandString", "RandString(6)", "RandString(6) should be 6 letters ", randStr)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandInt(t *testing.T) {
|
||||
randInt := RandInt(1, 10)
|
||||
|
||||
if randInt < 1 || randInt >= 10 {
|
||||
utils.LogFailedTestInfo(t, "RandInt", "RandInt(1, 10)", "RandInt(1, 10) should between [1, 10) ", randInt)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandBytes(t *testing.T) {
|
||||
randBytes := RandBytes(4)
|
||||
|
||||
if len(randBytes) != 4 {
|
||||
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(randBytes)
|
||||
et := v.Type().Elem()
|
||||
if v.Kind() != reflect.Slice || et.Kind() != reflect.Uint8 {
|
||||
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
411
slice/slice.go
Normal file
411
slice/slice.go
Normal file
@@ -0,0 +1,411 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package slice implements some functions to manipulate slice.
|
||||
package slice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Contain check if the value is in the slice or not
|
||||
func Contain(slice interface{}, value interface{}) bool {
|
||||
v := reflect.ValueOf(slice)
|
||||
switch reflect.TypeOf(slice).Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if v.Index(i).Interface() == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if v.MapIndex(reflect.ValueOf(value)).IsValid() {
|
||||
return true
|
||||
}
|
||||
case reflect.String:
|
||||
s := fmt.Sprintf("%v", slice)
|
||||
ss := fmt.Sprintf("%v", value)
|
||||
return strings.Contains(s, ss)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Chunk creates an slice of elements split into groups the length of `size`.
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} {
|
||||
var res [][]interface{}
|
||||
|
||||
if len(slice) == 0 || size <= 0 {
|
||||
return res
|
||||
}
|
||||
|
||||
length := len(slice)
|
||||
if size == 1 || size >= length {
|
||||
for _, v := range slice {
|
||||
var tmp []interface{}
|
||||
tmp = append(tmp, v)
|
||||
res = append(res, tmp)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// divide slice equally
|
||||
divideNum := length/size + 1
|
||||
for i := 0; i < divideNum; i++ {
|
||||
if i == divideNum-1 {
|
||||
if len(slice[i*size:]) > 0 {
|
||||
res = append(res, slice[i*size:])
|
||||
}
|
||||
} else {
|
||||
res = append(res, slice[i*size:(i+1)*size])
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Difference creates an slice of whose element not included in the other given slice
|
||||
func Difference(slice1, slice2 interface{}) interface{} {
|
||||
v := sliceValue(slice1)
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vi := v.Index(i).Interface()
|
||||
if !Contain(slice2, vi) {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(v.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(v.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Filter iterates over elements of slice, returning an slice of all elements `signature` returns truthy for.
|
||||
// The function signature should be func(index int, value interface{}) bool .
|
||||
func Filter(slice, function interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
||||
if flag.Bool() {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(sv.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Map creates an slice of values by running each element of `slice` thru `function`.
|
||||
// The function signature should be func(index int, value interface{}) interface{}.
|
||||
func Map(slice, function interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
||||
panic("Map function must be of type func(int, " + elemType.String() + ")" + elemType.String())
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
res.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0])
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Reduce creates an slice of values by running each element of `slice` thru `function`.
|
||||
// The function signature should be func(index int, value1, value2 interface{}) interface{} .
|
||||
func Reduce(slice, function, zero interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
len := sv.Len()
|
||||
if len == 0 {
|
||||
return zero
|
||||
} else if len == 1 {
|
||||
return sv.Index(0)
|
||||
}
|
||||
|
||||
elementType := sv.Type().Elem()
|
||||
fn := functionValue(function)
|
||||
|
||||
if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) {
|
||||
t := elementType.String()
|
||||
panic("Reduce function must be of type func(int, " + t + ", " + t + ")" + t)
|
||||
}
|
||||
|
||||
var params [3]reflect.Value
|
||||
params[0] = reflect.ValueOf(0)
|
||||
params[1] = sv.Index(0)
|
||||
params[2] = sv.Index(1)
|
||||
|
||||
res := fn.Call(params[:])[0]
|
||||
|
||||
for i := 2; i < len; i++ {
|
||||
params[0] = reflect.ValueOf(i)
|
||||
params[1] = res
|
||||
params[2] = sv.Index(i)
|
||||
res = fn.Call(params[:])[0]
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// InterfaceSlice convert param to slice of interface.
|
||||
func InterfaceSlice(slice interface{}) []interface{} {
|
||||
sv := sliceValue(slice)
|
||||
if sv.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make([]interface{}, sv.Len())
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
res[i] = sv.Index(i).Interface()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// StringSlice convert param to slice of string.
|
||||
func StringSlice(slice interface{}) []string {
|
||||
var res []string
|
||||
|
||||
v := sliceValue(slice)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
res = append(res, fmt.Sprint(v.Index(i)))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// IntSlice convert param to slice of int.
|
||||
func IntSlice(slice interface{}) ([]int, error) {
|
||||
var res []int
|
||||
|
||||
sv := sliceValue(slice)
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
res = append(res, v)
|
||||
default:
|
||||
return nil, errors.New("InvalidSliceElementType")
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ConvertSlice convert original slice to new data type element of slice.
|
||||
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} {
|
||||
sv := sliceValue(originalSlice)
|
||||
if newSliceType.Kind() != reflect.Slice {
|
||||
panic(fmt.Sprintf("Invalid newSliceType(non-slice type of type %T)", newSliceType))
|
||||
}
|
||||
|
||||
newSlice := reflect.New(newSliceType)
|
||||
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(newSlice.Pointer()))
|
||||
|
||||
var newElemSize = int(sv.Type().Elem().Size()) / int(newSliceType.Elem().Size())
|
||||
|
||||
hdr.Cap = sv.Cap() * newElemSize
|
||||
hdr.Len = sv.Len() * newElemSize
|
||||
hdr.Data = sv.Pointer()
|
||||
|
||||
return newSlice.Elem().Interface()
|
||||
}
|
||||
|
||||
// DeleteByIndex delete the element of slice from start index to end index - 1.
|
||||
// Delete i: s = append(s[:i], s[i+1:]...)
|
||||
// Delete i to j: s = append(s[:i], s[j:]...)
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
i := start
|
||||
if v.Len() == 0 || i < 0 || i > v.Len() {
|
||||
return nil, errors.New("InvalidStartIndex")
|
||||
}
|
||||
if len(end) > 0 {
|
||||
j := end[0]
|
||||
if j <= i || j > v.Len() {
|
||||
return nil, errors.New("InvalidEndIndex")
|
||||
}
|
||||
v = reflect.AppendSlice(v.Slice(0, i), v.Slice(j, v.Len()))
|
||||
} else {
|
||||
v = reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len()))
|
||||
}
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// InsertByIndex insert the element into slice at index.
|
||||
// Insert value: s = append(s[:i], append([]T{x}, s[i:]...)...)
|
||||
// Insert slice: a = append(a[:i], append(b, a[i:]...)...)
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
|
||||
if index < 0 || index > v.Len() {
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
|
||||
// value is slice
|
||||
vv := reflect.ValueOf(value)
|
||||
if vv.Kind() == reflect.Slice {
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value).Elem() {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
v = reflect.AppendSlice(v.Slice(0, index), reflect.AppendSlice(vv.Slice(0, vv.Len()), v.Slice(index, v.Len())))
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// value is not slice
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
if index == v.Len() {
|
||||
return reflect.Append(v, reflect.ValueOf(value)).Interface(), nil
|
||||
}
|
||||
|
||||
v = reflect.AppendSlice(v.Slice(0, index+1), v.Slice(index, v.Len()))
|
||||
v.Index(index).Set(reflect.ValueOf(value))
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// UpdateByIndex update the slice element at index.
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
|
||||
if index < 0 || index >= v.Len() {
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
|
||||
v.Index(index).Set(reflect.ValueOf(value))
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// Unique remove duplicate elements in slice.
|
||||
func Unique(slice interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
if sv.Len() == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
var res []interface{}
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
flag := true
|
||||
for j := range res {
|
||||
if v == res[j] {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
// if use map filter, the result slice element order is random, not same as origin slice
|
||||
//mp := make(map[interface{}]bool)
|
||||
//for i := 0; i < sv.Len(); i++ {
|
||||
// v := sv.Index(i).Interface()
|
||||
// mp[v] = true
|
||||
//}
|
||||
//
|
||||
//var res []interface{}
|
||||
//for k := range mp {
|
||||
// res = append(res, mp[k])
|
||||
//}
|
||||
//return res
|
||||
|
||||
}
|
||||
|
||||
// ReverseSlice return slice of element order is reversed to the given slice
|
||||
func ReverseSlice(slice interface{}) {
|
||||
v := sliceValue(slice)
|
||||
swp := reflect.Swapper(v.Interface())
|
||||
for i, j := 0, v.Len()-1; i < j; i, j = i+1, j-1 {
|
||||
swp(i, j)
|
||||
}
|
||||
}
|
||||
|
||||
// SortByField return sorted slice by field
|
||||
// Slice element should be struct, field type should be int, uint, string, or bool
|
||||
// default sortType is ascending (asc), if descending order, set sortType to desc
|
||||
func SortByField(slice interface{}, field string, sortType ...string) error {
|
||||
v := sliceValue(slice)
|
||||
t := v.Type().Elem()
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", slice)
|
||||
}
|
||||
|
||||
// Find the field.
|
||||
sf, ok := t.FieldByName(field)
|
||||
if !ok {
|
||||
return fmt.Errorf("field name %s not found", field)
|
||||
}
|
||||
|
||||
// Create a less function based on the field's kind.
|
||||
var less func(a, b reflect.Value) bool
|
||||
switch sf.Type.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||
case reflect.Float32, reflect.Float64:
|
||||
less = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
|
||||
case reflect.String:
|
||||
less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||
case reflect.Bool:
|
||||
less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
|
||||
default:
|
||||
return fmt.Errorf("field type %s not supported", sf.Type)
|
||||
}
|
||||
|
||||
sort.Slice(slice, func(i, j int) bool {
|
||||
a := v.Index(i)
|
||||
b := v.Index(j)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
a = a.Elem()
|
||||
b = b.Elem()
|
||||
}
|
||||
a = a.FieldByIndex(sf.Index)
|
||||
b = b.FieldByIndex(sf.Index)
|
||||
return less(a, b)
|
||||
})
|
||||
|
||||
if sortType[0] == "desc" {
|
||||
ReverseSlice(slice)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
438
slice/slice_test.go
Normal file
438
slice/slice_test.go
Normal file
@@ -0,0 +1,438 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestContain(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d"}
|
||||
contain(t, t1, "a", true)
|
||||
contain(t, t1, "e", false)
|
||||
|
||||
var t2 []string
|
||||
contain(t, t2, "1", false)
|
||||
}
|
||||
|
||||
func contain(t *testing.T, test interface{}, value interface{}, expected bool) {
|
||||
res := Contain(test, value)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Contain", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestChunk(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
r1 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 1, r1)
|
||||
|
||||
r2 := [][]interface{}{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 2, r2)
|
||||
|
||||
r3 := [][]interface{}{
|
||||
{"a", "b", "c"},
|
||||
{"d", "e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 3, r3)
|
||||
|
||||
r4 := [][]interface{}{
|
||||
{"a", "b", "c", "d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 4, r4)
|
||||
|
||||
r5 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 5, r5)
|
||||
|
||||
}
|
||||
|
||||
func chunk(t *testing.T, test []interface{}, num int, expected [][]interface{}) {
|
||||
res := Chunk(test, num)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "Chunk", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertSlice(t *testing.T) {
|
||||
//t1 := []string{"1","2"}
|
||||
//aInt, _ := strconv.ParseInt("1", 10, 64)
|
||||
//bInt, _ := strconv.ParseInt("2", 10, 64)
|
||||
//expected :=[]int64{aInt, bInt}
|
||||
//
|
||||
//a := ConvertSlice(t1, reflect.TypeOf(expected))
|
||||
//if !reflect.DeepEqual(a, expected) {
|
||||
// utils.LogFailedTestInfo(t, "ConvertSlice", t1, expected, a)
|
||||
// t.FailNow()
|
||||
//}
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
even := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
e1 := []int{2, 4}
|
||||
r1 := Filter(s1, even)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Filter", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
students := []student{
|
||||
{"a", 10},
|
||||
{"b", 11},
|
||||
{"c", 12},
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
filterFunc := func(i int, s student) bool {
|
||||
return s.age > 12
|
||||
}
|
||||
|
||||
r2 := Filter(students, filterFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
multiplyTwo := func(i, num int) int {
|
||||
return num * 2
|
||||
}
|
||||
e1 := []int{2, 4, 6, 8}
|
||||
r1 := Map(s1, multiplyTwo)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Map", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
students := []student{
|
||||
{"a", 1},
|
||||
{"b", 2},
|
||||
{"c", 3},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
{"a", 11},
|
||||
{"b", 12},
|
||||
{"c", 13},
|
||||
}
|
||||
mapFunc := func(i int, s student) student {
|
||||
s.age += 10
|
||||
return s
|
||||
}
|
||||
r2 := Map(students, mapFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
f1 := func(i, v1, v2 int) int {
|
||||
return v1 + v2
|
||||
}
|
||||
e1 := 10
|
||||
r1 := Reduce(s1, f1, 0)
|
||||
if e1 != r1 {
|
||||
utils.LogFailedTestInfo(t, "Reduce", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// failed Reduce function should be func(i int, v1, v2 int) int
|
||||
//s1 := []int{1, 2, 3, 4}
|
||||
//f1 := func(i string, v1, v2 int) int { //i should be int
|
||||
// return v1+v2
|
||||
//}
|
||||
//e1 := 10
|
||||
//r1 := Reduce(s1, f1, 0)
|
||||
//if e1 != r1 {
|
||||
// utils.LogFailedTestInfo(t, "Reduce", s1, e1, r1)
|
||||
// t.FailNow()
|
||||
//}
|
||||
}
|
||||
|
||||
func TestIntSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, 1, 2, 3, 4, 5)
|
||||
expect := []int{1, 2, 3, 4, 5}
|
||||
intSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func intSlice(t *testing.T, test interface{}, expected []int) {
|
||||
res, err := IntSlice(test)
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "IntSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, "a", "b", "c", "d", "e")
|
||||
expect := []string{"a", "b", "c", "d", "e"}
|
||||
stringSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func stringSlice(t *testing.T, test interface{}, expected []string) {
|
||||
res := StringSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "StringSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceSlice(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
expect := []interface{}{"a", "b", "c", "d", "e"}
|
||||
interfaceSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func interfaceSlice(t *testing.T, test interface{}, expected []interface{}) {
|
||||
res := InterfaceSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InterfaceSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteByIndex(t *testing.T) {
|
||||
origin := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
r1 := []string{"b", "c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 0, r1)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r2 := []string{"a", "b", "c", "e"}
|
||||
deleteByIndex(t, origin, t1, 3, 0, r2)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r3 := []string{"a", "b", "c", "d"}
|
||||
deleteByIndex(t, origin, t1, 4, 0, r3)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r4 := []string{"c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 2, r4)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r5 := []string{} // var r5 []string{} failed
|
||||
deleteByIndex(t, origin, t1, 0, 5, r5)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r6 := []string{"a", "c", "d","e"}
|
||||
//deleteByIndex(t, origin, t1, 1, 1, r6)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r7 := []string{}
|
||||
//deleteByIndex(t, origin, t1, 0, 6, r7)
|
||||
}
|
||||
|
||||
func deleteByIndex(t *testing.T, origin, test interface{}, start, end int, expected interface{}) {
|
||||
var res interface{}
|
||||
var err error
|
||||
if end != 0 {
|
||||
res, err = DeleteByIndex(test, start, end)
|
||||
} else {
|
||||
res, err = DeleteByIndex(test, start)
|
||||
}
|
||||
if err != nil {
|
||||
t.Error("DeleteByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "DeleteByIndex", origin, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertByIndex(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c"}
|
||||
|
||||
r1 := []string{"1", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, "1", r1)
|
||||
|
||||
r2 := []string{"a", "1", "b", "c"}
|
||||
insertByIndex(t, t1, 1, "1", r2)
|
||||
|
||||
r3 := []string{"a", "b", "c", "1"}
|
||||
insertByIndex(t, t1, 3, "1", r3)
|
||||
|
||||
r4 := []string{"1", "2", "3", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, []string{"1", "2", "3"}, r4)
|
||||
|
||||
r5 := []string{"a", "1", "2", "3", "b", "c"}
|
||||
insertByIndex(t, t1, 1, []string{"1", "2", "3"}, r5)
|
||||
|
||||
r6 := []string{"a", "b", "1", "2", "3", "c"}
|
||||
insertByIndex(t, t1, 2, []string{"1", "2", "3"}, r6)
|
||||
|
||||
r7 := []string{"a", "b", "c", "1", "2", "3"}
|
||||
insertByIndex(t, t1, 3, []string{"1", "2", "3"}, r7)
|
||||
}
|
||||
|
||||
func insertByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := InsertByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("InsertByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InsertByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateByIndex(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c"}
|
||||
r1 := []string{"1", "b", "c"}
|
||||
updateByIndex(t, t1, 0, "1", r1)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r2 := []string{"a", "1", "c"}
|
||||
updateByIndex(t, t1, 1, "1", r2)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r3 := []string{"a", "b", "1"}
|
||||
updateByIndex(t, t1, 2, "1", r3)
|
||||
|
||||
//failed
|
||||
//t1 = []string{"a","b","c"}
|
||||
//r4 := []string{"a", "b", "1"}
|
||||
//updateByIndex(t, t1, 3, "1", r4)
|
||||
|
||||
}
|
||||
|
||||
func updateByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := UpdateByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("UpdateByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "UpdateByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnique(t *testing.T) {
|
||||
t1 := []int{1, 2, 2, 3}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1, _ := IntSlice(Unique(t1))
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
t2 := []string{"a", "a", "b", "c"}
|
||||
e2 := []string{"a", "b", "c"}
|
||||
r2 := StringSlice(Unique(t2))
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseSlice(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
e1 := []int{5, 4, 3, 2, 1}
|
||||
ReverseSlice(s1)
|
||||
if !reflect.DeepEqual(s1, e1) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s1, e1, s1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
s2 := []string{"a", "b", "c", "d", "e"}
|
||||
e2 := []string{"e", "d", "c", "b", "a"}
|
||||
ReverseSlice(s2)
|
||||
if !reflect.DeepEqual(s2, e2) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s2, e2, s2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifference(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
s2 := []int{4, 5, 6}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1 := Difference(s1, s2)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Difference", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortByField(t *testing.T) {
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
students := []student{
|
||||
{"a", 10},
|
||||
{"b", 15},
|
||||
{"c", 5},
|
||||
{"d", 6},
|
||||
}
|
||||
|
||||
sortByAge := []student{
|
||||
{"b", 15},
|
||||
{"a", 10},
|
||||
{"d", 6},
|
||||
{"c", 5},
|
||||
}
|
||||
|
||||
err := SortByField(students, "age", "desc")
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(students, sortByAge) {
|
||||
utils.LogFailedTestInfo(t, "SortByField", students, sortByAge, students)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
54
slice/slice_util.go
Normal file
54
slice/slice_util.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// sliceValue return the reflect value of a slice
|
||||
func sliceValue(slice interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(slice)
|
||||
if v.Kind() != reflect.Slice {
|
||||
panic(fmt.Sprintf("Invalid slice type, value of type %T", slice))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// functionValue return the reflect value of a function
|
||||
func functionValue(function interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(function)
|
||||
if v.Kind() != reflect.Func {
|
||||
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// checkSliceCallbackFuncSignature Check func sign : s :[]type1{} -> func(i int, data type1) type2
|
||||
// see https://coolshell.cn/articles/21164.html#%E6%B3%9B%E5%9E%8BMap-Reduce
|
||||
func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bool {
|
||||
//Check it is a function
|
||||
if fn.Kind() != reflect.Func {
|
||||
return false
|
||||
}
|
||||
// NumIn() - returns a function type's input parameter count.
|
||||
// NumOut() - returns a function type's output parameter count.
|
||||
if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {
|
||||
return false
|
||||
}
|
||||
// In() - returns the type of a function type's i'th input parameter.
|
||||
// first input param type should be int
|
||||
if fn.Type().In(0) != reflect.TypeOf(1) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(types)-1; i++ {
|
||||
if fn.Type().In(i) != types[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Out() - returns the type of a function type's i'th output parameter.
|
||||
outType := types[len(types)-1]
|
||||
if outType != nil && fn.Type().Out(0) != outType {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
217
strutil/string.go
Normal file
217
strutil/string.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package strutil implements some functions to manipulate string.
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CamelCase covert string to camelCase string.
|
||||
func CamelCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
res := ""
|
||||
blankSpace := " "
|
||||
regex, _ := regexp.Compile("[-_&]+")
|
||||
ss := regex.ReplaceAllString(s, blankSpace)
|
||||
for i, v := range strings.Split(ss, blankSpace) {
|
||||
vv := []rune(v)
|
||||
if i == 0 {
|
||||
if vv[i] >= 65 && vv[i] <= 96 {
|
||||
vv[0] += 32
|
||||
}
|
||||
res += string(vv)
|
||||
} else {
|
||||
res += Capitalize(v)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Capitalize converts the first character of a string to upper case and the remaining to lower case.
|
||||
func Capitalize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
res := ""
|
||||
for i, v := range []rune(s) {
|
||||
if i == 0 {
|
||||
if v >= 97 && v <= 122 {
|
||||
v -= 32
|
||||
}
|
||||
res += string(v)
|
||||
} else {
|
||||
res += strings.ToLower(string(v))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// LowerFirst converts the first character of string to lower case.
|
||||
func LowerFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
res := ""
|
||||
for i, v := range []rune(s) {
|
||||
if i == 0 {
|
||||
if v >= 65 && v <= 96 {
|
||||
v += 32
|
||||
res += string(v)
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
} else {
|
||||
res += string(v)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// PadEnd pads string on the right side if it's shorter than size.
|
||||
// Padding characters are truncated if they exceed size.
|
||||
func PadEnd(source string, size int, padStr string) string {
|
||||
len1 := len(source)
|
||||
len2 := len(padStr)
|
||||
|
||||
if len1 >= size {
|
||||
return source
|
||||
}
|
||||
|
||||
fill := ""
|
||||
if len2 >= size-len1 {
|
||||
fill = padStr[0 : size-len1]
|
||||
} else {
|
||||
fill = strings.Repeat(padStr, size-len1)
|
||||
}
|
||||
return source + fill[0:size-len1]
|
||||
}
|
||||
|
||||
// PadStart pads string on the left side if it's shorter than size.
|
||||
// Padding characters are truncated if they exceed size.
|
||||
func PadStart(source string, size int, padStr string) string {
|
||||
len1 := len(source)
|
||||
len2 := len(padStr)
|
||||
|
||||
if len1 >= size {
|
||||
return source
|
||||
}
|
||||
|
||||
fill := ""
|
||||
if len2 >= size-len1 {
|
||||
fill = padStr[0 : size-len1]
|
||||
} else {
|
||||
fill = strings.Repeat(padStr, size-len1)
|
||||
}
|
||||
return fill[0:size-len1] + source
|
||||
}
|
||||
|
||||
// KebabCase covert string to kebab-case
|
||||
func KebabCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`[\W|_]+`)
|
||||
blankSpace := " "
|
||||
match := regex.ReplaceAllString(s, blankSpace)
|
||||
rs := strings.Split(match, blankSpace)
|
||||
|
||||
var res []string
|
||||
for _, v := range rs {
|
||||
splitWords := splitWordsToLower(v)
|
||||
if len(splitWords) > 0 {
|
||||
res = append(res, splitWords...)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(res, "-")
|
||||
}
|
||||
|
||||
// SnakeCase covert string to snake_case
|
||||
func SnakeCase(s string) string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`[\W|_]+`)
|
||||
blankSpace := " "
|
||||
match := regex.ReplaceAllString(s, blankSpace)
|
||||
rs := strings.Split(match, blankSpace)
|
||||
|
||||
var res []string
|
||||
for _, v := range rs {
|
||||
splitWords := splitWordsToLower(v)
|
||||
if len(splitWords) > 0 {
|
||||
res = append(res, splitWords...)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(res, "_")
|
||||
}
|
||||
|
||||
// Before create substring in source string before position when char first appear
|
||||
func Before(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
return s
|
||||
}
|
||||
i := strings.Index(s, char)
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// BeforeLast create substring in source string before position when char last appear
|
||||
func BeforeLast(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
return s
|
||||
}
|
||||
i := strings.LastIndex(s, char)
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// After create substring in source string after position when char first appear
|
||||
func After(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
return s
|
||||
}
|
||||
i := strings.Index(s, char)
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
// AfterLast create substring in source string after position when char last appear
|
||||
func AfterLast(s, char string) string {
|
||||
if s == "" || char == "" {
|
||||
return s
|
||||
}
|
||||
i := strings.LastIndex(s, char)
|
||||
return s[i+len(char):]
|
||||
}
|
||||
|
||||
// IsString check if the value data type is string or not.
|
||||
func IsString(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
switch v.(type) {
|
||||
case string:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ReverseStr return string whose char order is reversed to the given string
|
||||
func ReverseStr(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
200
strutil/string_test.go
Normal file
200
strutil/string_test.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestCamelCase(t *testing.T) {
|
||||
camelCase(t, "foo_bar", "fooBar")
|
||||
camelCase(t, "Foo-Bar", "fooBar")
|
||||
camelCase(t, "Foo&bar", "fooBar")
|
||||
camelCase(t, "foo bar", "fooBar")
|
||||
}
|
||||
|
||||
func camelCase(t *testing.T, test string, expected string) {
|
||||
res := CamelCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "CamelCase", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapitalize(t *testing.T) {
|
||||
capitalize(t, "foo", "Foo")
|
||||
capitalize(t, "fOO", "Foo")
|
||||
capitalize(t, "FOo", "Foo")
|
||||
}
|
||||
|
||||
func capitalize(t *testing.T, test string, expected string) {
|
||||
res := Capitalize(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Capitalize", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestKebabCase(t *testing.T) {
|
||||
kebabCase(t, "Foo Bar-", "foo-bar")
|
||||
kebabCase(t, "foo_Bar", "foo-bar")
|
||||
kebabCase(t, "fooBar", "foo-bar")
|
||||
kebabCase(t, "__FOO_BAR__", "f-o-o-b-a-r")
|
||||
}
|
||||
|
||||
func kebabCase(t *testing.T, test string, expected string) {
|
||||
res := KebabCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "KebabCase", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnakeCase(t *testing.T) {
|
||||
snakeCase(t, "Foo Bar-", "foo_bar")
|
||||
snakeCase(t, "foo_Bar", "foo_bar")
|
||||
snakeCase(t, "fooBar", "foo_bar")
|
||||
snakeCase(t, "__FOO_BAR__", "f_o_o_b_a_r")
|
||||
snakeCase(t, "aBbc-s$@a&%_B.B^C", "a_bbc_s_a_b_b_c")
|
||||
}
|
||||
|
||||
func snakeCase(t *testing.T, test string, expected string) {
|
||||
res := SnakeCase(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "SnakeCase", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerFirst(t *testing.T) {
|
||||
lowerFirst(t, "foo", "foo")
|
||||
lowerFirst(t, "BAR", "bAR")
|
||||
lowerFirst(t, "FOo", "fOo")
|
||||
}
|
||||
|
||||
func lowerFirst(t *testing.T, test string, expected string) {
|
||||
res := LowerFirst(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "LowerFirst", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadEnd(t *testing.T) {
|
||||
padEnd(t, "a", 1, "b", "a")
|
||||
padEnd(t, "a", 2, "b", "ab")
|
||||
padEnd(t, "abcd", 6, "mno", "abcdmn")
|
||||
padEnd(t, "abcd", 6, "m", "abcdmm")
|
||||
padEnd(t, "abc", 6, "ab", "abcaba")
|
||||
}
|
||||
|
||||
func padEnd(t *testing.T, source string, size int, fillString string, expected string) {
|
||||
res := PadEnd(source, size, fillString)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadStart(t *testing.T) {
|
||||
padStart(t, "a", 1, "b", "a")
|
||||
padStart(t, "a", 2, "b", "ba")
|
||||
padStart(t, "abcd", 6, "mno", "mnabcd")
|
||||
padStart(t, "abcd", 6, "m", "mmabcd")
|
||||
padStart(t, "abc", 6, "ab", "abaabc")
|
||||
}
|
||||
|
||||
func padStart(t *testing.T, source string, size int, fillString string, expected string) {
|
||||
res := PadStart(source, size, fillString)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
before(t, "lancet", "", "lancet")
|
||||
before(t, "github.com/test/lancet", "/", "github.com")
|
||||
before(t, "github.com/test/lancet", "test", "github.com/")
|
||||
}
|
||||
|
||||
func before(t *testing.T, source, char, expected string) {
|
||||
res := Before(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Before", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeforeLast(t *testing.T) {
|
||||
beforeLast(t, "lancet", "", "lancet")
|
||||
beforeLast(t, "github.com/test/lancet", "/", "github.com/test")
|
||||
beforeLast(t, "github.com/test/test/lancet", "test", "github.com/test/")
|
||||
}
|
||||
|
||||
func beforeLast(t *testing.T, source, char, expected string) {
|
||||
res := BeforeLast(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "BeforeLast", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
after(t, "lancet", "", "lancet")
|
||||
after(t, "github.com/test/lancet", "/", "test/lancet")
|
||||
after(t, "github.com/test/lancet", "test", "/lancet")
|
||||
}
|
||||
|
||||
func after(t *testing.T, source, char, expected string) {
|
||||
res := After(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "After", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterLast(t *testing.T) {
|
||||
afterLast(t, "lancet", "", "lancet")
|
||||
afterLast(t, "github.com/test/lancet", "/", "lancet")
|
||||
afterLast(t, "github.com/test/test/lancet", "test", "/lancet")
|
||||
}
|
||||
|
||||
func afterLast(t *testing.T, source, char, expected string) {
|
||||
res := AfterLast(source, char)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "AfterLast", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsString(t *testing.T) {
|
||||
isString(t, "lancet", true)
|
||||
isString(t, 1, false)
|
||||
isString(t, true, false)
|
||||
isString(t, []string{}, false)
|
||||
}
|
||||
|
||||
func isString(t *testing.T, test interface{}, expected bool) {
|
||||
res := IsString(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsString", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseStr(t *testing.T) {
|
||||
reverseStr(t, "abc", "cba")
|
||||
reverseStr(t, "12345", "54321")
|
||||
|
||||
//failed
|
||||
//reverseStr(t, "abc", "abc")
|
||||
}
|
||||
|
||||
func reverseStr(t *testing.T, test string, expected string) {
|
||||
res := ReverseStr(test)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "ReverseStr", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
40
strutil/string_util.go
Normal file
40
strutil/string_util.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package strutil
|
||||
|
||||
import "strings"
|
||||
|
||||
// splitWordsToLower split a string into worlds by uppercase char
|
||||
func splitWordsToLower(s string) []string {
|
||||
var res []string
|
||||
|
||||
upperIndexes := upperIndex(s)
|
||||
l := len(upperIndexes)
|
||||
if upperIndexes == nil || l == 0 {
|
||||
if s != "" {
|
||||
res = append(res, s)
|
||||
}
|
||||
return res
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
if i < l-1 {
|
||||
res = append(res, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]]))
|
||||
} else {
|
||||
res = append(res, strings.ToLower(s[upperIndexes[i]:]))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// upperIndex get a int slice which elements are all the uppercase char index of a string
|
||||
func upperIndex(s string) []int {
|
||||
var res []int
|
||||
for i := 0; i < len(s); i++ {
|
||||
if 64 < s[i] && s[i] < 91 {
|
||||
res = append(res, i)
|
||||
}
|
||||
}
|
||||
if len(s) > 0 && res != nil && res[0] != 0 {
|
||||
res = append([]int{0}, res...)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
15
utils/utils.go
Normal file
15
utils/utils.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package utils implements is for internal use.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func LogFailedTestInfo(t *testing.T, testCase, input, expected, result interface{}) {
|
||||
errInfo := fmt.Sprintf("Test case %v: input is %+v, expected %v, but result is %v", testCase, input, expected, result)
|
||||
t.Error(errInfo)
|
||||
}
|
||||
194
validator/validator.go
Normal file
194
validator/validator.go
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package validator implements some validate function for string.
|
||||
package validator
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// IsAlpha checks if the string contains only letters (a-zA-Z)
|
||||
func IsAlpha(s string) bool {
|
||||
pattern := `^[a-zA-Z]+$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(s)
|
||||
}
|
||||
|
||||
// IsNumberStr check if the string can convert to a number.
|
||||
func IsNumberStr(s string) bool {
|
||||
return IsIntStr(s) || IsFloatStr(s)
|
||||
}
|
||||
|
||||
// IsFloatStr check if the string can convert to a float.
|
||||
func IsFloatStr(s string) bool {
|
||||
_, e := strconv.ParseFloat(s, 64)
|
||||
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsIntStr check if the string can convert to a integer.
|
||||
func IsIntStr(s string) bool {
|
||||
match, _ := regexp.MatchString(`^[\+-]?\d+$`, s)
|
||||
return match
|
||||
}
|
||||
|
||||
// IsIp check if the string is a ip address.
|
||||
func IsIp(ipstr string) bool {
|
||||
ip := net.ParseIP(ipstr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsIpV4 check if the string is a ipv4 address.
|
||||
func IsIpV4(ipstr string) bool {
|
||||
ip := net.ParseIP(ipstr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(ipstr); i++ {
|
||||
switch ipstr[i] {
|
||||
case '.':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIpV6 check if the string is a ipv6 address.
|
||||
func IsIpV6(ipstr string) bool {
|
||||
ip := net.ParseIP(ipstr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(ipstr); i++ {
|
||||
switch ipstr[i] {
|
||||
case ':':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDns check if the string is dns.
|
||||
func IsDns(dns string) bool {
|
||||
pattern := `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(dns)
|
||||
}
|
||||
|
||||
// IsEmail check if the string is a email address.
|
||||
func IsEmail(email string) bool {
|
||||
pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(email)
|
||||
}
|
||||
|
||||
// IsChineseMobile check if the string is chinese mobile number.
|
||||
func IsChineseMobile(mobileNum string) bool {
|
||||
pattern := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(mobileNum)
|
||||
}
|
||||
|
||||
// IsChineseIdNum check if the string is chinese id number.
|
||||
func IsChineseIdNum(id string) bool {
|
||||
pattern := `^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(id)
|
||||
}
|
||||
|
||||
// ContainChinese check if the string contain mandarin chinese.
|
||||
func ContainChinese(s string) bool {
|
||||
pattern := "[\u4e00-\u9fa5]"
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(s)
|
||||
}
|
||||
|
||||
// IsChinesePhone check if the string is chinese phone number.
|
||||
// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx
|
||||
func IsChinesePhone(phone string) bool {
|
||||
pattern := `\d{3}-\d{8}|\d{4}-\d{7}`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(phone)
|
||||
}
|
||||
|
||||
// IsCreditCard check if the string is credit card.
|
||||
func IsCreditCard(creditCart string) bool {
|
||||
pattern := `^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(creditCart)
|
||||
}
|
||||
|
||||
// IsBase64 check if the string is base64 string.
|
||||
func IsBase64(base64 string) bool {
|
||||
pattern := `^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(base64)
|
||||
}
|
||||
|
||||
// IsEmptyString check if the string is empty.
|
||||
func IsEmptyString(s string) bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
// IsRegexMatch check if the string match the regexp
|
||||
func IsRegexMatch(s, regex string) bool {
|
||||
reg := regexp.MustCompile(regex)
|
||||
return reg.MatchString(s)
|
||||
}
|
||||
|
||||
// IsStrongPassword check if the string is strong password, if len(password) is less than the length param, return false
|
||||
// Strong password: alpha(lower+upper) + number + special chars(!@#$%^&*()?><)
|
||||
func IsStrongPassword(password string, length int) bool {
|
||||
if len(password) < length {
|
||||
return false
|
||||
}
|
||||
var num, lower, upper, special bool
|
||||
for _, r := range password {
|
||||
switch {
|
||||
case unicode.IsDigit(r):
|
||||
num = true
|
||||
case unicode.IsUpper(r):
|
||||
upper = true
|
||||
case unicode.IsLower(r):
|
||||
lower = true
|
||||
case unicode.IsSymbol(r), unicode.IsPunct(r):
|
||||
special = true
|
||||
}
|
||||
}
|
||||
|
||||
return num && lower && upper && special
|
||||
|
||||
// go doesn't support regexp (?=re)
|
||||
//pattern := `^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&+=])(?=\S+$).$`
|
||||
//reg := regexp.MustCompile(pattern)
|
||||
//return reg.MatchString(password)
|
||||
}
|
||||
|
||||
// IsWeakPassword check if the string is weak password
|
||||
// Weak password: only letter or only number or letter + number
|
||||
func IsWeakPassword(password string) bool {
|
||||
var num, letter, special bool
|
||||
for _, r := range password {
|
||||
switch {
|
||||
case unicode.IsDigit(r):
|
||||
num = true
|
||||
case unicode.IsLetter(r):
|
||||
letter = true
|
||||
case unicode.IsSymbol(r), unicode.IsPunct(r):
|
||||
special = true
|
||||
}
|
||||
}
|
||||
|
||||
return (num || letter) && !special
|
||||
}
|
||||
|
||||
283
validator/validator_test.go
Normal file
283
validator/validator_test.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestIsNumberStr(t *testing.T) {
|
||||
isNumberStr(t, "3.", true)
|
||||
isNumberStr(t, "+3.", true)
|
||||
isNumberStr(t, "-3.", true)
|
||||
isNumberStr(t, "+3e2", true)
|
||||
isNumberStr(t, "abc", false)
|
||||
}
|
||||
|
||||
func isNumberStr(t *testing.T, source string, expected bool) {
|
||||
res := IsNumberStr(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsNumberStr", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFloatStr(t *testing.T) {
|
||||
isFloatStr(t, "3.", true)
|
||||
isFloatStr(t, "+3.", true)
|
||||
isFloatStr(t, "-3.", true)
|
||||
isFloatStr(t, "12", true)
|
||||
isFloatStr(t, "abc", false)
|
||||
}
|
||||
|
||||
func isFloatStr(t *testing.T, source string, expected bool) {
|
||||
res := IsFloatStr(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsFloatStr", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIntStr(t *testing.T) {
|
||||
isIntStr(t, "+3", true)
|
||||
isIntStr(t, "-3", true)
|
||||
isIntStr(t, "3.", false)
|
||||
isIntStr(t, "abc", false)
|
||||
}
|
||||
|
||||
func isIntStr(t *testing.T, source string, expected bool) {
|
||||
res := IsIntStr(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsIntStr", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIp(t *testing.T) {
|
||||
isIp(t, "127.0.0.1", true)
|
||||
isIp(t, "::0:0:0:0:0:0:1", true)
|
||||
isIp(t, "120.0.0", false)
|
||||
isIp(t, "abc", false)
|
||||
}
|
||||
|
||||
func isIp(t *testing.T, source string, expected bool) {
|
||||
res := IsIp(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsIp", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIpV4(t *testing.T) {
|
||||
isIpV4(t, "127.0.0.1", true)
|
||||
isIpV4(t, "::0:0:0:0:0:0:1", false)
|
||||
}
|
||||
|
||||
func isIpV4(t *testing.T, source string, expected bool) {
|
||||
res := IsIpV4(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsIpV4", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIpV6(t *testing.T) {
|
||||
isIpV6(t, "127.0.0.1", false)
|
||||
isIpV6(t, "::0:0:0:0:0:0:1", true)
|
||||
}
|
||||
|
||||
func isIpV6(t *testing.T, source string, expected bool) {
|
||||
res := IsIpV6(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsIpV6", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDns(t *testing.T) {
|
||||
isDns(t, "abc.com", true)
|
||||
isDns(t, "a.b.com", false)
|
||||
}
|
||||
|
||||
func isDns(t *testing.T, source string, expected bool) {
|
||||
res := IsDns(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsDns", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmail(t *testing.T) {
|
||||
isEmail(t, "abc@xyz.com", true)
|
||||
isEmail(t, "a.b@@com", false)
|
||||
}
|
||||
|
||||
func isEmail(t *testing.T, source string, expected bool) {
|
||||
res := IsEmail(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsEmail", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainChinese(t *testing.T) {
|
||||
containChinese(t, "你好", true)
|
||||
containChinese(t, "hello", false)
|
||||
containChinese(t, "hello你好", true)
|
||||
}
|
||||
|
||||
func containChinese(t *testing.T, source string, expected bool) {
|
||||
res := ContainChinese(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsContainChineseChar", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsChineseMobile(t *testing.T) {
|
||||
isChineseMobile(t, "13263527980", true)
|
||||
isChineseMobile(t, "434324324", false)
|
||||
}
|
||||
|
||||
func isChineseMobile(t *testing.T, source string, expected bool) {
|
||||
res := IsChineseMobile(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsChineseMobile", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsChinesePhone(t *testing.T) {
|
||||
isChinesePhone(t, "010-32116675", true)
|
||||
isChinesePhone(t, "0464-8756213", true)
|
||||
isChinesePhone(t, "123-87562", false)
|
||||
}
|
||||
|
||||
func isChinesePhone(t *testing.T, source string, expected bool) {
|
||||
res := IsChinesePhone(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsChinesePhone", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsChineseIdNum(t *testing.T) {
|
||||
isChineseIdNum(t, "210911192105130715", true)
|
||||
isChineseIdNum(t, "21091119210513071X", true)
|
||||
isChineseIdNum(t, "21091119210513071x", true)
|
||||
isChineseIdNum(t, "123456", false)
|
||||
}
|
||||
|
||||
func isChineseIdNum(t *testing.T, source string, expected bool) {
|
||||
res := IsChineseIdNum(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsChineseIdNum", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCreditCard(t *testing.T) {
|
||||
isCreditCard(t, "4111111111111111", true)
|
||||
isCreditCard(t, "123456", false)
|
||||
}
|
||||
|
||||
func isCreditCard(t *testing.T, source string, expected bool) {
|
||||
res := IsCreditCard(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsCreditCard", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBase64(t *testing.T) {
|
||||
isBase64(t, "aGVsbG8", true)
|
||||
isBase64(t, "123456", false)
|
||||
}
|
||||
|
||||
func isBase64(t *testing.T, source string, expected bool) {
|
||||
res := IsBase64(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsBase64", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmptyString(t *testing.T) {
|
||||
isEmptyString(t, "111", false)
|
||||
isEmptyString(t, " ", false)
|
||||
isEmptyString(t, "\t", false)
|
||||
isEmptyString(t, "", true)
|
||||
}
|
||||
|
||||
func isEmptyString(t *testing.T, source string, expected bool) {
|
||||
res := IsEmptyString(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsEmptyString", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAlpha(t *testing.T) {
|
||||
isAlpha(t, "abc", true)
|
||||
isAlpha(t, "111", false)
|
||||
isAlpha(t, " ", false)
|
||||
isAlpha(t, "\t", false)
|
||||
isAlpha(t, "", false)
|
||||
}
|
||||
|
||||
func isAlpha(t *testing.T, source string, expected bool) {
|
||||
res := IsAlpha(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsAlpha", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsRegexMatch(t *testing.T) {
|
||||
isRegexMatch(t, "abc", `^[a-zA-Z]+$`, true)
|
||||
isRegexMatch(t, "1ab", `^[a-zA-Z]+$`, false)
|
||||
isRegexMatch(t, "111", `^[a-zA-Z]+$`, false)
|
||||
isRegexMatch(t, "", `^[a-zA-Z]+$`, false)
|
||||
}
|
||||
|
||||
func isRegexMatch(t *testing.T, source, regex string, expected bool) {
|
||||
res := IsRegexMatch(source, regex)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsRegexMatch", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsStrongPassword(t *testing.T) {
|
||||
isStrongPassword(t, "abc", 3, false)
|
||||
isStrongPassword(t, "abc123", 6, false)
|
||||
isStrongPassword(t, "abcABC", 6, false)
|
||||
isStrongPassword(t, "abc123@#$", 9, false)
|
||||
isStrongPassword(t, "abcABC123@#$", 16, false)
|
||||
isStrongPassword(t, "abcABC123@#$", 12, true)
|
||||
isStrongPassword(t, "abcABC123@#$", 10, true)
|
||||
}
|
||||
|
||||
func isStrongPassword(t *testing.T, source string, length int, expected bool) {
|
||||
res := IsStrongPassword(source, length)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsStrongPassword", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsWeakPassword(t *testing.T) {
|
||||
isWeakPassword(t, "abc", true)
|
||||
isWeakPassword(t, "123", true)
|
||||
isWeakPassword(t, "abc123", true)
|
||||
isWeakPassword(t, "abcABC123", true)
|
||||
isWeakPassword(t, "abc123@#$", false)
|
||||
}
|
||||
|
||||
func isWeakPassword(t *testing.T, source string, expected bool) {
|
||||
res := IsWeakPassword(source)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "IsWeakPassword", source, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user