1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-08 14:42:27 +08:00

Compare commits

..

28 Commits

Author SHA1 Message Date
dudaodong
8bb102cb6e release v1.2.2 2022-01-24 10:10:38 +08:00
dudaodong
e07d54d1da fix: fix exec windows command test failed 2022-01-23 14:40:59 +08:00
dudaodong
6f035f710e feat: add DifferenceBy func 2022-01-23 14:27:37 +08:00
dudaodong
92967e0add fix: TestCompact for blank string case 2022-01-22 21:21:12 +08:00
dudaodong
6a1a0b8677 feat: add Concat func 2022-01-22 21:16:34 +08:00
dudaodong
ca88687f3d feat: add UpperFirst func 2022-01-22 18:55:41 +08:00
dudaodong
aa64bf5bee feat: add IsUrl func 2022-01-22 18:11:52 +08:00
dudaodong
a3399503f7 feat: add Debounced func 2022-01-21 17:13:31 +08:00
dudaodong
3ca096b4ac update MiMeType func comment 2022-01-21 14:54:55 +08:00
dudaodong
28317a1683 feat: remove ConvertSlice func 2022-01-19 20:41:46 +08:00
dudaodong
2ab898741d test: add TestOsDetection 2022-01-19 20:18:04 +08:00
dudaodong
454efd486d test: add test logic for assert 2022-01-19 19:45:18 +08:00
dudaodong
efa20a97c4 release v1.2.1 2022-01-19 14:58:52 +08:00
dudaodong
25ef78bc64 refactor: Md5File for reading large file 2022-01-19 14:34:11 +08:00
dudaodong
261370e30d fix: os.go/ExecCommand make error the last return value 2022-01-17 16:57:38 +08:00
dudaodong
764a6fe107 release v1.2.0 2022-01-17 14:49:20 +08:00
dudaodong
f3749c52b9 feat: add system package 2022-01-17 11:54:03 +08:00
dudaodong
f368854b2d update text style of readme file 2022-01-17 10:40:41 +08:00
dudaodong
c424b88d40 update style of readme file 2022-01-16 22:17:51 +08:00
Dan Anstis
aeebd63eda docs(readme): fix convertor import example (#23) 2022-01-14 12:33:53 +08:00
dudaodong
22b3c4dd42 feat: add validator functions, IsAllUpper, IsAllLower, ContainUpper, ContainLower, ContainLetter, IsJSON and IsPort 2022-01-13 20:19:41 +08:00
dudaodong
bd976642f6 feat: add try package for executing a function repeatedly 2022-01-13 16:18:49 +08:00
dudaodong
e31fb28003 feat: add func ContainSubSlice 2022-01-13 11:00:27 +08:00
dudaodong
fd271fe176 add test passing badge 2022-01-12 11:27:09 +08:00
dudaodong
6890bbfe05 update: rename workflow 2022-01-12 11:23:51 +08:00
dudaodong
24ae47a12f remove v2 branch 2022-01-12 10:13:13 +08:00
dudaodong
d8d85efedf update: add v2 branch 2022-01-12 10:05:21 +08:00
dudaodong
ba73847b80 fix: fix some go report issue 2022-01-12 09:57:10 +08:00
20 changed files with 1016 additions and 161 deletions

View File

@@ -1,11 +1,13 @@
name: Test and coverage name: test
on: on:
push: push:
branches: branches:
- main - main
# - v2
pull_request: pull_request:
branches: branches:
- main - main
# - v2
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest

148
README.md
View File

@@ -1,44 +1,42 @@
<div align="center"> # Lancet
<h1 style="width: 100%; text-align: center;">Lancet</h1> <p style="font-size: 18px">
<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.
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js. </p>
</p>
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.9-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-1.2.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE)
</div>
English | [简体中文](./README_zh-CN.md) English | [简体中文](./README_zh-CN.md)
</div>
### Feature ## Feature
- 👏 Comprehensive, efficient and reusable. - 👏 Comprehensive, efficient and reusable.
- 💪 140+ common go util functions, support string, slice, datetime, net, crypt... - 💪 160+ common go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depend on the go standard library. - 💅 Only depend on the go standard library.
- 🌍 Unit test for every exported function. - 🌍 Unit test for every exported function.
### Installation ## Installation
```go ```go
go get github.com/duke-git/lancet go get github.com/duke-git/lancet
``` ```
### Usage ## Usage
Lancet organizes the code into package structure, and you need to import the corresponding package name when use it. For example, if you use string-related functions,import the strutil package like below: 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 ```go
import "github.com/duke-git/lancet/strutil" import "github.com/duke-git/lancet/strutil"
``` ```
### Example ## Example
Here takes the string function ReverseStr (reverse order string) as an example, and the strutil package needs to be imported. Here takes the string function ReverseStr (reverse order string) as an example, and the strutil package needs to be imported.
@@ -57,12 +55,11 @@ func main() {
} }
``` ```
### API Documentation ## API Documentation
### 1. convertor contains some functions for data convertion
#### 1. convertor contains some functions for data convertion
- Support conversion between commonly used data types. - Support conversion between commonly used data types.
- Usage: import "github.com/duke-git/lancet/cryptor" - Usage: import "github.com/duke-git/lancet/convertor"
```go ```go
package main package main
@@ -97,7 +94,7 @@ 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 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 ### 2. cryptor is for data encryption and decryption
- Support md5, hmac, aes, des, ras. - Support md5, hmac, aes, des, ras.
- Usage: import "github.com/duke-git/lancet/cryptor" - Usage: import "github.com/duke-git/lancet/cryptor"
@@ -156,7 +153,7 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA decrypt
``` ```
#### 3. datetime parse and format datetime ### 3. datetime parse and format datetime
- Parse and format datetime - Parse and format datetime
- Usage: import "github.com/duke-git/lancet/datetime" - Usage: import "github.com/duke-git/lancet/datetime"
@@ -191,7 +188,7 @@ func FormatTimeToStr(t time.Time, format string) string //convert time to string
func FormatStrToTime(str, format string) time.Time //convert string to time func FormatStrToTime(str, format string) time.Time //convert string to time
``` ```
#### 4. fileutil basic functions for file operations ### 4. fileutil basic functions for file operations
- Basic functions for file operations. - Basic functions for file operations.
- Usage: import "github.com/duke-git/lancet/fileutil" - Usage: import "github.com/duke-git/lancet/fileutil"
@@ -228,7 +225,7 @@ func Zip(fpath string, destPath string) error //create zip file, fpath could be
func UnZip(zipFile string, destPath string) error //unzip the file and save it to destPath func UnZip(zipFile string, destPath string) error //unzip the file and save it to destPath
``` ```
#### 5. formatter is for data format ### 5. formatter is for data format
- Contain some formatting function - Contain some formatting function
- Usage: import "github.com/duke-git/lancet/formatter" - Usage: import "github.com/duke-git/lancet/formatter"
@@ -253,7 +250,7 @@ func main() {
func Comma(v interface{}, symbol string) string //add comma to number by every 3 numbers from right. ahead by symbol char func Comma(v interface{}, symbol string) string //add comma to number by every 3 numbers from right. ahead by symbol char
``` ```
#### 6. function can control the function execution and support functional programming ### 6. function can control the function execution and support functional programming
- Control function execution and support functional programming. - Control function execution and support functional programming.
- Usage: import "github.com/duke-git/lancet/function" - Usage: import "github.com/duke-git/lancet/function"
@@ -281,6 +278,7 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value //c
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called less than n times func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //creates a function that invokes func once it's called less than n times
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //make a curryed function func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //make a curryed function
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //compose the functions from right to left func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //compose the functions from right to left
func Debounced(fn func(), duration time.Duration) func() //creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //invoke function after delayed time func Delay(delay time.Duration, fn interface{}, args ...interface{}) //invoke function after delayed time
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //invoke function every duration time, util close the returned bool chan func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //invoke function every duration time, util close the returned bool chan
func (w *Watcher) Start() //start the watch timer. func (w *Watcher) Start() //start the watch timer.
@@ -289,7 +287,7 @@ func (w *Watcher) Reset() {} //reset the watch timer.
func (w *Watcher) GetElapsedTime() time.Duration //return time duration from watcher start to end. func (w *Watcher) GetElapsedTime() time.Duration //return time duration from watcher start to end.
``` ```
#### 7. netutil is for net process ### 7. netutil is for net process
- Ip and http request method. - Ip and http request method.
- Usage: import "github.com/duke-git/lancet/netutil". - Usage: import "github.com/duke-git/lancet/netutil".
@@ -337,7 +335,7 @@ func ConvertMapToQueryString(param map[string]interface{}) string //convert map
func ParseHttpResponse(resp *http.Response, obj interface{}) error //decode http response to specified interface func ParseHttpResponse(resp *http.Response, obj interface{}) error //decode http response to specified interface
``` ```
#### 8. random is for rand string and int generation ### 8. random is for rand string and int generation
- Generate random string and int. - Generate random string and int.
- Usage: import "github.com/duke-git/lancet/random". - Usage: import "github.com/duke-git/lancet/random".
@@ -366,7 +364,49 @@ func RandInt(min, max int) int //generate random int
func RandString(length int) string //generate random string func RandString(length int) string //generate random string
``` ```
#### 9. slice is for process slice ### 9. retry is for executing a function repeatedly until it was successful or canceled by the context.
- Executes a function repeatedly until it was successful or canceled by the context.
- Default retry times is 5, default retry duration is 3 second.
- Usage: import "github.com/duke-git/lancet/retry".
```go
package main
import (
"fmt"
"io/ioutil"
"log"
"github.com/duke-git/lancet/retry"
)
func main() {
var number int
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50))
fmt.Println(number) //3
}
```
- Function list:
```go
type RetryFunc func() error //function that retry executes
func RetryTimes(n uint) //set times of retry
func RetryDuration(d time.Duration) //generate random string
func Context(ctx context.Context) //set retry context config
func Retry(retryFunc RetryFunc, opts ...Option) error //executes the retryFunc repeatedly until it was successful or canceled by the context
```
### 10. slice is for process slice
- Contain function for process slice. - Contain function for process slice.
- Usage: import "github.com/duke-git/lancet/slice" - Usage: import "github.com/duke-git/lancet/slice"
@@ -393,15 +433,18 @@ func main() {
```go ```go
func Contain(slice interface{}, value interface{}) bool //check if the value is in the slice or not 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 ContainSubSlice(slice interface{}, subslice interface{}) bool //check if the slice contain subslice or not
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //convert originalSlice to newSliceType func Chunk(slice []interface{}, size int) [][]interface{} //creates an slice of elements split into groups the length of `size`
func Compact(slice interface{}) interface{} //creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
func Concat(slice interface{}, values ...interface{}) interface{} //creates a new slice concatenating slice with any additional slices and/or values
func Difference(slice1, slice2 interface{}) interface{} //creates an slice of whose element not included in the other given slice func Difference(slice1, slice2 interface{}) interface{} //creates an slice of whose element not included in the other given slice
func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} //it accepts iteratee which is invoked for each element of slice and values to generate the criterion by which they're compared.
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1 func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1
func Drop(slice interface{}, n int) interface{} //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0 func Drop(slice interface{}, n int) interface{} //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0
func Every(slice, function interface{}) bool //return true if all of the values in the slice pass the predicate function, function signature should be func(index int, value interface{}) bool func Every(slice, function interface{}) bool //return true if all of the values in the slice pass the predicate function, function signature should be func(index int, value interface{}) bool
func None(slice, function interface{}) bool // return true if all the values in the slice mismatch the criteria func None(slice, function interface{}) bool // return true if all the values in the slice mismatch the criteria
func Filter(slice, function interface{}) interface{} //filter slice, function signature should be func(index int, value interface{}) bool func Filter(slice, function interface{}) interface{} //filter slice, function signature should be func(index int, value interface{}) bool
func Find(slice, function interface{}) (interface{}, bool) //iterates over elements of slice, returning the first one that passes a truth test on function.function signature should be func(index int, value interface{}) bool . func Find(slice, function interface{}) (interface{}, bool) //iterates over elements of slice, returning the first one that passes a truth test on function.function signature should be func(index int, value interface{}) bool
func FlattenDeep(slice interface{}) interface{} //flattens slice recursive func FlattenDeep(slice interface{}) interface{} //flattens slice recursive
func ForEach(slice, function interface{}) //iterates over elements of slice and invokes function for each element, function signature should be func(index int, value interface{}) func ForEach(slice, function interface{}) //iterates over elements of slice and invokes function for each element, function signature should be func(index int, value interface{})
func IntSlice(slice interface{}) ([]int, error) //convert value to int slice func IntSlice(slice interface{}) ([]int, error) //convert value to int slice
@@ -416,14 +459,14 @@ func SortByField(slice interface{}, field string, sortType ...string) error //so
func Some(slice, function interface{}) bool //return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool func Some(slice, function interface{}) bool //return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool
func StringSlice(slice interface{}) []string //convert value to string slice func StringSlice(slice interface{}) []string //convert value to string slice
func Unique(slice interface{}) interface{} //remove duplicate elements in slice func Unique(slice interface{}) interface{} //remove duplicate elements in slice
func Union(slices ...interface{}) interface{} //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons. func Union(slices ...interface{}) interface{} //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index. func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index.
func Without(slice interface{}, values ...interface{}) interface{} //creates a slice excluding all given values func Without(slice interface{}, values ...interface{}) interface{} //creates a slice excluding all given values
func GroupBy(slice, function interface{}) (interface{}, interface{}) // groups slice into two categories func GroupBy(slice, function interface{}) (interface{}, interface{}) // groups slice into two categories
func Count(slice, function interface{}) int // Count iterates over elements of slice, returns a count of all matched elements func Count(slice, function interface{}) int // Count iterates over elements of slice, returns a count of all matched elements
``` ```
#### 10. strutil is for processing string ### 11. strutil is for processing string
- Contain functions to precess string - Contain functions to precess string
- Usage: import "github.com/duke-git/lancet/strutil" - Usage: import "github.com/duke-git/lancet/strutil"
@@ -457,6 +500,7 @@ func Capitalize(s string) string //convert the first character of a string to up
func IsString(v interface{}) bool //check if the value data type is string or not 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 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 LowerFirst(s string) string //convert the first character of string to lower case
func UpperFirst(s string) string //converts the first character of string to upper case
func PadEnd(source string, size int, padStr string) string //pads string on the right side if it's shorter than size 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 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 ReverseStr(s string) string //return string whose char order is reversed to the given string
@@ -464,8 +508,41 @@ func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_b
func Wrap(str string, wrapWith string) string //wrap a string with another string. func Wrap(str string, wrapWith string) string //wrap a string with another string.
func Unwrap(str string, wrapToken string) string //unwrap a given string from anther string. will change str value func Unwrap(str string, wrapToken string) string //unwrap a given string from anther string. will change str value
``` ```
### 12. system contain some functions about os, runtime, shell command.
#### 11. validator is for data validation - Generate random string and int.
- Usage: import "github.com/duke-git/lancet/system".
```go
package main
import (
"fmt"
"io/ioutil"
"log"
"github.com/duke-git/lancet/system"
)
func main() {
envFoo := system.GetOsEnv("foo")
fmt.Println(envFoo)
}
```
- Function list:
```go
func IsWindows() bool //check if current os is windows
func IsLinux() bool //check if current os is linux
func IsMac() bool //check if current os is macos
func GetOsEnv(key string) string //gets the value of the environment variable named by the key.
func SetOsEnv(key, value string) error //sets the value of the environment variable named by the key.
func RemoveOsEnv(key string) error //remove a single environment variable.
func CompareOsEnv(key, comparedEnv string) bool //gets env named by the key and compare it with comparedEnv
func ExecCommand(command string) (stdout, stderr string, err error) //use shell /bin/bash -c to execute command
```
### 13. validator is for data validation
- Contain function for data validation. - Contain function for data validation.
- Usage: import "github.com/duke-git/lancet/validator". - Usage: import "github.com/duke-git/lancet/validator".
@@ -493,6 +570,12 @@ func main() {
func ContainChinese(s string) bool //check if the string contain mandarin chinese 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 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 IsBase64(base64 string) bool //check if the string is base64 string
func IsAllUpper(str string) bool //check if the string is all upper case letters A-Z
func IsAllLower(str string) bool //check if the string is all lower case letters a-z
func ContainUpper(str string) bool //check if the string contain at least one upper case letter A-Z
func ContainLower(str string) bool //check if the string contain at least one lower case letter a-z
func ContainLetter(str string) bool //check if the string contain at least one letter
func IsJSON(str string) bool //checks if the string is valid JSON
func IsChineseMobile(mobileNum string) bool //check if the string is chinese mobile number 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 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 IsChinesePhone(phone string) bool //check if the string is chinese phone number
@@ -508,5 +591,6 @@ 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 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 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 IsStrongPassword(password string, length int) bool //check if the string is strong password (alpha(lower+upper) + number + special chars(!@#$%^&*()?><))
func IsUrl(str string) bool //check if the string is url
func IsWeakPassword(password string) bool //check if the string is weak passwordonly letter or only number or letter + number func IsWeakPassword(password string) bool //check if the string is weak passwordonly letter or only number or letter + number
``` ```

View File

@@ -1,37 +1,34 @@
<div align="center"> # Lancet
<h1 style="width: 100%; text-align: center;">Lancet</h1> <p style="font-size: 18px">
<p style="font-size: 18px"> lancet柳叶刀是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
lancet柳叶刀是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。 </p>
</p>
<div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.9-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-1.2.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE)
</div>
简体中文 | [English](./README.md) 简体中文 | [English](./README.md)
</div>
### 特性 ## 特性
- 👏 全面、高效、可复用 - 👏 全面、高效、可复用
- 💪 140+常用go工具函数支持string、slice、datetime、net、crypt... - 💪 160+常用go工具函数支持string、slice、datetime、net、crypt...
- 💅 只依赖go标准库 - 💅 只依赖go标准库
- 🌍 所有导出函数单元测试覆盖率100% - 🌍 所有导出函数单元测试覆盖率100%
### 安装 ## 安装
```go ```go
go get github.com/duke-git/lancet go get github.com/duke-git/lancet
``` ```
### 用法 ## 用法
lancet是以包的结构组织代码的使用时需要导入相应的包名。例如如果使用字符串相关函数需要导入strutil包: lancet是以包的结构组织代码的使用时需要导入相应的包名。例如如果使用字符串相关函数需要导入strutil包:
@@ -39,7 +36,7 @@ lancet是以包的结构组织代码的使用时需要导入相应的包名
import "github.com/duke-git/lancet/strutil" import "github.com/duke-git/lancet/strutil"
``` ```
### 例子 ## 例子
此处以字符串工具函数ReverseStr逆序字符串为例需要导入strutil包: 此处以字符串工具函数ReverseStr逆序字符串为例需要导入strutil包:
@@ -58,9 +55,8 @@ func main() {
} }
``` ```
### API文档 ## API文档
### 1. convertor数据转换包
#### 1. convertor数据转换包
- 转换函数支持常用数据类型之间的转换 - 转换函数支持常用数据类型之间的转换
- 导入包import "github.com/duke-git/lancet/convertor" - 导入包import "github.com/duke-git/lancet/convertor"
@@ -98,7 +94,7 @@ func ToString(value interface{}) string //interface转成string
func StructToMap(value interface{}) (map[string]interface{}, error) //struct串转成map, 需要设置struct tag `json` func StructToMap(value interface{}) (map[string]interface{}, error) //struct串转成map, 需要设置struct tag `json`
``` ```
#### 2. cryptor加解密包 ### 2. cryptor加解密包
- 加密函数支持md5, hmac, aes, des, ras - 加密函数支持md5, hmac, aes, des, ras
- 导入包import "github.com/duke-git/lancet/cryptor" - 导入包import "github.com/duke-git/lancet/cryptor"
@@ -157,7 +153,7 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA解密
``` ```
#### 3. datetime日期时间处理包 ### 3. datetime日期时间处理包
- 处理日期时间 - 处理日期时间
- 导入包import "github.com/duke-git/lancet/datetime" - 导入包import "github.com/duke-git/lancet/datetime"
@@ -192,7 +188,7 @@ func FormatTimeToStr(t time.Time, format string) string //时间格式化字符
func FormatStrToTime(str, format string) time.Time //字符串转换成时间 func FormatStrToTime(str, format string) time.Time //字符串转换成时间
``` ```
#### 4. fileutil文件处理包 ### 4. fileutil文件处理包
- 文件处理常用函数 - 文件处理常用函数
- 导入包import "github.com/duke-git/lancet/fileutil" - 导入包import "github.com/duke-git/lancet/fileutil"
@@ -229,7 +225,7 @@ func Zip(fpath string, destPath string) error //压缩文件fpath参数可以是
func UnZip(zipFile string, destPath string) error //解压文件并将文件存储在destPath目录中 func UnZip(zipFile string, destPath string) error //解压文件并将文件存储在destPath目录中
``` ```
#### 5. formatter格式化处理包 ### 5. formatter格式化处理包
- 格式化相关处理函数 - 格式化相关处理函数
- 导入包import "github.com/duke-git/lancet/formatter" - 导入包import "github.com/duke-git/lancet/formatter"
@@ -254,7 +250,7 @@ func main() {
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串 func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
``` ```
#### 6. function包可以控制函数执行支持部分函数式编程 ### 6. function包可以控制函数执行支持部分函数式编程
- 控制函数执行,支持部分函数式编程 - 控制函数执行,支持部分函数式编程
- 导入包import "github.com/duke-git/lancet/function" - 导入包import "github.com/duke-git/lancet/function"
@@ -283,6 +279,7 @@ func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value //
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //函数柯里化 func (f Fn) Curry(i interface{}) func(...interface{}) interface{} //函数柯里化
func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //从右至左组合函数 func Compose(fnList ...func(...interface{}) interface{}) func(...interface{}) interface{} //从右至左组合函数
func Delay(delay time.Duration, fn interface{}, args ...interface{}) //延迟调用函数 func Delay(delay time.Duration, fn interface{}, args ...interface{}) //延迟调用函数
func Debounced(fn func(), duration time.Duration) func() //go防抖函数在duration时间内连续调用只会执行一次.
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //每隔duration时间调用函数, 关闭返回通道可以停止调用 func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool //每隔duration时间调用函数, 关闭返回通道可以停止调用
func (w *Watcher) Start() //开时watcher func (w *Watcher) Start() //开时watcher
func (w *Watcher) Stop() //开时watcher func (w *Watcher) Stop() //开时watcher
@@ -290,7 +287,7 @@ func (w *Watcher) Reset() {} //重置代码watcher
func (w *Watcher) GetElapsedTime() time.Duration //get code excution elapsed time. func (w *Watcher) GetElapsedTime() time.Duration //get code excution elapsed time.
``` ```
#### 7. netutil网络处理包 ### 7. netutil网络处理包
- 处理ip, http请求相关函数 - 处理ip, http请求相关函数
- 导入包import "github.com/duke-git/lancet/netutil" - 导入包import "github.com/duke-git/lancet/netutil"
@@ -338,7 +335,7 @@ func ConvertMapToQueryString(param map[string]interface{}) string //将map转换
func ParseHttpResponse(resp *http.Response, obj interface{}) error //将http响应解码成特定interface func ParseHttpResponse(resp *http.Response, obj interface{}) error //将http响应解码成特定interface
``` ```
#### 8. random随机数处理包 ### 8. random随机数处理包
- 生成和处理随机数 - 生成和处理随机数
- 导入包import "github.com/duke-git/lancet/random" - 导入包import "github.com/duke-git/lancet/random"
@@ -367,7 +364,49 @@ func RandInt(min, max int) int //生成随机int
func RandString(length int) string //生成随机string func RandString(length int) string //生成随机string
``` ```
#### 9. slice切片操作包 ### 9. retry重试执行函数
- 重试执行函数直到函数成功或被context停止
- 默认重试次数5, 默认执行间隔3秒.
- Usage: import "github.com/duke-git/lancet/retry".
```go
package main
import (
"fmt"
"io/ioutil"
"log"
"github.com/duke-git/lancet/retry"
)
func main() {
var number int
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := retry.Retry(increaseNumber, retry.RetryDuration(time.Microsecond*50))
fmt.Println(number) //3
}
```
- Function list:
```go
type RetryFunc func() error //要重试执行的函数
func RetryTimes(n uint) //设置重试次数默认5次
func RetryDuration(d time.Duration) //设置重试间隔时间默认3秒
func Context(ctx context.Context) //context config
func Retry(retryFunc RetryFunc, opts ...Option) error //重试函数
```
### 10. slice切片操作包
- 切片操作相关函数 - 切片操作相关函数
- 导入包import "github.com/duke-git/lancet/slice" - 导入包import "github.com/duke-git/lancet/slice"
@@ -394,9 +433,12 @@ func main() {
```go ```go
func Contain(slice interface{}, value interface{}) bool //判断slice是否包含value func Contain(slice interface{}, value interface{}) bool //判断slice是否包含value
func ContainSubSlice(slice interface{}, subslice interface{}) bool //判断slice是否包含subslice
func Chunk(slice []interface{}, size int) [][]interface{} //均分slice func Chunk(slice []interface{}, size int) [][]interface{} //均分slice
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //将originalSlice转换为 newSliceType func Compact(slice interface{}) interface{} //去除slice中的false vule. false values are false, nil, 0, and ""
func Concat(slice interface{}, values ...interface{}) interface{} //连接values到slice中
func Difference(slice1, slice2 interface{}) interface{} //返回切片其元素在slice1中不在slice2中 func Difference(slice1, slice2 interface{}) interface{} //返回切片其元素在slice1中不在slice2中
func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} //将slice 和comparedSlice中每个元素调用iterateeFn后作比较如果不相等返回slice中的元素。
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值 func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值
func Drop(slice interface{}, n int) interface{} //创建一个新切片当n大于0时删除原切片前n个元素当n小于0时删除原切片后n个元素 func Drop(slice interface{}, n int) interface{} //创建一个新切片当n大于0时删除原切片前n个元素当n小于0时删除原切片后n个元素
func Every(slice, function interface{}) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool func Every(slice, function interface{}) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool
@@ -424,7 +466,7 @@ func GroupBy(slice, function interface{}) (interface{}, interface{}) //根据函
func Count(slice, function interface{}) int func Count(slice, function interface{}) int
``` ```
#### 10. strutil字符串处理包 ### 11. strutil字符串处理包
- 字符串操作相关函数 - 字符串操作相关函数
- 导入包import "github.com/duke-git/lancet/strutil" - 导入包import "github.com/duke-git/lancet/strutil"
@@ -457,6 +499,7 @@ func CamelCase(s string) string //字符串转为cameCase, "foo bar" -> "fooBar"
func Capitalize(s string) string //字符串转为Capitalize, "fOO" -> "Foo" func Capitalize(s string) string //字符串转为Capitalize, "fOO" -> "Foo"
func IsString(v interface{}) bool //判断是否是字符串 func IsString(v interface{}) bool //判断是否是字符串
func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-bar" func KebabCase(s string) string //字符串转为KebabCase, "foo_Bar" -> "foo-bar"
func UpperFirst(s string) string //字符串的第一个字母转为大写字母
func LowerFirst(s string) string //字符串的第一个字母转为小写字母 func LowerFirst(s string) string //字符串的第一个字母转为小写字母
func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符 func PadEnd(source string, size int, padStr string) string //字符串末尾填充size个字符
func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符 func PadStart(source string, size int, padStr string) string//字符串开头填充size个字符
@@ -466,7 +509,41 @@ func Unwrap(str string, wrapToken string) string //解包裹字符串 Wrap("*abc
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar" func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
``` ```
#### 11. validator验证器 ### 12. system系统
- 包含一些操作系统运行时shell命令执行的函数.
- Usage: import "github.com/duke-git/lancet/system".
```go
package main
import (
"fmt"
"io/ioutil"
"log"
"github.com/duke-git/lancet/system"
)
func main() {
envFoo := system.GetOsEnv("foo")
fmt.Println(envFoo)
}
```
- Function list:
```go
func IsWindows() bool //判断操作系统是windows
func IsLinux() bool //判断操作系统是linux
func IsMac() bool //判断操作系统是macos
func GetOsEnv(key string) string //获取名称为key的环境变量
func SetOsEnv(key, value string) error //设置环境变量
func RemoveOsEnv(key string) error //删除指定key的环境变量
func CompareOsEnv(key, comparedEnv string) bool //获取名称为key的环境变量并和comparedEnv比较
func ExecCommand(command string) (stdout, stderr string, err error) //执行shell命令/bin/bash)
```
### 13. validator验证器包
- 数据校验相关函数 - 数据校验相关函数
- 导入包import "github.com/duke-git/lancet/validator" - 导入包import "github.com/duke-git/lancet/validator"
@@ -494,6 +571,12 @@ func main() {
func ContainChinese(s string) bool //判断字符串中是否含有中文字符 func ContainChinese(s string) bool //判断字符串中是否含有中文字符
func IsAlpha(s string) bool //判断字符串是否只含有字母 func IsAlpha(s string) bool //判断字符串是否只含有字母
func IsBase64(base64 string) bool //判断字符串是base64 func IsBase64(base64 string) bool //判断字符串是base64
func IsAllUpper(str string) bool //断字符串是否全是大写字母
func IsAllLower(str string) bool //断字符串是否全是小写字母
func ContainUpper(str string) bool //判断字符串是否包含大写字母
func ContainLower(str string) bool //判断字符串是否包含小写字母
func ContainLetter(str string) bool //判断字符串是否包含字母
func IsJSON(str string) bool //判断字符串是否是JSON
func IsChineseMobile(mobileNum string) bool //判断字符串是否是手机号 func IsChineseMobile(mobileNum string) bool //判断字符串是否是手机号
func IsChineseIdNum(id string) bool //判断字符串是否是身份证号 func IsChineseIdNum(id string) bool //判断字符串是否是身份证号
func IsChinesePhone(phone string) bool //判断字符串是否是座机电话号码 func IsChinesePhone(phone string) bool //判断字符串是否是座机电话号码
@@ -509,5 +592,6 @@ func IsIp(ipstr string) bool //判断字符串是否是ip
func IsIpV4(ipstr string) bool //判断字符串是否是ipv4 func IsIpV4(ipstr string) bool //判断字符串是否是ipv4
func IsIpV6(ipstr string) bool //判断字符串是否是ipv6 func IsIpV6(ipstr string) bool //判断字符串是否是ipv6
func IsStrongPassword(password string, length int) bool //判断字符串是否是强密码(大小写字母+数字+特殊字符) func IsStrongPassword(password string, length int) bool //判断字符串是否是强密码(大小写字母+数字+特殊字符)
func IsUrl(str string) bool //判断字符串是否是url
func IsWeakPassword(password string) bool //判断字符串是否是弱密码(只有字母或数字) func IsWeakPassword(password string) bool //判断字符串是否是弱密码(只有字母或数字)
``` ```

View File

@@ -6,6 +6,7 @@
package cryptor package cryptor
import ( import (
"bufio"
"crypto/hmac" "crypto/hmac"
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
@@ -13,7 +14,9 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"io/ioutil" "fmt"
"io"
"os"
) )
// Base64StdEncode encode string with base64 encoding // Base64StdEncode encode string with base64 encoding
@@ -36,14 +39,34 @@ func Md5String(s string) string {
// Md5File return the md5 value of file // Md5File return the md5 value of file
func Md5File(filename string) (string, error) { func Md5File(filename string) (string, error) {
f, err := ioutil.ReadFile(filename) if fileInfo, err := os.Stat(filename); err != nil {
return "", err
} else if fileInfo.IsDir() {
return "", nil
}
file, err := os.Open(filename)
if err != nil { if err != nil {
return "", err return "", err
} }
defer file.Close()
h := md5.New() hash := md5.New()
h.Write(f)
return hex.EncodeToString(h.Sum(nil)), nil chunkSize := 65536
for buf, reader := make([]byte, chunkSize), bufio.NewReader(file); ; {
n, err := reader.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return "", err
}
hash.Write(buf[:n])
}
checksum := fmt.Sprintf("%x", hash.Sum(nil))
return checksum, nil
} }
// HmacMd5 return the hmac hash of string use md5 // HmacMd5 return the hmac hash of string use md5

View File

@@ -262,7 +262,7 @@ func FileMode(path string) (fs.FileMode, error) {
} }
// MiMeType return file mime type // MiMeType return file mime type
// file should be string or *os.File // param `file` should be string(file path) or *os.File
func MiMeType(file interface{}) string { func MiMeType(file interface{}) string {
var mediatype string var mediatype string

View File

@@ -70,6 +70,23 @@ func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
invokeFunc(fn, args...) invokeFunc(fn, args...)
} }
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
func Debounced(fn func(), duration time.Duration) func() {
timer := time.NewTimer(duration)
timer.Stop()
go func() {
for {
select {
case <-timer.C:
go fn()
}
}
}()
return func() { timer.Reset(duration) }
}
// Schedule invoke function every duration time, util close the returned bool chan // Schedule invoke function every duration time, util close the returned bool chan
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
// Catch programming error while constructing the closure // Catch programming error while constructing the closure

View File

@@ -93,6 +93,28 @@ func TestDelay(t *testing.T) {
Delay(2*time.Second, print, "test delay") Delay(2*time.Second, print, "test delay")
} }
func TestDebounced(t *testing.T) {
assert := internal.NewAssert(t, "TestDebounced")
count := 0
add := func() {
count++
}
debouncedAdd := Debounced(add, 50*time.Microsecond)
debouncedAdd()
debouncedAdd()
debouncedAdd()
debouncedAdd()
time.Sleep(100 * time.Millisecond)
assert.Equal(1, count)
debouncedAdd()
time.Sleep(100 * time.Millisecond)
assert.Equal(2, count)
}
func TestSchedule(t *testing.T) { func TestSchedule(t *testing.T) {
assert := internal.NewAssert(t, "TestSchedule") assert := internal.NewAssert(t, "TestSchedule")

View File

@@ -24,7 +24,7 @@ func TestWatcher(t *testing.T) {
assert.Equal(false, w.excuting) assert.Equal(false, w.excuting)
w.Reset() w.Reset()
assert.Equal(int64(0), w.startTime) assert.Equal(int64(0), w.startTime)
assert.Equal(int64(0), w.stopTime) assert.Equal(int64(0), w.stopTime)
} }

View File

@@ -18,7 +18,7 @@ const (
compareGreater compareGreater
) )
// Assert is a simple implementation of assertion, only for internal useage // Assert is a simple implementation of assertion, only for internal usage
type Assert struct { type Assert struct {
T *testing.T T *testing.T
CaseName string CaseName string
@@ -154,9 +154,8 @@ func compare(x, y interface{}) int {
default: default:
if reflect.DeepEqual(x, y) { if reflect.DeepEqual(x, y) {
return compareEqual return compareEqual
} else {
return compareNotEqual
} }
return compareNotEqual
} }
return compareNotEqual return compareNotEqual

View File

@@ -8,11 +8,31 @@ func TestAssert(t *testing.T) {
assert := NewAssert(t, "TestAssert") assert := NewAssert(t, "TestAssert")
assert.Equal(0, 0) assert.Equal(0, 0)
assert.NotEqual(1, 0) assert.NotEqual(1, 0)
assert.NotEqual("1", 1)
var uInt1 uint
var uInt2 uint
var uInt8 uint8
var uInt16 uint16
var uInt32 uint32
var uInt64 uint64
assert.NotEqual(uInt1, uInt8)
assert.NotEqual(uInt8, uInt16)
assert.NotEqual(uInt16, uInt32)
assert.NotEqual(uInt32, uInt64)
assert.Equal(uInt1, uInt2)
uInt1 = 1
uInt2 = 2
assert.Less(uInt1, uInt2)
assert.Greater(1, 0) assert.Greater(1, 0)
assert.GreaterOrEqual(1, 1) assert.GreaterOrEqual(1, 1)
assert.Less(0, 1) assert.Less(0, 1)
assert.LessOrEqual(0, 0) assert.LessOrEqual(0, 0)
assert.Equal(0.1, 0.1)
assert.Greater(1.1, 0.1) assert.Greater(1.1, 0.1)
assert.Less(0.1, 1.1) assert.Less(0.1, 1.1)

91
retry/retry.go Normal file
View File

@@ -0,0 +1,91 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package retry is for executing a function repeatedly until it was successful or canceled by the context.
package retry
import (
"context"
"errors"
"fmt"
"reflect"
"runtime"
"strings"
"time"
)
const (
// DefaultRetryTimes times of retry
DefaultRetryTimes = 5
// DefaultRetryDuration time duration of two retries
DefaultRetryDuration = time.Second * 3
)
// RetryConfig is config for retry
type RetryConfig struct {
context context.Context
retryTimes uint
retryDuration time.Duration
}
// RetryFunc is function that retry executes
type RetryFunc func() error
// Option is for adding retry config
type Option func(*RetryConfig)
// RetryTimes set times of retry
func RetryTimes(n uint) Option {
return func(rc *RetryConfig) {
rc.retryTimes = n
}
}
// RetryDuration set duration of retries
func RetryDuration(d time.Duration) Option {
return func(rc *RetryConfig) {
rc.retryDuration = d
}
}
// Context set retry context config
func Context(ctx context.Context) Option {
return func(rc *RetryConfig) {
rc.context = ctx
}
}
// Retry executes the retryFunc repeatedly until it was successful or canceled by the context
// The default times of retries is 5 and the default duration between retries is 3 seconds
func Retry(retryFunc RetryFunc, opts ...Option) error {
config := &RetryConfig{
retryTimes: DefaultRetryTimes,
retryDuration: DefaultRetryDuration,
context: context.TODO(),
}
for _, opt := range opts {
opt(config)
}
var i uint
for i < config.retryTimes {
err := retryFunc()
if err != nil {
select {
case <-time.After(config.retryDuration):
case <-config.context.Done():
return errors.New("retry is cancelled")
}
} else {
return nil
}
i++
}
funcPath := runtime.FuncForPC(reflect.ValueOf(retryFunc).Pointer()).Name()
lastSlash := strings.LastIndex(funcPath, "/")
funcName := funcPath[lastSlash+1:]
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
}

80
retry/retry_test.go Normal file
View File

@@ -0,0 +1,80 @@
package retry
import (
"context"
"errors"
"testing"
"time"
"github.com/duke-git/lancet/internal"
)
func TestRetryFailed(t *testing.T) {
assert := internal.NewAssert(t, "TestRetryFailed")
var number int
increaseNumber := func() error {
number++
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
assert.IsNotNil(err)
assert.Equal(DefaultRetryTimes, number)
}
func TestRetrySucceeded(t *testing.T) {
assert := internal.NewAssert(t, "TestRetrySucceeded")
var number int
increaseNumber := func() error {
number++
if number == DefaultRetryTimes {
return nil
}
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
assert.IsNil(err)
assert.Equal(DefaultRetryTimes, number)
}
func TestSetRetryTimes(t *testing.T) {
assert := internal.NewAssert(t, "TestSetRetryTimes")
var number int
increaseNumber := func() error {
number++
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50), RetryTimes(3))
assert.IsNotNil(err)
assert.Equal(3, number)
}
func TestCancelRetry(t *testing.T) {
assert := internal.NewAssert(t, "TestCancelRetry")
ctx, cancel := context.WithCancel(context.TODO())
var number int
increaseNumber := func() error {
number++
if number > 3 {
cancel()
}
return errors.New("error occurs")
}
err := Retry(increaseNumber,
RetryDuration(time.Microsecond*50),
Context(ctx),
)
assert.IsNotNil(err)
assert.Equal(4, number)
}

View File

@@ -12,12 +12,10 @@ import (
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"unsafe"
) )
// Contain check if the value is in the iterable type or not // Contain check if the value is in the iterable type or not
func Contain(iterableType interface{}, value interface{}) bool { func Contain(iterableType interface{}, value interface{}) bool {
v := reflect.ValueOf(iterableType) v := reflect.ValueOf(iterableType)
switch kind := reflect.TypeOf(iterableType).Kind(); kind { switch kind := reflect.TypeOf(iterableType).Kind(); kind {
@@ -47,6 +45,30 @@ func Contain(iterableType interface{}, value interface{}) bool {
return false return false
} }
// ContainSubSlice check if the slice contain subslice or not
func ContainSubSlice(slice interface{}, subslice interface{}) bool {
super := sliceValue(slice)
sub := sliceValue(subslice)
if super.Type().Elem().Kind() != sub.Type().Elem().Kind() {
return false
}
unique := make(map[interface{}]bool)
for i := 0; i < super.Len(); i++ {
v := super.Index(i).Interface()
unique[v] = true
}
for i := 0; i < sub.Len(); i++ {
v := sub.Index(i).Interface()
if !unique[v] {
return false
}
}
return true
}
// Chunk creates an slice of elements split into groups the length of `size`. // Chunk creates an slice of elements split into groups the length of `size`.
func Chunk(slice []interface{}, size int) [][]interface{} { func Chunk(slice []interface{}, size int) [][]interface{} {
var res [][]interface{} var res [][]interface{}
@@ -80,25 +102,107 @@ func Chunk(slice []interface{}, size int) [][]interface{} {
return res return res
} }
// Difference creates an slice of whose element not included in the other given slice // Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
func Difference(slice1, slice2 interface{}) interface{} { func Compact(slice interface{}) interface{} {
v := sliceValue(slice1) sv := sliceValue(slice)
var indexes []int var indexes []int
for i := 0; i < v.Len(); i++ { for i := 0; i < sv.Len(); i++ {
vi := v.Index(i).Interface() item := sv.Index(i).Interface()
if !Contain(slice2, vi) { if item != nil && item != false && item != "" && item != 0 {
indexes = append(indexes, i)
}
}
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
for i := range indexes {
res.Index(i).Set(sv.Index(indexes[i]))
}
return res.Interface()
}
// Concat creates a new slice concatenating slice with any additional slices and/or values.
func Concat(slice interface{}, values ...interface{}) interface{} {
sv := sliceValue(slice)
size := sv.Len()
res := reflect.MakeSlice(sv.Type(), size, size)
for i := 0; i < size; i++ {
res.Index(i).Set(sv.Index(i))
}
for _, v := range values {
if reflect.TypeOf(v).Kind() == reflect.Slice {
vv := reflect.ValueOf(v)
for i := 0; i < vv.Len(); i++ {
res = reflect.Append(res, vv.Index(i))
}
} else {
res = reflect.Append(res, reflect.ValueOf(v))
}
}
return res.Interface()
}
// Difference creates an slice of whose element in slice1, not in slice2
func Difference(slice1, slice2 interface{}) interface{} {
sv := sliceValue(slice1)
var indexes []int
for i := 0; i < sv.Len(); i++ {
item := sv.Index(i).Interface()
if !Contain(slice2, item) {
indexes = append(indexes, i) indexes = append(indexes, i)
} }
} }
res := reflect.MakeSlice(v.Type(), len(indexes), len(indexes)) res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
for i := range indexes { for i := range indexes {
res.Index(i).Set(v.Index(indexes[i])) res.Index(i).Set(sv.Index(indexes[i]))
} }
return res.Interface() return res.Interface()
} }
// DifferenceBy it accepts iteratee which is invoked for each element of slice
// and values to generate the criterion by which they're compared.
// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy,
// the iterateeFn function signature should be func(index int, value interface{}) interface{}.
func DifferenceBy(slice interface{}, comparedSlice interface{}, iterateeFn interface{}) interface{} {
sv := sliceValue(slice)
smv := sliceValue(comparedSlice)
fn := functionValue(iterateeFn)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
panic("function param should be of type func(" + elemType.String() + ")" + elemType.String())
}
slice1 := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
for i := 0; i < sv.Len(); i++ {
slice1.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0])
}
slice2 := reflect.MakeSlice(smv.Type(), smv.Len(), smv.Len())
for i := 0; i < smv.Len(); i++ {
slice2.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), smv.Index(i)})[0])
}
sliceAfterMap := slice1.Interface()
comparedSliceAfterMap := slice2.Interface()
res := reflect.MakeSlice(sv.Type(), 0, 0)
sm := sliceValue(sliceAfterMap)
for i := 0; i < sm.Len(); i++ {
item := sm.Index(i).Interface()
if !Contain(comparedSliceAfterMap, item) {
res = reflect.Append(res, sv.Index(i))
}
}
return res.Interface()
}
// Every return true if all of the values in the slice pass the predicate function. // Every return true if all of the values in the slice pass the predicate function.
// The function signature should be func(index int, value interface{}) bool . // The function signature should be func(index int, value interface{}) bool .
func Every(slice, function interface{}) bool { func Every(slice, function interface{}) bool {
@@ -404,26 +508,6 @@ func IntSlice(slice interface{}) []int {
return out return out
} }
// 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. // DeleteByIndex delete the element of slice from start index to end index - 1.
// Delete i: s = append(s[:i], s[i+1:]...) // Delete i: s = append(s[:i], s[i+1:]...)
// Delete i to j: s = append(s[:i], s[j:]...) // Delete i to j: s = append(s[:i], s[j:]...)

View File

@@ -23,59 +23,54 @@ func TestContain(t *testing.T) {
assert.Equal(false, Contain("abc", "d")) assert.Equal(false, Contain("abc", "d"))
} }
func TestContainSubSlice(t *testing.T) {
assert := internal.NewAssert(t, "TestContainSubSlice")
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"}))
assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1}))
assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []string{"a"}))
}
func TestChunk(t *testing.T) { func TestChunk(t *testing.T) {
assert := internal.NewAssert(t, "TestChunk") assert := internal.NewAssert(t, "TestChunk")
arr := []string{"a", "b", "c", "d", "e"} arr := []string{"a", "b", "c", "d", "e"}
r1 := [][]interface{}{ r1 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
{"a"},
{"b"},
{"c"},
{"d"},
{"e"},
}
assert.Equal(r1, Chunk(InterfaceSlice(arr), 1)) assert.Equal(r1, Chunk(InterfaceSlice(arr), 1))
r2 := [][]interface{}{ r2 := [][]interface{}{{"a", "b"}, {"c", "d"}, {"e"}}
{"a", "b"},
{"c", "d"},
{"e"},
}
assert.Equal(r2, Chunk(InterfaceSlice(arr), 2)) assert.Equal(r2, Chunk(InterfaceSlice(arr), 2))
r3 := [][]interface{}{ r3 := [][]interface{}{{"a", "b", "c"}, {"d", "e"}}
{"a", "b", "c"},
{"d", "e"},
}
assert.Equal(r3, Chunk(InterfaceSlice(arr), 3)) assert.Equal(r3, Chunk(InterfaceSlice(arr), 3))
r4 := [][]interface{}{ r4 := [][]interface{}{{"a", "b", "c", "d"}, {"e"}}
{"a", "b", "c", "d"},
{"e"},
}
assert.Equal(r4, Chunk(InterfaceSlice(arr), 4)) assert.Equal(r4, Chunk(InterfaceSlice(arr), 4))
r5 := [][]interface{}{ r5 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
{"a"},
{"b"},
{"c"},
{"d"},
{"e"},
}
assert.Equal(r5, Chunk(InterfaceSlice(arr), 5)) assert.Equal(r5, Chunk(InterfaceSlice(arr), 5))
} }
func TestConvertSlice(t *testing.T) { func TestCompact(t *testing.T) {
//t1 := []string{"1","2"} assert := internal.NewAssert(t, "TesCompact")
//aInt, _ := strconv.ParseInt("1", 10, 64)
//bInt, _ := strconv.ParseInt("2", 10, 64) assert.Equal([]int{}, Compact([]int{0}))
//expected :=[]int64{aInt, bInt} assert.Equal([]int{1, 2, 3}, Compact([]int{0, 1, 2, 3}))
// assert.Equal([]string{}, Compact([]string{""}))
//a := ConvertSlice(t1, reflect.TypeOf(expected)) assert.Equal([]string{" "}, Compact([]string{" "}))
//if !reflect.DeepEqual(a, expected) { assert.Equal([]string{"a", "b", "0"}, Compact([]string{"", "a", "b", "0"}))
// utils.LogFailedTestInfo(t, "ConvertSlice", t1, expected, a) assert.Equal([]bool{true, true}, Compact([]bool{false, true, true}))
// t.FailNow() }
//}
func TestConcat(t *testing.T) {
assert := internal.NewAssert(t, "Concat")
assert.Equal([]int{0}, Concat([]int{}, 0))
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, 4, 5))
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4, 5}))
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, 5))
} }
func TestEvery(t *testing.T) { func TestEvery(t *testing.T) {
@@ -421,6 +416,7 @@ func TestIntersection(t *testing.T) {
for i := 0; i < len(res); i++ { for i := 0; i < len(res); i++ {
assert.Equal(res[i], expected[i]) assert.Equal(res[i], expected[i])
} }
assert.IsNil(Intersection())
} }
func TestReverseSlice(t *testing.T) { func TestReverseSlice(t *testing.T) {
@@ -443,6 +439,17 @@ func TestDifference(t *testing.T) {
assert.Equal([]int{1, 2, 3}, Difference(s1, s2)) assert.Equal([]int{1, 2, 3}, Difference(s1, s2))
} }
func TestDifferenceBy(t *testing.T) {
assert := internal.NewAssert(t, "TestDifferenceBy")
s1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6
s2 := []int{3, 4, 5} //after add one: 4 5 6
addOne := func(i int, v int) int {
return v + 1
}
assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne))
}
func TestSortByFielDesc(t *testing.T) { func TestSortByFielDesc(t *testing.T) {
assert := internal.NewAssert(t, "TestWithout") assert := internal.NewAssert(t, "TestWithout")

View File

@@ -54,6 +54,18 @@ func Capitalize(s string) string {
return string(out) return string(out)
} }
// UpperFirst converts the first character of string to upper case.
func UpperFirst(s string) string {
if len(s) == 0 {
return ""
}
r, size := utf8.DecodeRuneInString(s)
r = unicode.ToUpper(r)
return string(r) + s[size:]
}
// LowerFirst converts the first character of string to lower case. // LowerFirst converts the first character of string to lower case.
func LowerFirst(s string) string { func LowerFirst(s string) string {
if len(s) == 0 { if len(s) == 0 {

View File

@@ -50,6 +50,17 @@ func TestSnakeCase(t *testing.T) {
assert.NotEqual("foo-bar", SnakeCase("foo_Bar")) assert.NotEqual("foo-bar", SnakeCase("foo_Bar"))
} }
func TestUpperFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst")
assert.Equal("Foo", UpperFirst("foo"))
assert.Equal("BAR", UpperFirst("bAR"))
assert.Equal("FOo", UpperFirst("FOo"))
assert.Equal("FOo大", UpperFirst("fOo大"))
assert.NotEqual("Bar", UpperFirst("BAR"))
}
func TestLowerFirst(t *testing.T) { func TestLowerFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst") assert := internal.NewAssert(t, "TestLowerFirst")

72
system/os.go Normal file
View File

@@ -0,0 +1,72 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package system contain some functions about os, runtime, shell command.
package system
import (
"bytes"
"os"
"os/exec"
"runtime"
)
// IsWindows check if current os is windows
func IsWindows() bool {
return runtime.GOOS == "windows"
}
// IsLinux check if current os is linux
func IsLinux() bool {
return runtime.GOOS == "linux"
}
// IsMac check if current os is macos
func IsMac() bool {
return runtime.GOOS == "darwin"
}
// GetOsEnv gets the value of the environment variable named by the key.
func GetOsEnv(key string) string {
return os.Getenv(key)
}
// SetOsEnv sets the value of the environment variable named by the key.
func SetOsEnv(key, value string) error {
return os.Setenv(key, value)
}
// RemoveOsEnv remove a single environment variable.
func RemoveOsEnv(key string) error {
return os.Unsetenv(key)
}
// CompareOsEnv gets env named by the key and compare it with comparedEnv
func CompareOsEnv(key, comparedEnv string) bool {
env := GetOsEnv(key)
if env == "" {
return false
}
return env == comparedEnv
}
// ExecCommand use shell /bin/bash -c to execute command
func ExecCommand(command string) (stdout, stderr string, err error) {
var out bytes.Buffer
var errout bytes.Buffer
cmd := exec.Command("/bin/bash", "-c", command)
if IsWindows() {
cmd = exec.Command("cmd")
}
cmd.Stdout = &out
cmd.Stderr = &errout
err = cmd.Run()
if err != nil {
stderr = string(errout.Bytes())
}
stdout = string(out.Bytes())
return
}

62
system/os_test.go Normal file
View File

@@ -0,0 +1,62 @@
package system
import (
"strings"
"testing"
"github.com/duke-git/lancet/internal"
)
func TestOsDetection(t *testing.T) {
assert := internal.NewAssert(t, "TestOsJudgment")
osType, _, _ := ExecCommand("echo $OSTYPE")
if strings.Index(osType, "linux") != -1 {
assert.Equal(true, IsLinux())
}
if strings.Index(osType, "darwin") != -1 {
assert.Equal(true, IsMac())
}
}
func TestOsEnvOperation(t *testing.T) {
assert := internal.NewAssert(t, "TestOsEnvOperation")
envNotExist := GetOsEnv("foo")
assert.Equal("", envNotExist)
SetOsEnv("foo", "foo_value")
envExist := GetOsEnv("foo")
assert.Equal("foo_value", envExist)
assert.Equal(true, CompareOsEnv("foo", "foo_value"))
assert.Equal(false, CompareOsEnv("foo", "abc"))
assert.Equal(false, CompareOsEnv("abc", "abc"))
assert.Equal(false, CompareOsEnv("abc", "abc"))
err := RemoveOsEnv("foo")
if err != nil {
t.Fail()
}
assert.Equal(false, CompareOsEnv("foo", "foo_value"))
}
func TestExecCommand(t *testing.T) {
assert := internal.NewAssert(t, "TestExecCommand")
out, errout, err := ExecCommand("ls")
t.Log("std out: ", out)
t.Log("std err: ", errout)
assert.IsNil(err)
out, errout, err = ExecCommand("abc")
t.Log("std out: ", out)
t.Log("std err: ", errout)
if err != nil {
t.Logf("error: %v\n", err)
}
if !IsWindows() {
assert.IsNotNil(err)
}
}

View File

@@ -5,17 +5,73 @@
package validator package validator
import ( import (
"encoding/json"
"net" "net"
"net/url"
"regexp" "regexp"
"strconv" "strconv"
"strings"
"unicode" "unicode"
) )
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
// IsAlpha checks if the string contains only letters (a-zA-Z) // IsAlpha checks if the string contains only letters (a-zA-Z)
func IsAlpha(s string) bool { func IsAlpha(str string) bool {
return isAlphaRegexMatcher.MatchString(s) return isAlphaRegexMatcher.MatchString(str)
}
// IsAllUpper check if the string is all upper case letters A-Z
func IsAllUpper(str string) bool {
for _, r := range str {
if !unicode.IsUpper(r) {
return false
}
}
return str != ""
}
// IsAllLower check if the string is all lower case letters a-z
func IsAllLower(str string) bool {
for _, r := range str {
if !unicode.IsLower(r) {
return false
}
}
return str != ""
}
// ContainUpper check if the string contain at least one upper case letter A-Z
func ContainUpper(str string) bool {
for _, r := range str {
if unicode.IsUpper(r) && unicode.IsLetter(r) {
return true
}
}
return false
}
// ContainLower check if the string contain at least one lower case letter A-Z
func ContainLower(str string) bool {
for _, r := range str {
if unicode.IsLower(r) && unicode.IsLetter(r) {
return true
}
}
return false
}
var containLetterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
// ContainLetter check if the string contain at least one letter
func ContainLetter(str string) bool {
return containLetterRegexMatcher.MatchString(str)
}
// IsJSON checks if the string is valid JSON
func IsJSON(str string) bool {
var js json.RawMessage
return json.Unmarshal([]byte(str), &js) == nil
} }
// IsNumberStr check if the string can convert to a number. // IsNumberStr check if the string can convert to a number.
@@ -24,16 +80,16 @@ func IsNumberStr(s string) bool {
} }
// IsFloatStr check if the string can convert to a float. // IsFloatStr check if the string can convert to a float.
func IsFloatStr(s string) bool { func IsFloatStr(str string) bool {
_, e := strconv.ParseFloat(s, 64) _, e := strconv.ParseFloat(str, 64)
return e == nil return e == nil
} }
var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`) var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
// IsIntStr check if the string can convert to a integer. // IsIntStr check if the string can convert to a integer.
func IsIntStr(s string) bool { func IsIntStr(str string) bool {
return isIntStrRegexMatcher.MatchString(s) return isIntStrRegexMatcher.MatchString(str)
} }
// IsIp check if the string is a ip address. // IsIp check if the string is a ip address.
@@ -48,13 +104,7 @@ func IsIpV4(ipstr string) bool {
if ip == nil { if ip == nil {
return false return false
} }
for i := 0; i < len(ipstr); i++ { return strings.Contains(ipstr, ".")
switch ipstr[i] {
case '.':
return true
}
}
return false
} }
// IsIpV6 check if the string is a ipv6 address. // IsIpV6 check if the string is a ipv6 address.
@@ -63,15 +113,38 @@ func IsIpV6(ipstr string) bool {
if ip == nil { if ip == nil {
return false return false
} }
for i := 0; i < len(ipstr); i++ { return strings.Contains(ipstr, ":")
switch ipstr[i] { }
case ':':
return true // IsPort check if the string is a valid net port.
} func IsPort(str string) bool {
if i, err := strconv.ParseInt(str, 10, 64); err == nil && i > 0 && i < 65536 {
return true
} }
return false return false
} }
var isUrlRegexMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
// IsUrl check if the string is url.
func IsUrl(str string) bool {
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
return false
}
u, err := url.Parse(str)
if err != nil {
return false
}
if strings.HasPrefix(u.Host, ".") {
return false
}
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
return false
}
return isUrlRegexMatcher.MatchString(str)
}
var isDnsRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`) var isDnsRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
// IsDns check if the string is dns. // IsDns check if the string is dns.
@@ -130,14 +203,14 @@ func IsBase64(base64 string) bool {
} }
// IsEmptyString check if the string is empty. // IsEmptyString check if the string is empty.
func IsEmptyString(s string) bool { func IsEmptyString(str string) bool {
return len(s) == 0 return len(str) == 0
} }
// IsRegexMatch check if the string match the regexp // IsRegexMatch check if the string match the regexp
func IsRegexMatch(s, regex string) bool { func IsRegexMatch(str, regex string) bool {
reg := regexp.MustCompile(regex) reg := regexp.MustCompile(regex)
return reg.MatchString(s) return reg.MatchString(str)
} }
// IsStrongPassword check if the string is strong password, if len(password) is less than the length param, return false // IsStrongPassword check if the string is strong password, if len(password) is less than the length param, return false

View File

@@ -6,6 +6,96 @@ import (
"github.com/duke-git/lancet/internal" "github.com/duke-git/lancet/internal"
) )
func TestIsAllUpper(t *testing.T) {
assert := internal.NewAssert(t, "TestIsAllUpper")
assert.Equal(true, IsAllUpper("ABC"))
assert.Equal(false, IsAllUpper(""))
assert.Equal(false, IsAllUpper("abc"))
assert.Equal(false, IsAllUpper("aBC"))
assert.Equal(false, IsAllUpper("1BC"))
assert.Equal(false, IsAllUpper("1bc"))
assert.Equal(false, IsAllUpper("123"))
assert.Equal(false, IsAllUpper("你好"))
assert.Equal(false, IsAllUpper("A&"))
assert.Equal(false, IsAllUpper("&@#$%^&*"))
}
func TestIsAllLower(t *testing.T) {
assert := internal.NewAssert(t, "TestIsAllLower")
assert.Equal(true, IsAllLower("abc"))
assert.Equal(false, IsAllLower("ABC"))
assert.Equal(false, IsAllLower(""))
assert.Equal(false, IsAllLower("aBC"))
assert.Equal(false, IsAllLower("1BC"))
assert.Equal(false, IsAllLower("1bc"))
assert.Equal(false, IsAllLower("123"))
assert.Equal(false, IsAllLower("你好"))
assert.Equal(false, IsAllLower("A&"))
assert.Equal(false, IsAllLower("&@#$%^&*"))
}
func TestContainLower(t *testing.T) {
assert := internal.NewAssert(t, "TestContainLower")
assert.Equal(true, ContainLower("abc"))
assert.Equal(true, ContainLower("aBC"))
assert.Equal(true, ContainLower("1bc"))
assert.Equal(true, ContainLower("a&"))
assert.Equal(false, ContainLower("ABC"))
assert.Equal(false, ContainLower(""))
assert.Equal(false, ContainLower("1BC"))
assert.Equal(false, ContainLower("123"))
assert.Equal(false, ContainLower("你好"))
assert.Equal(false, ContainLower("&@#$%^&*"))
}
func TestContainUpper(t *testing.T) {
assert := internal.NewAssert(t, "TestContainUpper")
assert.Equal(true, ContainUpper("ABC"))
assert.Equal(true, ContainUpper("aBC"))
assert.Equal(true, ContainUpper("1BC"))
assert.Equal(true, ContainUpper("A&"))
assert.Equal(false, ContainUpper("abc"))
assert.Equal(false, ContainUpper(""))
assert.Equal(false, ContainUpper("1bc"))
assert.Equal(false, ContainUpper("123"))
assert.Equal(false, ContainUpper("你好"))
assert.Equal(false, ContainUpper("&@#$%^&*"))
}
func TestContainLetter(t *testing.T) {
assert := internal.NewAssert(t, "TestContainLetter")
assert.Equal(true, ContainLetter("ABC"))
assert.Equal(true, ContainLetter("1Bc"))
assert.Equal(true, ContainLetter("1ab"))
assert.Equal(true, ContainLetter("A&"))
assert.Equal(false, ContainLetter(""))
assert.Equal(false, ContainLetter("123"))
assert.Equal(false, ContainLetter("你好"))
assert.Equal(false, ContainLetter("&@#$%^&*"))
}
func TestIsJSON(t *testing.T) {
assert := internal.NewAssert(t, "TestIsJSON")
assert.Equal(true, IsJSON("{}"))
assert.Equal(true, IsJSON("{\"name\": \"test\"}"))
assert.Equal(true, IsJSON("[]"))
assert.Equal(true, IsJSON("123"))
assert.Equal(false, IsJSON(""))
assert.Equal(false, IsJSON("abc"))
assert.Equal(false, IsJSON("你好"))
assert.Equal(false, IsJSON("&@#$%^&*"))
}
func TestIsNumberStr(t *testing.T) { func TestIsNumberStr(t *testing.T) {
assert := internal.NewAssert(t, "TestIsNumberStr") assert := internal.NewAssert(t, "TestIsNumberStr")
@@ -35,6 +125,18 @@ func TestIsIntStr(t *testing.T) {
assert.Equal(false, IsIntStr("abc")) assert.Equal(false, IsIntStr("abc"))
} }
func TestIsPort(t *testing.T) {
assert := internal.NewAssert(t, "TestIsPort")
assert.Equal(true, IsPort("1"))
assert.Equal(true, IsPort("65535"))
assert.Equal(false, IsPort("abc"))
assert.Equal(false, IsPort("123abc"))
assert.Equal(false, IsPort(""))
assert.Equal(false, IsPort("-1"))
assert.Equal(false, IsPort("65536"))
}
func TestIsIp(t *testing.T) { func TestIsIp(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIntStr") assert := internal.NewAssert(t, "TestIsIntStr")
@@ -58,11 +160,21 @@ func TestIsIpV6(t *testing.T) {
assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1")) assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1"))
} }
func TestIsUrl(t *testing.T) {
assert := internal.NewAssert(t, "TestIsUrl")
assert.Equal(true, IsUrl("http://abc.com"))
assert.Equal(true, IsUrl("abc.com"))
assert.Equal(true, IsUrl("a.b.com"))
assert.Equal(false, IsUrl("abc"))
}
func TestIsDns(t *testing.T) { func TestIsDns(t *testing.T) {
assert := internal.NewAssert(t, "TestIsDns") assert := internal.NewAssert(t, "TestIsDns")
assert.Equal(true, IsDns("abc.com")) assert.Equal(true, IsDns("abc.com"))
assert.Equal(false, IsDns("a.b.com")) assert.Equal(false, IsDns("a.b.com"))
assert.Equal(false, IsDns("http://abc.com"))
} }
func TestIsEmail(t *testing.T) { func TestIsEmail(t *testing.T) {