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

Compare commits

...

91 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
dudaodong
69453eba19 release v1.1.9 2022-01-11 20:42:04 +08:00
donutloop
f147f78a41 Slice: sort from v2 branch (#22)
ref: f1d7154179
2022-01-11 20:13:25 +08:00
dudaodong
bbfc5b7060 delete file utils.go and fix some misspel 2022-01-09 16:12:26 +08:00
dudaodong
1f45937190 refactor: rewrite all unit test functions with assert 2022-01-09 16:04:33 +08:00
dudaodong
52c5a91606 refactor: rewrite all unit test functions with assert 2022-01-09 15:57:21 +08:00
dudaodong
49f62c3550 refactor: rewrite all unit test functions with assert 2022-01-09 15:53:30 +08:00
dudaodong
23701e6998 refactor: rewrite all unit test functions with assert 2022-01-09 15:49:52 +08:00
dudaodong
1199c30ef3 refactor: rewrite all unit test functions with assert 2022-01-09 15:48:29 +08:00
dudaodong
b0e17c7bc4 refactor: rewrite all unit test functions with assert 2022-01-09 15:39:50 +08:00
dudaodong
d3525dfe8f refactor: rewrite all unit test functions with assert 2022-01-09 15:34:17 +08:00
dudaodong
9da7115169 refactor: rewrite all unit test functions with assert 2022-01-09 15:10:56 +08:00
dudaodong
9cb9aa2f2f refactor: rewrite all unit test functions with assert 2022-01-09 14:46:17 +08:00
dudaodong
e4cd7dad35 refactor: rewrite all unit test functions with assert 2022-01-09 14:31:31 +08:00
dudaodong
31e08197d4 remove file comment 2022-01-09 14:03:46 +08:00
dudaodong
642d0b8077 refactor: rewrite all unit test functions with assert 2022-01-09 14:01:51 +08:00
dudaodong
25b2ae6b98 fix: return empty byte slice when rand bytes lenght less 1 2022-01-09 14:00:23 +08:00
dudaodong
65719515bd feat: add LessOrEqual and GreaterOrEqual 2022-01-09 13:58:27 +08:00
dudaodong
3ffd81a98a refactor: rewrite all unit test functions with assert 2022-01-09 13:51:07 +08:00
dudaodong
f490ef2404 refactor: rewrite all unit test functions with assert 2022-01-09 13:30:08 +08:00
dudaodong
3438f3b18a refactor: rewrite all unit test functions with assert 2022-01-09 13:07:49 +08:00
dudaodong
73f4ae7b35 refactor: add internal/assert.go and rewrite all unit funcs string_test.go with assert 2022-01-08 21:58:35 +08:00
rumikk
a8996933bf ToJson fix error handling (#16) 2022-01-07 15:00:05 +08:00
donutloop
3905c0bde1 Slice: Add count func (#15)
Returns the count of matched elements
2022-01-06 20:32:14 +08:00
dudaodong
c7e961704d Merge branch 'main' of github.com:duke-git/lancet into main 2022-01-06 17:09:43 +08:00
dudaodong
cb7df1b57d update: add some new feature comments for file and slice 2022-01-06 17:09:01 +08:00
dudaodong
eeff28606e feat: add IsLink, FileMode, MiMeType funcs for file 2022-01-06 16:53:32 +08:00
dudaodong
86d4b25a2b feat: and Zip and UnZip func for file operation 2022-01-06 15:15:59 +08:00
Ahmad Alfy
ad287ed99a doc: minor spelling mistak (#14) 2022-01-05 21:25:21 +08:00
dudaodong
df9de3065b feat: add ForEach func 2022-01-05 20:17:16 +08:00
dudaodong
71a2ea3f20 update: change case name for TestNone func 2022-01-05 19:46:51 +08:00
dudaodong
955f2e6de6 update func GetElapsedTime and None comment 2022-01-05 19:44:22 +08:00
donutloop
4aef9d6d22 Slice: Add none func (#13)
Returns true whether no elements of this slice match the provided predicate
func. Negated form of Every func
2022-01-05 19:38:14 +08:00
dudaodong
4752725dd6 add Wrap and Unwrap func comment 2022-01-04 11:22:54 +08:00
dudaodong
07d1704cb2 release v1.1.7 2022-01-04 10:56:05 +08:00
dudaodong
74d262e609 Merge branch 'main' of github.com:duke-git/lancet into main 2022-01-03 20:11:32 +08:00
dudaodong
97e0789ea4 feat: add Wrap and Unwrap func 2022-01-03 20:11:15 +08:00
donutloop
bc39b0887b Filter: remove second loop and indexes slice (#12)
Use less memory and cpu resources to filter out elements from the slice.
2022-01-03 20:10:15 +08:00
dudaodong
4fd8a18677 release v1.1.6 2022-01-03 15:58:19 +08:00
dudaodong
3de906d7c9 refactor: reduce gocyclo of doHttpRequest func 2022-01-03 15:53:25 +08:00
dudaodong
56fc12c660 refactor: reduce gocyclo of ToString func 2022-01-03 15:15:22 +08:00
dudaodong
7a9b0847f9 fix: fix some go line issue in go report card 2022-01-03 14:57:14 +08:00
dudaodong
9266d99249 add chinese comment for GroupBy func 2022-01-02 22:12:01 +08:00
donutloop
f15131f032 Slice: find nothing case (#11)
The current behavior can result into wired edge cases.

**Current behavior**

if find was unsuccessfully then it will return the first element of the
slice

**Desired behavior**

if find was unsuccessfully then it should return negative ok and a none
value
2022-01-02 22:05:27 +08:00
donutloop
b4a49fccfd Slice: Add GroupBy func (#10)
Group by: split slice into two groups, applies on each slice element a
predicate func to categorize this element.

Changes

* Add groub by func
* Add test case for this func
2022-01-02 21:24:56 +08:00
dudaodong
94b1a1d383 feat: add Reset func for watcher 2022-01-01 20:14:41 +08:00
dudaodong
7db46696f6 release v1.1.5 2022-01-01 20:13:21 +08:00
dudaodong
d4f49af2ad feat: add watcher for record code excution time 2022-01-01 19:56:15 +08:00
dudaodong
ed4acc1c67 feat: add Shuffle func 2022-01-01 19:18:33 +08:00
dudaodong
042a00296f feat: add FlattenDeep func 2022-01-01 18:14:35 +08:00
dudaodong
b42aac53b3 feat: add Drop func 2022-01-01 17:20:41 +08:00
dudaodong
c625a88067 refactor: rename package 'utils' to 'internal' 2021-12-31 11:36:11 +08:00
dudaodong
e15ae9eb98 update: add Md5String func and Md5File func 2021-12-31 11:25:43 +08:00
dudaodong
bb4ac01209 Merge branch 'main' of github.com:duke-git/lancet into main 2021-12-31 10:18:11 +08:00
donutloop
147c1625b5 Slice: Optimize slice func tools (#9)
IntSlice and StringSlice: preallocate memory up front for output slice.
Instead returning a error throw a panic because most likely it's a
programming error.
Contain: rename slice to iterableType and add default case for type
mismatches
2021-12-31 10:15:38 +08:00
dudaodong
2aed55f704 refactor: function in slice.go 2021-12-31 10:12:14 +08:00
dudaodong
051f20caef release: v1.1.4, merge pr7 and pr8 2021-12-30 20:25:20 +08:00
donutloop
613785b07c function: catch earlier programming error (#8)
Place at first line of the function body a function type safe guard
check.
2021-12-30 20:22:00 +08:00
donutloop
0b0eb695e8 datetime: optimized func calls (#7)
Replace time parsing calls with less expensive operations
2021-12-30 10:29:10 +08:00
dudaodong
745082fff1 fix: update api url for http timeout issue in the task of github actions 2021-12-29 11:39:48 +08:00
dudaodong
24b8da360e release: v1.1.3, merge pr5 and pr6 2021-12-29 09:55:42 +08:00
donutloop
b106c428ae Every: use int counter (#6)
Use counter to verify all elements passed the perdicate func.
Replace slice of indexes with int counter.
2021-12-29 09:51:50 +08:00
dudaodong
8b1171d0cb fmt: go fmt for request_test.go 2021-12-28 19:28:55 +08:00
donutloop
ab012f2545 LowerFirst: use slicing and utf8 func tools (#5)
Replace looping with slicing and utf8 func tools operations.
2021-12-28 19:25:44 +08:00
41 changed files with 2734 additions and 1431 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

3
.gitignore vendored
View File

@@ -3,4 +3,7 @@
.DS_Store .DS_Store
cryptor/*.txt cryptor/*.txt
fileutil/*.txt fileutil/*.txt
fileutil/*.zip
fileutil/*.link
fileutil/unzip/*
cryptor/*.pem cryptor/*.pem

170
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.2-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.
- 💪 100+ 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 exery 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"
@@ -145,6 +142,8 @@ func HmacMd5(data, key string) string //get hmac md5 value
func HmacSha1(data, key string) string //get hmac sha1 value func HmacSha1(data, key string) string //get hmac sha1 value
func HmacSha256(data, key string) string //get hmac sha256 value func HmacSha256(data, key string) string //get hmac sha256 value
func HmacSha512(data, key string) string //get hmac sha512 value func HmacSha512(data, key string) string //get hmac sha512 value
func Md5String(s string) string //return the md5 value of string
func Md5File(filename string) (string, error) //return the md5 value of file
func Sha1(data string) string //get sha1 value func Sha1(data string) string //get sha1 value
func Sha256(data string) string //getsha256 value func Sha256(data string) string //getsha256 value
func Sha512(data string) string //get sha512 value func Sha512(data string) string //get sha512 value
@@ -154,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"
@@ -189,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"
@@ -213,15 +212,20 @@ func main() {
func ClearFile(path string) error //write empty string to path file func ClearFile(path string) error //write empty string to path file
func CreateFile(path string) bool // create a file in path func CreateFile(path string) bool // create a file in path
func CopyFile(srcFilePath string, dstFilePath string) error //copy src file to dst file func CopyFile(srcFilePath string, dstFilePath string) error //copy src file to dst file
func FileMode(path string) (fs.FileMode, error) //return file's mode and permission
func MiMeType(file interface{}) string //return file mime type, file should be string or *os.File
func IsExist(path string) bool //checks if a file or directory exists func IsExist(path string) bool //checks if a file or directory exists
func IsLink(path string) bool //checks if a file is symbol link or not
func IsDir(path string) bool //checks if the path is directy or not 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 ListFileNames(path string) ([]string, error) //return all file names in the path
func RemoveFile(path string) error //remove the path file func RemoveFile(path string) error //remove the path file
func ReadFileToString(path string) (string, error) //return string of file content func ReadFileToString(path string) (string, error) //return string of file content
func ReadFileByLine(path string)([]string, error) //read file content by line func ReadFileByLine(path string)([]string, error) //read file content by line
func Zip(fpath string, destPath string) error //create zip file, fpath could be a single file or a directory
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"
@@ -246,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"
@@ -274,11 +278,16 @@ 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) Stop() //stop the watch timer
func (w *Watcher) Reset() {} //reset the watch timer.
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".
@@ -326,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".
@@ -355,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"
@@ -382,13 +433,20 @@ 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 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 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{} //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 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
func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice
func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices. func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices.
@@ -396,16 +454,19 @@ func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}
func Map(slice, function interface{}) interface{} //map lisce, function signature should be func(index int, value interface{}) interface{} 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 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 Reduce(slice, function, zero interface{}) interface{} //reduce slice, function signature should be func(index int, value1, value2 interface{}) interface{}
func Shuffle(slice interface{}) interface{} //creates an slice of shuffled values
func SortByField(slice interface{}, field string, sortType ...string) error //sort struct slice by field func SortByField(slice interface{}, field string, sortType ...string) error //sort struct slice by field
func Some(slice, function interface{}) bool //return true if any of the values in the list pass the predicate function, function signature should be func(index int, value interface{}) bool func 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 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"
@@ -439,13 +500,49 @@ 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
func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar" func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_bar"
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
```
### 12. system contain some functions about os, runtime, shell command.
- 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)
}
``` ```
#### 11. validator is for data validation - 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".
@@ -473,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
@@ -488,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.2-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>
### 特性 ## 特性
- 👏 全面、高效、可复用 - 👏 全面、高效、可复用
- 💪 100+常用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"
@@ -146,6 +142,8 @@ func HmacMd5(data, key string) string //获取hmac md5值
func HmacSha1(data, key string) string //获取hmac sha1值 func HmacSha1(data, key string) string //获取hmac sha1值
func HmacSha256(data, key string) string //获取hmac sha256值 func HmacSha256(data, key string) string //获取hmac sha256值
func HmacSha512(data, key string) string //获取hmac sha512值 func HmacSha512(data, key string) string //获取hmac sha512值
func Md5String(s string) string //获取字符串md5值
func Md5File(filename string) (string, error) //获取文件md5值
func Sha1(data string) string //获取sha1值 func Sha1(data string) string //获取sha1值
func Sha256(data string) string //获取sha256值 func Sha256(data string) string //获取sha256值
func Sha512(data string) string //获取sha512值 func Sha512(data string) string //获取sha512值
@@ -155,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"
@@ -190,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"
@@ -212,17 +210,22 @@ func main() {
```go ```go
func ClearFile(path string) error //清空文件内容 func ClearFile(path string) error //清空文件内容
func IsExist(path string) bool //判断文件/目录是否存在
func CreateFile(path string) bool //创建文件 func CreateFile(path string) bool //创建文件
func FileMode(path string) (fs.FileMode, error) //返回文件mode信息
func MiMeType(file interface{}) string //返回文件mime类型
func IsExist(path string) bool //判断文件/目录是否存在
func IsDir(path string) bool //判断是否为目录 func IsDir(path string) bool //判断是否为目录
func IsLink(path string) bool //检查文件是否为符号链接文件
func RemoveFile(path string) error //删除文件 func RemoveFile(path string) error //删除文件
func CopyFile(srcFilePath string, dstFilePath string) error //复制文件 func CopyFile(srcFilePath string, dstFilePath string) error //复制文件
func ListFileNames(path string) ([]string, error) //列出目录下所有文件名称 func ListFileNames(path string) ([]string, error) //列出目录下所有文件名称
func ReadFileToString(path string) (string, error) //读取文件内容为字符串 func ReadFileToString(path string) (string, error) //读取文件内容为字符串
func ReadFileByLine(path string)([]string, error) //按行读取文件内容 func ReadFileByLine(path string)([]string, error) //按行读取文件内容
func Zip(fpath string, destPath string) error //压缩文件fpath参数可以是文件或目录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"
@@ -247,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"
@@ -276,10 +279,15 @@ 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) Stop() //开时watcher
func (w *Watcher) Reset() {} //重置代码watcher
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"
@@ -327,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"
@@ -356,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"
@@ -383,13 +433,20 @@ 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 Difference(slice1, slice2 interface{}) interface{} //返回 func Concat(slice interface{}, values ...interface{}) interface{} //连接values到slice中
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 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
func Find(slice, function interface{}) interface{} //查找slice中第一个符合条件的元素,函数签名func(index int, value interface{}) bool func None(slice, function interface{}) bool //slice中所有元素都不符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool
func Find(slice, function interface{}) (interface{}, bool)//查找slice中第一个符合条件的元素函数签名func(index int, value interface{}) bool
func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名func(index int, value interface{}) bool func Filter(slice, function interface{}) interface{} //过滤slice, 函数签名func(index int, value interface{}) bool
func FlattenDeep(slice interface{}) interface{} //将slice递归为一维切片。
func ForEach(slice, function interface{}) //遍历切片在每个元素上执行函数函数签名func(index int, value interface{})
func IntSlice(slice interface{}) ([]int, error) //转成int切片 func IntSlice(slice interface{}) ([]int, error) //转成int切片
func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片 func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片
func Intersection(slices ...interface{}) interface{} //slice交集去重 func Intersection(slices ...interface{}) interface{} //slice交集去重
@@ -397,6 +454,7 @@ func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}
func Map(slice, function interface{}) interface{} //遍历切片, 函数签名func(index int, value interface{}) interface{} func Map(slice, function interface{}) interface{} //遍历切片, 函数签名func(index int, value interface{}) interface{}
func ReverseSlice(slice interface{}) //反转切片 func ReverseSlice(slice interface{}) //反转切片
func Reduce(slice, function, zero interface{}) interface{} //切片reduce操作 函数签名func(index int, value1, value2 interface{}) interface{} func Reduce(slice, function, zero interface{}) interface{} //切片reduce操作 函数签名func(index int, value1, value2 interface{}) interface{}
func Shuffle(slice interface{}) interface{} //创建一个被打乱值的切片
func Some(slice, function interface{}) bool //slice中任意一个元素都符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool func Some(slice, function interface{}) bool //slice中任意一个元素都符合函数条件时返回true, 否则返回false. 函数签名func(index int, value interface{}) bool
func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序 func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序
func StringSlice(slice interface{}) []string //转为string切片 func StringSlice(slice interface{}) []string //转为string切片
@@ -404,9 +462,11 @@ func Unique(slice interface{}) interface{} //去重切片
func Union(slices ...interface{}) interface{} //slice并集, 去重 func Union(slices ...interface{}) interface{} //slice并集, 去重
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value
func Without(slice interface{}, values ...interface{}) interface{} //slice去除values func Without(slice interface{}, values ...interface{}) interface{} //slice去除values
func GroupBy(slice, function interface{}) (interface{}, interface{}) //根据函数function的逻辑分slice为两组slice
func Count(slice, function interface{}) int
``` ```
#### 10. strutil字符串处理包 ### 11. strutil字符串处理包
- 字符串操作相关函数 - 字符串操作相关函数
- 导入包import "github.com/duke-git/lancet/strutil" - 导入包import "github.com/duke-git/lancet/strutil"
@@ -439,14 +499,51 @@ 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个字符
func ReverseStr(s string) string //字符串逆 func ReverseStr(s string) string //字符串逆
func Wrap(str string, wrapWith string) string //包裹字符串 Wrap("abc", "*") -> *abc*.
func Unwrap(str string, wrapToken string) string //解包裹字符串 Wrap("*abc*", "*") -> 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"
@@ -474,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 //判断字符串是否是座机电话号码
@@ -489,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

@@ -45,54 +45,44 @@ func ToChar(s string) []string {
// ToString convert value to string // ToString convert value to string
func ToString(value interface{}) string { func ToString(value interface{}) string {
var res string res := ""
if value == nil { if value == nil {
return res return res
} }
switch v := value.(type) {
case float64: v := reflect.ValueOf(value)
res = strconv.FormatFloat(v, 'f', -1, 64)
case float32: switch value.(type) {
res = strconv.FormatFloat(float64(v), 'f', -1, 64) case float32, float64:
case int: res = strconv.FormatFloat(v.Float(), 'f', -1, 64)
res = strconv.Itoa(v) return res
case uint: case int, int8, int16, int32, int64:
res = strconv.Itoa(int(v)) res = strconv.FormatInt(v.Int(), 10)
case int8: return res
res = strconv.Itoa(int(v)) case uint, uint8, uint16, uint32, uint64:
case uint8: res = strconv.FormatUint(v.Uint(), 10)
res = strconv.Itoa(int(v)) return res
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: case string:
res = value.(string) res = v.String()
return res
case []byte: case []byte:
res = string(value.([]byte)) res = string(v.Bytes())
return res
default: default:
newValue, _ := json.Marshal(value) newValue, _ := json.Marshal(value)
res = string(newValue) res = string(newValue)
return res
} }
return res
} }
// ToJson convert value to a valid json string // ToJson convert value to a valid json string
func ToJson(value interface{}) (string, error) { func ToJson(value interface{}) (string, error) {
res, err := json.Marshal(value) res, err := json.Marshal(value)
if err != nil { if err != nil {
res = []byte("") return "", err
} }
return string(res), err return string(res), nil
} }
// ToFloat convert value to a float64, if input is not a float return 0.0 and error // ToFloat convert value to a float64, if input is not a float return 0.0 and error

View File

@@ -2,13 +2,14 @@ package convertor
import ( import (
"fmt" "fmt"
"reflect"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestToChar(t *testing.T) { func TestToChar(t *testing.T) {
assert := internal.NewAssert(t, "TestToChar")
cases := []string{"", "abc", "1 2#3"} cases := []string{"", "abc", "1 2#3"}
expected := [][]string{ expected := [][]string{
{""}, {""},
@@ -16,28 +17,25 @@ func TestToChar(t *testing.T) {
{"1", " ", "2", "#", "3"}, {"1", " ", "2", "#", "3"},
} }
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := ToChar(cases[i]) assert.Equal(expected[i], 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) { func TestToBool(t *testing.T) {
assert := internal.NewAssert(t, "TestToBool")
cases := []string{"true", "True", "false", "False", "0", "1", "123"} cases := []string{"true", "True", "false", "False", "0", "1", "123"}
expected := []bool{true, true, false, false, false, true, false} expected := []bool{true, true, false, false, false, true, false}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res, _ := ToBool(cases[i]) actual, _ := ToBool(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "ToBool", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestToBytes(t *testing.T) { func TestToBytes(t *testing.T) {
assert := internal.NewAssert(t, "TestToBytes")
cases := []interface{}{ cases := []interface{}{
0, 0,
false, false,
@@ -49,16 +47,14 @@ func TestToBytes(t *testing.T) {
{4, 12, 0, 1, 49}, {4, 12, 0, 1, 49},
} }
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res, _ := ToBytes(cases[i]) actual, _ := ToBytes(cases[i])
fmt.Println(res) assert.Equal(expected[i], actual)
if !reflect.DeepEqual(res, expected[i]) {
utils.LogFailedTestInfo(t, "ToBytes", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestToInt(t *testing.T) { func TestToInt(t *testing.T) {
assert := internal.NewAssert(t, "TestToInt")
cases := []interface{}{"123", "-123", 123, cases := []interface{}{"123", "-123", 123,
uint(123), uint8(123), uint16(123), uint32(123), uint64(123), uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float32(12.3), float64(12.3), float32(12.3), float64(12.3),
@@ -67,15 +63,14 @@ func TestToInt(t *testing.T) {
expected := []int64{123, -123, 123, 123, 123, 123, 123, 123, 12, 12, 0, 0, 0} expected := []int64{123, -123, 123, 123, 123, 123, 123, 123, 12, 12, 0, 0, 0}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res, _ := ToInt(cases[i]) actual, _ := ToInt(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "ToInt", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestToFloat(t *testing.T) { func TestToFloat(t *testing.T) {
assert := internal.NewAssert(t, "TestToFloat")
cases := []interface{}{ cases := []interface{}{
"", "-1", "-.11", "1.23e3", ".123e10", "abc", "", "-1", "-.11", "1.23e3", ".123e10", "abc",
int(0), int8(1), int16(-1), int32(123), int64(123), int(0), int8(1), int16(-1), int32(123), int64(123),
@@ -86,108 +81,74 @@ func TestToFloat(t *testing.T) {
0, 1, -1, 123, 123, 123, 123, 123, 123, 123, 12.3, 12.300000190734863} 0, 1, -1, 123, 123, 123, 123, 123, 123, 123, 12.3, 12.300000190734863}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res, _ := ToFloat(cases[i]) actual, _ := ToFloat(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "ToFloat", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestToString(t *testing.T) { func TestToString(t *testing.T) {
// map assert := internal.NewAssert(t, "TestToString")
aMap := make(map[string]int) aMap := make(map[string]int)
aMap["a"] = 1 aMap["a"] = 1
aMap["b"] = 2 aMap["b"] = 2
aMap["c"] = 3 aMap["c"] = 3
// struct
type TestStruct struct { type TestStruct struct {
Name string Name string
} }
aStruct := TestStruct{Name: "TestStruct"} aStruct := TestStruct{Name: "TestStruct"}
cases := []interface{}{ cases := []interface{}{
"", nil,
int(0), int8(1), int16(-1), int32(123), int64(123), int(0), int8(1), int16(-1), int32(123), int64(123),
uint(123), uint8(123), uint16(123), uint32(123), uint64(123), uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
float64(12.3), float32(12.3), float64(12.3), float32(12.3),
true, false, true, false,
[]int{1, 2, 3}, aMap, aStruct, []byte{104, 101, 108, 108, 111}} []int{1, 2, 3}, aMap, aStruct, []byte{104, 101, 108, 108, 111}}
expected := []string{"0", "1", "-1", "123", "123", "123", "123", "123", expected := []string{
"123", "123", "12.3", "12.300000190734863", "true", "false", "", "",
"0", "1", "-1",
"123", "123", "123", "123", "123", "123", "123",
"12.3", "12.300000190734863",
"true", "false",
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"} "[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := ToString(cases[i]) actual := ToString(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "ToString", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestToJson(t *testing.T) { func TestToJson(t *testing.T) {
// map assert := internal.NewAssert(t, "TestToJson")
aMap := make(map[string]int)
aMap["a"] = 1
aMap["b"] = 2
aMap["c"] = 3
mapJson := "{\"a\":1,\"b\":2,\"c\":3}" var aMap = map[string]int{"a": 1, "b": 2, "c": 3}
r1, _ := ToJson(aMap) mapJsonStr, _ := ToJson(aMap)
if r1 != mapJson { assert.Equal("{\"a\":1,\"b\":2,\"c\":3}", mapJsonStr)
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
t.FailNow()
}
// struct
type TestStruct struct { type TestStruct struct {
Name string Name string
} }
aStruct := TestStruct{Name: "TestStruct"} aStruct := TestStruct{Name: "TestStruct"}
structJson := "{\"Name\":\"TestStruct\"}" structJsonStr, _ := ToJson(aStruct)
r2, _ := ToJson(aStruct) assert.Equal("{\"Name\":\"TestStruct\"}", structJsonStr)
if r2 != structJson {
utils.LogFailedTestInfo(t, "ToJson", aMap, mapJson, r1)
t.FailNow()
}
} }
func TestStructToMap(t *testing.T) { func TestStructToMap(t *testing.T) {
assert := internal.NewAssert(t, "TestStructToMap")
type People struct { type People struct {
Name string `json:"name"` Name string `json:"name"`
age int age int
} }
p := People{
p1 := People{
"test", "test",
100, 100,
} }
pm, _ := StructToMap(p)
pm1, _ := StructToMap(p1) var expected = map[string]interface{}{"name": "test"}
m1 := make(map[string]interface{}) assert.Equal(expected, pm)
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) { func TestColorHexToRGB(t *testing.T) {
@@ -196,22 +157,17 @@ func TestColorHexToRGB(t *testing.T) {
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b) colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
expected := "0,51,102" expected := "0,51,102"
if colorRGB != expected { assert := internal.NewAssert(t, "TestColorHexToRGB")
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorHex, expected, colorRGB) assert.Equal(expected, colorRGB)
t.FailNow()
}
} }
func TestColorRGBToHex(t *testing.T) { func TestColorRGBToHex(t *testing.T) {
r := 0 r := 0
g := 51 g := 51
b := 102 b := 102
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
colorHex := ColorRGBToHex(r, g, b) colorHex := ColorRGBToHex(r, g, b)
expected := "#003366" expected := "#003366"
if colorHex != expected { assert := internal.NewAssert(t, "TestColorRGBToHex")
utils.LogFailedTestInfo(t, "ColorHexToRGB", colorRGB, expected, colorHex) assert.Equal(expected, colorHex)
t.FailNow()
}
} }

View File

@@ -3,7 +3,7 @@ package cryptor
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestAesEcbEncrypt(t *testing.T) { func TestAesEcbEncrypt(t *testing.T) {
@@ -13,10 +13,8 @@ func TestAesEcbEncrypt(t *testing.T) {
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key)) aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key)) aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
if string(aesEcbDecrypt) != data { assert := internal.NewAssert(t, "TestAesEcbEncrypt")
utils.LogFailedTestInfo(t, "AesEcbEncrypt/AesEcbDecrypt", data, data, string(aesEcbDecrypt)) assert.Equal(data, string(aesEcbDecrypt))
t.FailNow()
}
} }
func TestAesCbcEncrypt(t *testing.T) { func TestAesCbcEncrypt(t *testing.T) {
@@ -26,10 +24,8 @@ func TestAesCbcEncrypt(t *testing.T) {
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key)) aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key)) aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
if string(aesCbcDecrypt) != data { assert := internal.NewAssert(t, "TestAesCbcEncrypt")
utils.LogFailedTestInfo(t, "AesCbcEncrypt/AesCbcDecrypt", data, data, string(aesCbcDecrypt)) assert.Equal(data, string(aesCbcDecrypt))
t.FailNow()
}
} }
func TestAesCtrCrypt(t *testing.T) { func TestAesCtrCrypt(t *testing.T) {
@@ -39,10 +35,8 @@ func TestAesCtrCrypt(t *testing.T) {
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key)) aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key)) aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
if string(aesCtrDeCrypt) != data { assert := internal.NewAssert(t, "TestAesCtrCrypt")
utils.LogFailedTestInfo(t, "AesCtrCrypt", data, data, string(aesCtrDeCrypt)) assert.Equal(data, string(aesCtrDeCrypt))
t.FailNow()
}
} }
func TestAesCfbEncrypt(t *testing.T) { func TestAesCfbEncrypt(t *testing.T) {
@@ -52,10 +46,8 @@ func TestAesCfbEncrypt(t *testing.T) {
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key)) aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key)) aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
if string(aesCfbDecrypt) != data { assert := internal.NewAssert(t, "TestAesCfbEncrypt")
utils.LogFailedTestInfo(t, "AesCfbEncrypt/AesCfbDecrypt", data, data, string(aesCfbDecrypt)) assert.Equal(data, string(aesCfbDecrypt))
t.FailNow()
}
} }
func TestAesOfbEncrypt(t *testing.T) { func TestAesOfbEncrypt(t *testing.T) {
@@ -65,8 +57,6 @@ func TestAesOfbEncrypt(t *testing.T) {
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key)) aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key)) aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
if string(aesOfbDecrypt) != data { assert := internal.NewAssert(t, "TestAesOfbEncrypt")
utils.LogFailedTestInfo(t, "AesOfbEncrypt/AesOfbDecrypt", data, data, string(aesOfbDecrypt)) assert.Equal(data, string(aesOfbDecrypt))
t.FailNow()
}
} }

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

@@ -1,66 +1,36 @@
package cryptor package cryptor
import ( import (
"fmt"
"os"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestBase64StdEncode(t *testing.T) { func TestBase64StdEncode(t *testing.T) {
s := "hello world" assert := internal.NewAssert(t, "TestBase64StdEncode")
bs := Base64StdEncode(s) assert.Equal("aGVsbG8gd29ybGQ=", Base64StdEncode("hello world"))
if bs != "aGVsbG8gd29ybGQ=" {
utils.LogFailedTestInfo(t, "Base64StdEncode", s, "aGVsbG8gd29ybGQ=", bs)
t.FailNow()
}
} }
func TestBase64StdDecode(t *testing.T) { func TestBase64StdDecode(t *testing.T) {
bs := "aGVsbG8gd29ybGQ=" assert := internal.NewAssert(t, "TestBase64StdDecode")
s := Base64StdDecode(bs) assert.Equal("hello world", Base64StdDecode("aGVsbG8gd29ybGQ="))
if s != "hello world" {
utils.LogFailedTestInfo(t, "Base64StdDecode", bs, "hello world=", s)
t.FailNow()
}
} }
func TestMd5String(t *testing.T) { func TestMd5String(t *testing.T) {
s := "hello" assert := internal.NewAssert(t, "TestMd5String")
smd5 := Md5String(s) assert.Equal("5d41402abc4b2a76b9719d911017c592", Md5String("hello"))
expected := "5d41402abc4b2a76b9719d911017c592"
if smd5 != expected {
utils.LogFailedTestInfo(t, "Md5String", s, expected, smd5)
t.FailNow()
}
} }
func TestMd5File(t *testing.T) { func TestMd5File(t *testing.T) {
file, _ := os.Create("./hello.txt") fileMd5, err := Md5File("./basic.go")
defer file.Close() assert := internal.NewAssert(t, "TestMd5File")
file.WriteString("hello\n") assert.IsNotNil(fileMd5)
assert.IsNil(err)
fileMd5, err := Md5File("./hello.txt")
if err != nil {
t.FailNow()
}
fmt.Println(fileMd5)
} }
func TestHmacMd5(t *testing.T) { func TestHmacMd5(t *testing.T) {
s := "hello world" assert := internal.NewAssert(t, "TestHmacMd5")
key := "12345" assert.Equal("5f4c9faaff0a1ad3007d9ddc06abe36d", HmacMd5("hello world", "12345"))
hmacMd5 := HmacMd5(s, key)
expected := "5f4c9faaff0a1ad3007d9ddc06abe36d"
if hmacMd5 != expected {
utils.LogFailedTestInfo(t, "HmacMd5", s, expected, hmacMd5)
t.FailNow()
}
} }
func TestHmacSha1(t *testing.T) { func TestHmacSha1(t *testing.T) {
@@ -69,10 +39,8 @@ func TestHmacSha1(t *testing.T) {
hmacSha1 := HmacSha1(s, key) hmacSha1 := HmacSha1(s, key)
expected := "3826f812255d8683f051ee97346d1359234d5dbd" expected := "3826f812255d8683f051ee97346d1359234d5dbd"
if hmacSha1 != expected { assert := internal.NewAssert(t, "TestHmacSha1")
utils.LogFailedTestInfo(t, "HmacSha1", s, expected, hmacSha1) assert.Equal(expected, hmacSha1)
t.FailNow()
}
} }
func TestHmacSha256(t *testing.T) { func TestHmacSha256(t *testing.T) {
@@ -81,10 +49,8 @@ func TestHmacSha256(t *testing.T) {
hmacSha256 := HmacSha256(s, key) hmacSha256 := HmacSha256(s, key)
expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8" expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8"
if hmacSha256 != expected { assert := internal.NewAssert(t, "TestHmacSha256")
utils.LogFailedTestInfo(t, "HmacSha256", s, expected, hmacSha256) assert.Equal(expected, hmacSha256)
t.FailNow()
}
} }
func TestHmacSha512(t *testing.T) { func TestHmacSha512(t *testing.T) {
@@ -93,10 +59,8 @@ func TestHmacSha512(t *testing.T) {
hmacSha512 := HmacSha512(s, key) hmacSha512 := HmacSha512(s, key)
expected := "5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175" expected := "5b1563ac4e9b49c9ada8ccb232588fc4f0c30fd12f756b3a0b95af4985c236ca60925253bae10ce2c6bf9af1c1679b51e5395ff3d2826c0a2c7c0d72225d4175"
if hmacSha512 != expected { assert := internal.NewAssert(t, "TestHmacSha512")
utils.LogFailedTestInfo(t, "HmacSha512", s, expected, hmacSha512) assert.Equal(expected, hmacSha512)
t.FailNow()
}
} }
func TestSha1(t *testing.T) { func TestSha1(t *testing.T) {
@@ -104,10 +68,8 @@ func TestSha1(t *testing.T) {
sha1 := Sha1(s) sha1 := Sha1(s)
expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
if sha1 != expected { assert := internal.NewAssert(t, "TestSha1")
utils.LogFailedTestInfo(t, "Sha1", s, expected, sha1) assert.Equal(expected, sha1)
t.FailNow()
}
} }
func TestSha256(t *testing.T) { func TestSha256(t *testing.T) {
@@ -115,10 +77,8 @@ func TestSha256(t *testing.T) {
sha256 := Sha256(s) sha256 := Sha256(s)
expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
if sha256 != expected { assert := internal.NewAssert(t, "TestSha256")
utils.LogFailedTestInfo(t, "Sha256", s, expected, sha256) assert.Equal(expected, sha256)
t.FailNow()
}
} }
func TestSha512(t *testing.T) { func TestSha512(t *testing.T) {
@@ -126,8 +86,6 @@ func TestSha512(t *testing.T) {
sha512 := Sha512(s) sha512 := Sha512(s)
expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f" expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f"
if sha512 != expected { assert := internal.NewAssert(t, "TestSha512")
utils.LogFailedTestInfo(t, "Sha512", s, expected, sha512) assert.Equal(expected, sha512)
t.FailNow()
}
} }

View File

@@ -3,7 +3,7 @@ package cryptor
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestDesEcbEncrypt(t *testing.T) { func TestDesEcbEncrypt(t *testing.T) {
@@ -13,10 +13,8 @@ func TestDesEcbEncrypt(t *testing.T) {
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key)) desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key)) desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
if string(desEcbDecrypt) != data { assert := internal.NewAssert(t, "TestDesEcbEncrypt")
utils.LogFailedTestInfo(t, "DesEcbEncrypt/DesEcbDecrypt", data, data, string(desEcbDecrypt)) assert.Equal(data, string(desEcbDecrypt))
t.FailNow()
}
} }
func TestDesCbcEncrypt(t *testing.T) { func TestDesCbcEncrypt(t *testing.T) {
@@ -26,10 +24,8 @@ func TestDesCbcEncrypt(t *testing.T) {
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key)) desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key)) desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
if string(desCbcDecrypt) != data { assert := internal.NewAssert(t, "TestDesCbcEncrypt")
utils.LogFailedTestInfo(t, "DesCbcEncrypt/DesCbcDecrypt", data, data, string(desCbcDecrypt)) assert.Equal(data, string(desCbcDecrypt))
t.FailNow()
}
} }
func TestDesCtrCrypt(t *testing.T) { func TestDesCtrCrypt(t *testing.T) {
@@ -39,10 +35,8 @@ func TestDesCtrCrypt(t *testing.T) {
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key)) desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key)) desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
if string(desCtrDeCrypt) != data { assert := internal.NewAssert(t, "TestDesCtrCrypt")
utils.LogFailedTestInfo(t, "DesCtrCrypt", data, data, string(desCtrDeCrypt)) assert.Equal(data, string(desCtrDeCrypt))
t.FailNow()
}
} }
func TestDesCfbEncrypt(t *testing.T) { func TestDesCfbEncrypt(t *testing.T) {
@@ -52,10 +46,8 @@ func TestDesCfbEncrypt(t *testing.T) {
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key)) desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key)) desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
if string(desCfbDecrypt) != data { assert := internal.NewAssert(t, "TestDesCfbEncrypt")
utils.LogFailedTestInfo(t, "DesCfbEncrypt/DesCfbDecrypt", data, data, string(desCfbDecrypt)) assert.Equal(data, string(desCfbDecrypt))
t.FailNow()
}
} }
func TestDesOfbEncrypt(t *testing.T) { func TestDesOfbEncrypt(t *testing.T) {
@@ -65,8 +57,6 @@ func TestDesOfbEncrypt(t *testing.T) {
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key)) desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key)) desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
if string(desOfbDecrypt) != data { assert := internal.NewAssert(t, "TestDesOfbEncrypt")
utils.LogFailedTestInfo(t, "DesOfbEncrypt/DesOfbDecrypt", data, data, string(desOfbDecrypt)) assert.Equal(data, string(desOfbDecrypt))
t.FailNow()
}
} }

View File

@@ -3,7 +3,7 @@ package cryptor
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestRsaEncrypt(t *testing.T) { func TestRsaEncrypt(t *testing.T) {
@@ -12,8 +12,6 @@ func TestRsaEncrypt(t *testing.T) {
encrypted := RsaEncrypt(data, "rsa_public.pem") encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem") decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
if string(data) != string(decrypted) { assert := internal.NewAssert(t, "TestRsaEncrypt")
utils.LogFailedTestInfo(t, "RsaEncrypt/RsaDecrypt", string(data), string(data), string(decrypted)) assert.Equal(string(data), string(decrypted))
t.FailNow()
}
} }

View File

@@ -25,7 +25,7 @@
package datetime package datetime
import ( import (
"strconv" "fmt"
"time" "time"
) )
@@ -56,24 +56,17 @@ func init() {
// AddMinute add or sub minute to the time // AddMinute add or sub minute to the time
func AddMinute(t time.Time, minute int64) time.Time { func AddMinute(t time.Time, minute int64) time.Time {
s := strconv.FormatInt(minute, 10) return t.Add(time.Minute * time.Duration(minute))
m, _ := time.ParseDuration(s + "m")
return t.Add(m)
} }
// AddHour add or sub hour to the time // AddHour add or sub hour to the time
func AddHour(t time.Time, hour int64) time.Time { func AddHour(t time.Time, hour int64) time.Time {
s := strconv.FormatInt(hour, 10) return t.Add(time.Hour * time.Duration(hour))
h, _ := time.ParseDuration(s + "h")
return t.Add(h)
} }
// AddDay add or sub day to the time // AddDay add or sub day to the time
func AddDay(t time.Time, day int64) time.Time { func AddDay(t time.Time, day int64) time.Time {
dayHours := day * 24 return t.Add(24 * time.Hour * time.Duration(day))
d := strconv.FormatInt(dayHours, 10)
h, _ := time.ParseDuration(d + "h")
return t.Add(h)
} }
// GetNowDate return format yyyy-mm-dd of current date // GetNowDate return format yyyy-mm-dd of current date
@@ -109,7 +102,11 @@ func FormatTimeToStr(t time.Time, format string) string {
} }
// FormatStrToTime convert string to time // FormatStrToTime convert string to time
func FormatStrToTime(str, format string) time.Time { func FormatStrToTime(str, format string) (time.Time, error) {
t, _ := time.Parse(timeFormat[format], str) v, ok := timeFormat[format]
return t if !ok {
return time.Time{}, fmt.Errorf("format %s not found", format)
}
return time.Parse(v, str)
} }

View File

@@ -1,90 +1,67 @@
package datetime package datetime
import ( import (
"github.com/duke-git/lancet/utils"
"testing" "testing"
"time" "time"
"github.com/duke-git/lancet/internal"
) )
func TestAddDay(t *testing.T) { func TestAddDay(t *testing.T) {
now := time.Now() assert := internal.NewAssert(t, "TestAddDay")
now := time.Now()
after2Days := AddDay(now, 2) after2Days := AddDay(now, 2)
diff1 := after2Days.Sub(now) diff1 := after2Days.Sub(now)
if diff1.Hours() != 48 { assert.Equal(float64(48), diff1.Hours())
utils.LogFailedTestInfo(t, "AddDay", now, 48, diff1.Hours())
t.FailNow()
}
before2Days := AddDay(now, -2) before2Days := AddDay(now, -2)
diff2 := before2Days.Sub(now) diff2 := before2Days.Sub(now)
if diff2.Hours() != -48 { assert.Equal(float64(-48), diff2.Hours())
utils.LogFailedTestInfo(t, "AddDay", now, -48, diff2.Hours())
t.FailNow()
}
} }
func TestAddHour(t *testing.T) {
now := time.Now()
func TestAddHour(t *testing.T) {
assert := internal.NewAssert(t, "TestAddHour")
now := time.Now()
after2Hours := AddHour(now, 2) after2Hours := AddHour(now, 2)
diff1 := after2Hours.Sub(now) diff1 := after2Hours.Sub(now)
if diff1.Hours() != 2 { assert.Equal(float64(2), diff1.Hours())
utils.LogFailedTestInfo(t, "AddHour", now, 2, diff1.Hours())
t.FailNow()
}
before2Hours := AddHour(now, -2) before2Hours := AddHour(now, -2)
diff2 := before2Hours.Sub(now) diff2 := before2Hours.Sub(now)
if diff2.Hours() != -2 { assert.Equal(float64(-2), diff2.Hours())
utils.LogFailedTestInfo(t, "AddHour", now, -2, diff2.Hours())
t.FailNow()
}
} }
func TestAddMinute(t *testing.T) { func TestAddMinute(t *testing.T) {
now := time.Now() assert := internal.NewAssert(t, "TestAddMinute")
now := time.Now()
after2Minutes := AddMinute(now, 2) after2Minutes := AddMinute(now, 2)
diff1 := after2Minutes.Sub(now) diff1 := after2Minutes.Sub(now)
if diff1.Minutes() != 2 { assert.Equal(float64(2), diff1.Minutes())
utils.LogFailedTestInfo(t, "AddMinute", now, 2, diff1.Minutes())
t.FailNow()
}
before2Minutes := AddMinute(now, -2) before2Minutes := AddMinute(now, -2)
diff2 := before2Minutes.Sub(now) diff2 := before2Minutes.Sub(now)
if diff2.Minutes() != -2 { assert.Equal(float64(-2), diff2.Minutes())
utils.LogFailedTestInfo(t, "AddMinute", now, -2, diff2.Minutes())
t.FailNow()
}
} }
func TestGetNowDate(t *testing.T) { func TestGetNowDate(t *testing.T) {
date := GetNowDate() assert := internal.NewAssert(t, "TestGetNowDate")
expected := time.Now().Format("2006-01-02") expected := time.Now().Format("2006-01-02")
if date != expected { assert.Equal(expected, GetNowDate())
utils.LogFailedTestInfo(t, "GetNowDate", "", expected, date)
t.FailNow()
}
} }
func TestGetNotTime(t *testing.T) { func TestGetNotTime(t *testing.T) {
ts := GetNowTime() assert := internal.NewAssert(t, "TestGetNotTime")
expected := time.Now().Format("15:04:05") expected := time.Now().Format("15:04:05")
if ts != expected { assert.Equal(expected, GetNowTime())
utils.LogFailedTestInfo(t, "GetNowTime", "", expected, ts)
t.FailNow()
}
} }
func TestGetNowDateTime(t *testing.T) { func TestGetNowDateTime(t *testing.T) {
ts := GetNowDateTime() assert := internal.NewAssert(t, "TestGetNowDateTime")
expected := time.Now().Format("2006-01-02 15:04:05") expected := time.Now().Format("2006-01-02 15:04:05")
if ts != expected { assert.Equal(expected, GetNowDateTime())
utils.LogFailedTestInfo(t, "GetNowDateTime", "", expected, ts)
t.FailNow()
}
} }
//todo //todo
@@ -98,6 +75,8 @@ func TestGetNowDateTime(t *testing.T) {
//} //}
func TestFormatTimeToStr(t *testing.T) { func TestFormatTimeToStr(t *testing.T) {
assert := internal.NewAssert(t, "TestFormatTimeToStr")
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
cases := []string{ cases := []string{
"yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd", "yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd",
@@ -110,16 +89,15 @@ func TestFormatTimeToStr(t *testing.T) {
"16:04:08", "2021/01"} "16:04:08", "2021/01"}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := FormatTimeToStr(datetime, cases[i]) actual := FormatTimeToStr(datetime, cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected[i], res)
t.FailNow()
}
}
}
} }
func TestFormatStrToTime(t *testing.T) { func TestFormatStrToTime(t *testing.T) {
assert := internal.NewAssert(t, "TestFormatStrToTime")
formats := []string{ formats := []string{
"2006-01-02 15:04:05", "2006-01-02", "2006-01-02 15:04:05", "2006-01-02",
"02-01-06 15:04:05", "2006/01/02 15:04:05", "02-01-06 15:04:05", "2006/01/02 15:04:05",
@@ -135,11 +113,11 @@ func TestFormatStrToTime(t *testing.T) {
"2021/01"} "2021/01"}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := FormatStrToTime(datetimeStr[i], cases[i]) actual, err := FormatStrToTime(datetimeStr[i], cases[i])
expected, _ := time.Parse(formats[i], datetimeStr[i]) if err != nil {
if res != expected { t.Fatal(err)
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)
t.FailNow()
} }
expected, _ := time.Parse(formats[i], datetimeStr[i])
assert.Equal(expected, actual)
} }
} }

View File

@@ -5,11 +5,16 @@
package fileutil package fileutil
import ( import (
"archive/zip"
"bufio" "bufio"
"errors" "errors"
"io" "io"
"io/fs"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath"
"strings"
) )
// IsExist checks if a file or directory exists // IsExist checks if a file or directory exists
@@ -148,3 +153,146 @@ func ListFileNames(path string) ([]string, error) {
return res, nil return res, nil
} }
// Zip create zip file, fpath could be a single file or a directory
func Zip(fpath string, destPath string) error {
zipFile, err := os.Create(destPath)
if err != nil {
return err
}
defer zipFile.Close()
archive := zip.NewWriter(zipFile)
defer archive.Close()
filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/")
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
})
return nil
}
// UnZip unzip the file and save it to destPath
func UnZip(zipFile string, destPath string) error {
zipReader, err := zip.OpenReader(zipFile)
if err != nil {
return err
}
defer zipReader.Close()
for _, f := range zipReader.File {
path := filepath.Join(destPath, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
} else {
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return err
}
inFile, err := f.Open()
if err != nil {
return err
}
defer inFile.Close()
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer outFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return err
}
}
}
return nil
}
// IsLink checks if a file is symbol link or not
func IsLink(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.Mode()&os.ModeSymlink != 0
}
// FileMode return file's mode and permission
func FileMode(path string) (fs.FileMode, error) {
fi, err := os.Lstat(path)
if err != nil {
return 0, err
}
return fi.Mode(), nil
}
// MiMeType return file mime type
// param `file` should be string(file path) or *os.File
func MiMeType(file interface{}) string {
var mediatype string
readBuffer := func(f *os.File) ([]byte, error) {
buffer := make([]byte, 512)
_, err := f.Read(buffer)
if err != nil {
return nil, err
}
return buffer, nil
}
if filePath, ok := file.(string); ok {
f, err := os.Open(filePath)
if err != nil {
return mediatype
}
buffer, err := readBuffer(f)
if err != nil {
return mediatype
}
return http.DetectContentType(buffer)
}
if f, ok := file.(*os.File); ok {
buffer, err := readBuffer(f)
if err != nil {
return mediatype
}
return http.DetectContentType(buffer)
}
return mediatype
}

View File

@@ -2,137 +2,201 @@ package fileutil
import ( import (
"os" "os"
"reflect"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestIsExist(t *testing.T) { func TestIsExist(t *testing.T) {
assert := internal.NewAssert(t, "TestIsExist")
cases := []string{"./", "./file.go", "./a.txt"} cases := []string{"./", "./file.go", "./a.txt"}
expected := []bool{true, true, false} expected := []bool{true, true, false}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := IsExist(cases[i]) actual := IsExist(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "IsExist", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestCreateFile(t *testing.T) { func TestCreateFile(t *testing.T) {
assert := internal.NewAssert(t, "TestCreateFile")
f := "./text.txt" f := "./text.txt"
if CreateFile(f) { if CreateFile(f) {
file, err := os.Open(f) file, err := os.Open(f)
if err != nil { assert.IsNil(err)
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error: "+err.Error()) assert.Equal(f, file.Name())
t.FailNow()
}
if file.Name() != f {
utils.LogFailedTestInfo(t, "CreateFile", f, f, file.Name())
t.FailNow()
}
} else { } else {
utils.LogFailedTestInfo(t, "CreateFile", f, f, "create file error")
t.FailNow() t.FailNow()
} }
os.Remove(f)
} }
func TestIsDir(t *testing.T) { func TestIsDir(t *testing.T) {
assert := internal.NewAssert(t, "TestIsDir")
cases := []string{"./", "./a.txt"} cases := []string{"./", "./a.txt"}
expected := []bool{true, false} expected := []bool{true, false}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := IsDir(cases[i]) actual := IsDir(cases[i])
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "IsDir", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestRemoveFile(t *testing.T) { func TestRemoveFile(t *testing.T) {
assert := internal.NewAssert(t, "TestRemoveFile")
f := "./text.txt" f := "./text.txt"
if CreateFile(f) { if !IsExist(f) {
CreateFile(f)
err := RemoveFile(f) err := RemoveFile(f)
if err != nil { assert.IsNil(err)
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) { func TestCopyFile(t *testing.T) {
assert := internal.NewAssert(t, "TestCopyFile")
srcFile := "./text.txt" srcFile := "./text.txt"
CreateFile(srcFile) CreateFile(srcFile)
dstFile := "./text_copy.txt" destFile := "./text_copy.txt"
err := CopyFile(srcFile, dstFile) err := CopyFile(srcFile, destFile)
if err != nil { if err != nil {
file, err := os.Open(dstFile) file, err := os.Open(destFile)
if err != nil { assert.IsNil(err)
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, "create file error: "+err.Error()) assert.Equal(destFile, file.Name())
t.FailNow()
}
if file.Name() != dstFile {
utils.LogFailedTestInfo(t, "CopyFile", srcFile, dstFile, file.Name())
t.FailNow()
}
} }
os.Remove(srcFile)
os.Remove(destFile)
} }
func TestListFileNames(t *testing.T) { func TestListFileNames(t *testing.T) {
filesInCurrentPath, err := ListFileNames("../datetime/") assert := internal.NewAssert(t, "TestListFileNames")
if err != nil {
t.FailNow() filesInPath, err := ListFileNames("../datetime/")
} assert.IsNil(err)
expected := []string{"datetime.go", "datetime_test.go"} expected := []string{"datetime.go", "datetime_test.go"}
if !reflect.DeepEqual(filesInCurrentPath, expected) { assert.Equal(expected, filesInPath)
utils.LogFailedTestInfo(t, "ToChar", "./", expected, filesInCurrentPath)
t.FailNow()
}
} }
func TestReadFileToString(t *testing.T) { func TestReadFileToString(t *testing.T) {
assert := internal.NewAssert(t, "TestReadFileToString")
path := "./text.txt" path := "./text.txt"
CreateFile(path) CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
f.WriteString("hello world") f.WriteString("hello world")
res, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
if res != "hello world" { assert.Equal("hello world", content)
utils.LogFailedTestInfo(t, "ReadFileToString", path, "hello world", res)
} os.Remove(path)
} }
func TestClearFile(t *testing.T) { func TestClearFile(t *testing.T) {
assert := internal.NewAssert(t, "TestClearFile")
path := "./text.txt" path := "./text.txt"
CreateFile(path) CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
f.WriteString("hello world") f.WriteString("hello world")
CreateFile(path) err := ClearFile(path)
assert.IsNil(err)
res, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
if res != "" { assert.Equal("", content)
utils.LogFailedTestInfo(t, "CreateFile", path, "", res)
} os.Remove(path)
} }
func TestReadFileByLine(t *testing.T) { func TestReadFileByLine(t *testing.T) {
assert := internal.NewAssert(t, "TestReadFileByLine")
path := "./text.txt" path := "./text.txt"
CreateFile(path) CreateFile(path)
f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
f.WriteString("hello\nworld") f.WriteString("hello\nworld")
expected := []string{"hello", "world"} expected := []string{"hello", "world"}
res, _ := ReadFileByLine(path) actual, _ := ReadFileByLine(path)
if !reflect.DeepEqual(res, expected) { assert.Equal(expected, actual)
utils.LogFailedTestInfo(t, "ReadFileByLine", path, expected, res)
} os.Remove(path)
}
func TestZipAndUnZip(t *testing.T) {
assert := internal.NewAssert(t, "TestZipAndUnZip")
srcFile := "./text.txt"
CreateFile(srcFile)
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
defer file.Close()
file.WriteString("hello\nworld")
zipFile := "./text.zip"
err := Zip(srcFile, zipFile)
assert.IsNil(err)
unZipPath := "./unzip"
err = UnZip(zipFile, unZipPath)
assert.IsNil(err)
unZipFile := "./unzip/text.txt"
assert.Equal(true, IsExist(unZipFile))
os.Remove(srcFile)
os.Remove(zipFile)
os.RemoveAll(unZipPath)
}
func TestFileMode(t *testing.T) {
assert := internal.NewAssert(t, "TestFileMode")
srcFile := "./text.txt"
CreateFile(srcFile)
mode, err := FileMode(srcFile)
assert.IsNil(err)
t.Log(mode)
os.Remove(srcFile)
}
func TestIsLink(t *testing.T) {
assert := internal.NewAssert(t, "TestIsLink")
srcFile := "./text.txt"
CreateFile(srcFile)
linkFile := "./text.link"
if !IsExist(linkFile) {
_ = os.Symlink(srcFile, linkFile)
}
assert.Equal(true, IsLink(linkFile))
assert.Equal(false, IsLink("./file.go"))
os.Remove(srcFile)
os.Remove(linkFile)
}
func TestMiMeType(t *testing.T) {
assert := internal.NewAssert(t, "TestMiMeType")
f, _ := os.Open("./file.go")
assert.Equal("text/plain; charset=utf-8", MiMeType(f))
assert.Equal("text/plain; charset=utf-8", MiMeType("./file.go"))
} }

View File

@@ -3,26 +3,21 @@ package formatter
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestComma(t *testing.T) { func TestComma(t *testing.T) {
comma(t, "", "", "") assert := internal.NewAssert(t, "TestComma")
comma(t, "aa", "", "")
comma(t, "aa.a", "", "")
comma(t, []int{1}, "", "")
comma(t, "123", "", "123")
comma(t, "12345", "", "12,345")
comma(t, 12345, "", "12,345")
comma(t, 12345, "$", "$12,345")
comma(t, 12345, "¥", "¥12,345")
comma(t, 12345.6789, "", "12,345.6789")
}
func comma(t *testing.T, test interface{}, symbol string, expected interface{}) { assert.Equal("", Comma("", ""))
res := Comma(test, symbol) assert.Equal("", Comma("aa", ""))
if res != expected { assert.Equal("", Comma("aa.a", ""))
utils.LogFailedTestInfo(t, "Comma", test, expected, res) assert.Equal("", Comma([]int{1}, ""))
t.FailNow() assert.Equal("123", Comma("123", ""))
} assert.Equal("12,345", Comma("12345", ""))
assert.Equal("12,345", Comma(12345, ""))
assert.Equal("$12,345", Comma(12345, "$"))
assert.Equal("¥12,345", Comma(12345, "¥"))
assert.Equal("12,345.6789", Comma(12345.6789, ""))
} }

View File

@@ -11,10 +11,12 @@ import (
// After creates a function that invokes func once it's called n or more times // After creates a function that invokes func once it's called n or more times
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
mustBeFunction(fn)
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
n-- n--
if n < 1 { if n < 1 {
return invokeFunc(fn, args...) return unsafeInvokeFunc(fn, args...)
} }
return nil return nil
} }
@@ -22,11 +24,12 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Before creates a function that invokes func once it's called less than n times // Before creates a function that invokes func once it's called less than n times
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
mustBeFunction(fn)
var res []reflect.Value var res []reflect.Value
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
if n > 0 { if n > 0 {
res = invokeFunc(fn, args...) res = unsafeInvokeFunc(fn, args...)
} }
if n <= 0 { if n <= 0 {
fn = nil fn = nil
@@ -67,13 +70,31 @@ func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
invokeFunc(fn, args...) invokeFunc(fn, args...)
} }
// Schedule invoke function every duration time, util close the returned bool chan // 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 Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { func Debounced(fn func(), duration time.Duration) func() {
quit := make(chan bool) timer := time.NewTimer(duration)
timer.Stop()
go func() { go func() {
for { for {
invokeFunc(fn, args...) select {
case <-timer.C:
go fn()
}
}
}()
return func() { timer.Reset(duration) }
}
// Schedule invoke function every duration time, util close the returned bool chan
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
// Catch programming error while constructing the closure
mustBeFunction(fn)
quit := make(chan bool)
go func() {
for {
unsafeInvokeFunc(fn, args...)
select { select {
case <-time.After(d): case <-time.After(d):
case <-quit: case <-quit:

View File

@@ -6,6 +6,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/duke-git/lancet/internal"
) )
func TestAfter(t *testing.T) { func TestAfter(t *testing.T) {
@@ -32,6 +34,8 @@ func TestAfter(t *testing.T) {
} }
func TestBefore(t *testing.T) { func TestBefore(t *testing.T) {
assert := internal.NewAssert(t, "TestBefore")
arr := []string{"a", "b", "c", "d", "e"} arr := []string{"a", "b", "c", "d", "e"}
f := Before(3, func(i int) int { f := Before(3, func(i int) int {
return i return i
@@ -40,7 +44,6 @@ func TestBefore(t *testing.T) {
var res []int64 var res []int64
type cb func(args ...interface{}) []reflect.Value type cb func(args ...interface{}) []reflect.Value
appendStr := func(i int, s string, fn cb) { appendStr := func(i int, s string, fn cb) {
fmt.Printf("appendStr: arr[%d] is %s \n", i, s)
v := fn(i) v := fn(i)
res = append(res, v[0].Int()) res = append(res, v[0].Int())
} }
@@ -49,28 +52,26 @@ func TestBefore(t *testing.T) {
appendStr(i, arr[i], f) appendStr(i, arr[i], f)
} }
expect := []int64{0, 1, 2, 2, 2} expected := []int64{0, 1, 2, 2, 2}
if !reflect.DeepEqual(expect, res) { assert.Equal(expected, res)
t.FailNow()
}
} }
func TestCurry(t *testing.T) { func TestCurry(t *testing.T) {
assert := internal.NewAssert(t, "TestCurry")
add := func(a, b int) int { add := func(a, b int) int {
return a + b return a + b
} }
var addCurry Fn = func(values ...interface{}) interface{} { var addCurry Fn = func(values ...interface{}) interface{} {
return add(values[0].(int), values[1].(int)) return add(values[0].(int), values[1].(int))
} }
add1 := addCurry.Curry(1) add1 := addCurry.Curry(1)
v := add1(2) assert.Equal(3, add1(2))
if v != 3 {
t.FailNow()
}
} }
func TestCompose(t *testing.T) { func TestCompose(t *testing.T) {
assert := internal.NewAssert(t, "TestCompose")
toUpper := func(a ...interface{}) interface{} { toUpper := func(a ...interface{}) interface{} {
return strings.ToUpper(a[0].(string)) return strings.ToUpper(a[0].(string))
} }
@@ -78,27 +79,47 @@ func TestCompose(t *testing.T) {
return strings.ToLower(a[0].(string)) return strings.ToLower(a[0].(string))
} }
expect := toUpper(toLower("aBCde")) expected := toUpper(toLower("aBCde"))
cf := Compose(toUpper, toLower) cf := Compose(toUpper, toLower)
res := cf("aBCde") res := cf("aBCde")
if res != expect { assert.Equal(expected, res)
t.FailNow()
}
} }
func TestDelay(t *testing.T) { func TestDelay(t *testing.T) {
var print = func(s string) { var print = func(s string) {
fmt.Println(s) t.Log(s)
} }
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")
var res []string var res []string
appendStr := func(s string) { appendStr := func(s string) {
fmt.Println(s)
res = append(res, s) res = append(res, s)
} }
@@ -106,9 +127,6 @@ func TestSchedule(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
close(stop) close(stop)
expect := []string{"*", "*", "*", "*", "*"} expected := []string{"*", "*", "*", "*", "*"}
if !reflect.DeepEqual(expect, res) { assert.Equal(expected, res)
t.FailNow()
}
fmt.Println("done")
} }

View File

@@ -14,6 +14,15 @@ func invokeFunc(fn interface{}, args ...interface{}) []reflect.Value {
return fv.Call(params) return fv.Call(params)
} }
func unsafeInvokeFunc(fn interface{}, args ...interface{}) []reflect.Value {
fv := reflect.ValueOf(fn)
params := make([]reflect.Value, len(args))
for i, item := range args {
params[i] = reflect.ValueOf(item)
}
return fv.Call(params)
}
func functionValue(function interface{}) reflect.Value { func functionValue(function interface{}) reflect.Value {
v := reflect.ValueOf(function) v := reflect.ValueOf(function)
if v.Kind() != reflect.Func { if v.Kind() != reflect.Func {
@@ -21,3 +30,10 @@ func functionValue(function interface{}) reflect.Value {
} }
return v return v
} }
func mustBeFunction(function interface{}) {
v := reflect.ValueOf(function)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("Invalid function type, value of type %T", function))
}
}

37
function/watcher.go Normal file
View File

@@ -0,0 +1,37 @@
package function
import "time"
// Watcher is used for record code excution time
type Watcher struct {
startTime int64
stopTime int64
excuting bool
}
// Start the watch timer.
func (w *Watcher) Start() {
w.startTime = time.Now().UnixNano()
w.excuting = true
}
// Stop the watch timer.
func (w *Watcher) Stop() {
w.stopTime = time.Now().UnixNano()
w.excuting = false
}
// GetElapsedTime get excute elapsed time.
func (w *Watcher) GetElapsedTime() time.Duration {
if w.excuting {
return time.Duration(time.Now().UnixNano() - w.startTime)
}
return time.Duration(w.stopTime - w.startTime)
}
// Reset the watch timer.
func (w *Watcher) Reset() {
w.startTime = 0
w.stopTime = 0
w.excuting = false
}

37
function/watcher_test.go Normal file
View File

@@ -0,0 +1,37 @@
package function
import (
"testing"
"github.com/duke-git/lancet/internal"
)
func TestWatcher(t *testing.T) {
assert := internal.NewAssert(t, "TestWatcher")
w := &Watcher{}
w.Start()
longRunningTask()
assert.Equal(true, w.excuting)
w.Stop()
eapsedTime := w.GetElapsedTime().Milliseconds()
t.Log("Elapsed Time (milsecond)", eapsedTime)
assert.Equal(false, w.excuting)
w.Reset()
assert.Equal(int64(0), w.startTime)
assert.Equal(int64(0), w.stopTime)
}
func longRunningTask() {
var slice []int64
for i := 0; i < 10000000; i++ {
slice = append(slice, int64(i))
}
}

171
internal/assert.go Normal file
View File

@@ -0,0 +1,171 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package internal is for internal use.
package internal
import (
"fmt"
"reflect"
"runtime"
"testing"
)
const (
compareNotEqual int = iota - 2
compareLess
compareEqual
compareGreater
)
// Assert is a simple implementation of assertion, only for internal usage
type Assert struct {
T *testing.T
CaseName string
}
// NewAssert return instance of Assert
func NewAssert(t *testing.T, caseName string) *Assert {
return &Assert{T: t, CaseName: caseName}
}
// Equal check if expected is equal with actual
func (a *Assert) Equal(expected, actual interface{}) {
if compare(expected, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, expected, actual)
}
}
// NotEqual check if expected is not equal with actual
func (a *Assert) NotEqual(expected, actual interface{}) {
if compare(expected, actual) == compareEqual {
expectedInfo := fmt.Sprintf("not %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}
// Greater check if expected is greate than actual
func (a *Assert) Greater(expected, actual interface{}) {
if compare(expected, actual) != compareGreater {
expectedInfo := fmt.Sprintf("> %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}
// GreaterOrEqual check if expected is greate than or equal with actual
func (a *Assert) GreaterOrEqual(expected, actual interface{}) {
isGreatOrEqual := compare(expected, actual) == compareGreater || compare(expected, actual) == compareEqual
if !isGreatOrEqual {
expectedInfo := fmt.Sprintf(">= %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}
// Less check if expected is less than actual
func (a *Assert) Less(expected, actual interface{}) {
if compare(expected, actual) != compareLess {
expectedInfo := fmt.Sprintf("< %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}
// LessOrEqual check if expected is less than or equal with actual
func (a *Assert) LessOrEqual(expected, actual interface{}) {
isLessOrEqual := compare(expected, actual) == compareLess || compare(expected, actual) == compareEqual
if !isLessOrEqual {
expectedInfo := fmt.Sprintf("<= %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}
// IsNil check if value is nil
func (a *Assert) IsNil(value interface{}) {
if value != nil {
makeTestFailed(a.T, a.CaseName, nil, value)
}
}
// IsNotNil check if value is not nil
func (a *Assert) IsNotNil(value interface{}) {
if value == nil {
makeTestFailed(a.T, a.CaseName, "not nil", value)
}
}
// compare x and y return :
// x > y -> 1, x < y -> -1, x == y -> 0, x != y -> -2
func compare(x, y interface{}) int {
vx := reflect.ValueOf(x)
vy := reflect.ValueOf(y)
if vx.Type() != vy.Type() {
return compareNotEqual
}
switch vx.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
xInt := vx.Int()
yInt := vy.Int()
if xInt > yInt {
return compareGreater
}
if xInt == yInt {
return compareEqual
}
if xInt < yInt {
return compareLess
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
xUint := vx.Uint()
yUint := vy.Uint()
if xUint > yUint {
return compareGreater
}
if xUint == yUint {
return compareEqual
}
if xUint < yUint {
return compareLess
}
case reflect.Float32, reflect.Float64:
xFloat := vx.Float()
yFloat := vy.Float()
if xFloat > yFloat {
return compareGreater
}
if xFloat == yFloat {
return compareEqual
}
if xFloat < yFloat {
return compareLess
}
case reflect.String:
xString := vx.String()
yString := vy.String()
if xString > yString {
return compareGreater
}
if xString == yString {
return compareEqual
}
if xString < yString {
return compareLess
}
default:
if reflect.DeepEqual(x, y) {
return compareEqual
}
return compareNotEqual
}
return compareNotEqual
}
// logFailedInfo make test failed and log error info
func makeTestFailed(t *testing.T, caseName string, expected, actual interface{}) {
_, file, line, _ := runtime.Caller(2)
errInfo := fmt.Sprintf("Case %v failed. file: %v, line: %v, expected: %v, actual: %v.", caseName, file, line, expected, actual)
t.Error(errInfo)
t.FailNow()
}

50
internal/assert_test.go Normal file
View File

@@ -0,0 +1,50 @@
package internal
import (
"testing"
)
func TestAssert(t *testing.T) {
assert := NewAssert(t, "TestAssert")
assert.Equal(0, 0)
assert.NotEqual(1, 0)
assert.NotEqual("1", 1)
var uInt1 uint
var uInt2 uint
var uInt8 uint8
var uInt16 uint16
var uInt32 uint32
var uInt64 uint64
assert.NotEqual(uInt1, uInt8)
assert.NotEqual(uInt8, uInt16)
assert.NotEqual(uInt16, uInt32)
assert.NotEqual(uInt32, uInt64)
assert.Equal(uInt1, uInt2)
uInt1 = 1
uInt2 = 2
assert.Less(uInt1, uInt2)
assert.Greater(1, 0)
assert.GreaterOrEqual(1, 1)
assert.Less(0, 1)
assert.LessOrEqual(0, 0)
assert.Equal(0.1, 0.1)
assert.Greater(1.1, 0.1)
assert.Less(0.1, 1.1)
assert.Equal("abc", "abc")
assert.NotEqual("abc", "abd")
assert.Less("abc", "abd")
assert.Greater("abd", "abc")
assert.Equal([]int{1, 2, 3}, []int{1, 2, 3})
assert.NotEqual([]int{1, 2, 3}, []int{1, 2})
assert.IsNil(nil)
assert.IsNotNil("abc")
}

View File

@@ -1,31 +1,32 @@
package netutil package netutil
import ( import (
"fmt"
"net" "net"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestGetInternalIp(t *testing.T) { func TestGetInternalIp(t *testing.T) {
assert := internal.NewAssert(t, "TestGetInternalIp")
internalIp := GetInternalIp() internalIp := GetInternalIp()
ip := net.ParseIP(internalIp) ip := net.ParseIP(internalIp)
if ip == nil { assert.IsNotNil(ip)
utils.LogFailedTestInfo(t, "GetInternalIp", "GetInternalIp", "", ip)
t.FailNow()
}
} }
func TestGetPublicIpInfo(t *testing.T) { func TestGetPublicIpInfo(t *testing.T) {
assert := internal.NewAssert(t, "TestGetPublicIpInfo")
publicIpInfo, err := GetPublicIpInfo() publicIpInfo, err := GetPublicIpInfo()
if err != nil { assert.IsNil(err)
t.FailNow()
} t.Logf("public ip info is: %+v \n", *publicIpInfo)
fmt.Printf("public ip info is: %+v \n", *publicIpInfo)
} }
func TestIsPublicIP(t *testing.T) { func TestIsPublicIP(t *testing.T) {
assert := internal.NewAssert(t, "TestIsPublicIP")
ips := []net.IP{ ips := []net.IP{
net.ParseIP("127.0.0.1"), net.ParseIP("127.0.0.1"),
net.ParseIP("192.168.0.1"), net.ParseIP("192.168.0.1"),
@@ -37,11 +38,7 @@ func TestIsPublicIP(t *testing.T) {
expected := []bool{false, false, false, false, true} expected := []bool{false, false, false, false, true}
for i := 0; i < len(ips); i++ { for i := 0; i < len(ips); i++ {
res := IsPublicIP(ips[i]) actual := IsPublicIP(ips[i])
assert.Equal(expected[i], actual)
if res != expected[i] {
utils.LogFailedTestInfo(t, "IsPublicIP", ips[i], expected[i], res)
t.FailNow()
}
} }
} }

View File

@@ -22,27 +22,27 @@ import (
//HttpGet send get http request //HttpGet send get http request
func HttpGet(url string, params ...interface{}) (*http.Response, error) { func HttpGet(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodGet, url, params...) return doHttpRequest(http.MethodGet, url, params...)
} }
//HttpPost send post http request //HttpPost send post http request
func HttpPost(url string, params ...interface{}) (*http.Response, error) { func HttpPost(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodPost, url, params...) return doHttpRequest(http.MethodPost, url, params...)
} }
//HttpPut send put http request //HttpPut send put http request
func HttpPut(url string, params ...interface{}) (*http.Response, error) { func HttpPut(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodPut, url, params...) return doHttpRequest(http.MethodPut, url, params...)
} }
//HttpDelete send delete http request //HttpDelete send delete http request
func HttpDelete(url string, params ...interface{}) (*http.Response, error) { func HttpDelete(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodDelete, url, params...) return doHttpRequest(http.MethodDelete, url, params...)
} }
// HttpPatch send patch http request // HttpPatch send patch http request
func HttpPatch(url string, params ...interface{}) (*http.Response, error) { func HttpPatch(url string, params ...interface{}) (*http.Response, error) {
return request(http.MethodPatch, url, params...) return doHttpRequest(http.MethodPatch, url, params...)
} }
// ParseHttpResponse decode http response to specified interface // ParseHttpResponse decode http response to specified interface

View File

@@ -2,123 +2,140 @@ package netutil
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestHttpGet(t *testing.T) { func TestHttpGet(t *testing.T) {
_, e := HttpGet("", nil) url := "https://jsonplaceholder.typicode.com/todos/1"
if e == nil {
t.FailNow()
}
url := "https://gutendex.com/books?"
queryParams := make(map[string]interface{})
queryParams["ids"] = "1"
resp, err := HttpGet(url, nil, queryParams)
if err != nil {
fmt.Println("error: ", err)
//t.FailNow()
}
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body))
}
func TestHttpPost(t *testing.T) {
url := "http://api.postcodes.io/postcodes"
type Postcode struct {
Postcodes []string `json:"postcodes"`
}
postcode := Postcode{[]string{"OX49 5NU"}}
bodyParams, _ := json.Marshal(postcode)
header := map[string]string{ header := map[string]string{
"Content-Type": "application/json", "Content-Type": "application/json",
} }
resp, err := HttpGet(url, header)
if err != nil {
log.Fatal(err)
t.FailNow()
}
body, _ := ioutil.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
func TestHttpPost(t *testing.T) {
url := "https://jsonplaceholder.typicode.com/todos"
header := map[string]string{
"Content-Type": "application/json",
}
type Todo struct {
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, "TestAddToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPost(url, header, nil, bodyParams) resp, err := HttpPost(url, header, nil, bodyParams)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
func TestHttpPut(t *testing.T) { func TestHttpPut(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users/10420" url := "https://jsonplaceholder.typicode.com/todos/1"
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{ header := map[string]string{
"Content-Type": "application/json", "Content-Type": "application/json",
} }
type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, 1, "TestPutToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPut(url, header, nil, bodyParams) resp, err := HttpPut(url, header, nil, bodyParams)
if err != nil { if err != nil {
fmt.Println("error: ", err) log.Fatal(err)
//t.FailNow() t.FailNow()
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
}
func TestHttpPatch(t *testing.T) {
url := "https://jsonplaceholder.typicode.com/todos/1"
header := map[string]string{
"Content-Type": "application/json",
}
type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, 1, "TestPatchToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPatch(url, header, nil, bodyParams)
if err != nil {
log.Fatal(err)
t.FailNow()
}
body, _ := ioutil.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
} }
func TestHttpDelete(t *testing.T) { func TestHttpDelete(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users/10420" url := "https://jsonplaceholder.typicode.com/todos/1"
resp, err := HttpDelete(url) resp, err := HttpDelete(url)
if err != nil { if err != nil {
fmt.Println("error: ", err) log.Fatal(err)
//t.FailNow() t.FailNow()
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
func TestConvertMapToQueryString(t *testing.T) { func TestConvertMapToQueryString(t *testing.T) {
assert := internal.NewAssert(t, "TestConvertMapToQueryString")
var m = map[string]interface{}{ var m = map[string]interface{}{
"c": 3, "c": 3,
"a": 1, "a": 1,
"b": 2, "b": 2,
} }
assert.Equal("a=1&b=2&c=3", ConvertMapToQueryString(m))
expected := "a=1&b=2&c=3"
r := ConvertMapToQueryString(m)
if r != expected {
utils.LogFailedTestInfo(t, "ConvertMapToQueryString", m, expected, r)
t.FailNow()
}
} }
func TestParseResponse(t *testing.T) { func TestParseResponse(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users" url := "https://jsonplaceholder.typicode.com/todos/1"
type User struct { header := map[string]string{
Id int `json:"id"` "Content-Type": "application/json",
Name string `json:"name"`
Email string `json:"email"`
} }
type UserResp struct {
Data []User `json:"data"` resp, err := HttpGet(url, header)
}
resp, err := HttpGet(url)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
} }
userResp := &UserResp{}
err = ParseHttpResponse(resp, userResp) type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
toDoResp := &Todo{}
err = ParseHttpResponse(resp, toDoResp)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
} }
fmt.Println(userResp.Data) t.Log("response: ", toDoResp)
} }

View File

@@ -10,7 +10,7 @@ import (
"strings" "strings"
) )
func request(method, reqUrl string, params ...interface{}) (*http.Response, error) { func doHttpRequest(method, reqUrl string, params ...interface{}) (*http.Response, error) {
if len(reqUrl) == 0 { if len(reqUrl) == 0 {
return nil, errors.New("url should be specified") return nil, errors.New("url should be specified")
} }
@@ -24,53 +24,29 @@ func request(method, reqUrl string, params ...interface{}) (*http.Response, erro
} }
client := &http.Client{} client := &http.Client{}
err := setUrl(req, reqUrl)
if err != nil {
return nil, err
}
switch len(params) { switch len(params) {
case 0:
err := setUrl(req, reqUrl)
if err != nil {
return nil, err
}
case 1: case 1:
err := setUrl(req, reqUrl)
if err != nil {
return nil, err
}
err = setHeader(req, params[0]) err = setHeader(req, params[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
case 2: case 2:
err := setHeader(req, params[0]) err := setHeaderAndQueryParam(req, reqUrl, params[0], params[1])
if err != nil {
return nil, err
}
err = setQueryParam(req, reqUrl, params[1])
if err != nil { if err != nil {
return nil, err return nil, err
} }
case 3: case 3:
err := setHeader(req, params[0]) err := setHeaderAndQueryAndBody(req, reqUrl, params[0], params[1], params[2])
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 { if err != nil {
return nil, err return nil, err
} }
case 4: case 4:
err := setHeader(req, params[0]) err := setHeaderAndQueryAndBody(req, reqUrl, params[0], params[1], params[2])
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 { if err != nil {
return nil, err return nil, err
} }
@@ -78,13 +54,40 @@ func request(method, reqUrl string, params ...interface{}) (*http.Response, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
resp, e := client.Do(req) resp, e := client.Do(req)
return resp, e return resp, e
} }
func setHeaderAndQueryParam(req *http.Request, reqUrl string, header, queryParam interface{}) error {
err := setHeader(req, header)
if err != nil {
return err
}
err = setQueryParam(req, reqUrl, queryParam)
if err != nil {
return err
}
return nil
}
func setHeaderAndQueryAndBody(req *http.Request, reqUrl string, header, queryParam, body interface{}) error {
err := setHeader(req, header)
if err != nil {
return err
}
err = setQueryParam(req, reqUrl, queryParam)
if err != nil {
return err
}
err = setBodyByte(req, body)
if err != nil {
return err
}
return nil
}
func setHeader(req *http.Request, header interface{}) error { func setHeader(req *http.Request, header interface{}) error {
if header != nil { if header != nil {
switch v := header.(type) { switch v := header.(type) {
@@ -109,6 +112,7 @@ func setHeader(req *http.Request, header interface{}) error {
return nil return nil
} }
func setUrl(req *http.Request, reqUrl string) error { func setUrl(req *http.Request, reqUrl string) error {
u, err := url.Parse(reqUrl) u, err := url.Parse(reqUrl)
if err != nil { if err != nil {
@@ -117,6 +121,7 @@ func setUrl(req *http.Request, reqUrl string) error {
req.URL = u req.URL = u
return nil return nil
} }
func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error { func setQueryParam(req *http.Request, reqUrl string, queryParam interface{}) error {
var values url.Values var values url.Values
if queryParam != nil { if queryParam != nil {

View File

@@ -39,7 +39,7 @@ func RandInt(min, max int) int {
// RandBytes generate random byte slice // RandBytes generate random byte slice
func RandBytes(length int) []byte { func RandBytes(length int) []byte {
if length < 1 { if length < 1 {
return nil return []byte{}
} }
b := make([]byte, length) b := make([]byte, length)

View File

@@ -1,71 +1,49 @@
/*
* @Descripttion:
* @version: v1.0.0
* @Author: dudaodong@kingsoft.com
* @Date: 2021-11-29 11:43:28
* @LastEditors: dudaodong@kingsoft.com
* @LastEditTime: 2021-12-01 18:05:29
*/
package random package random
import ( import (
"fmt"
"reflect" "reflect"
"regexp" "regexp"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestRandString(t *testing.T) { func TestRandString(t *testing.T) {
randStr := RandString(6)
fmt.Println(randStr)
pattern := `^[a-zA-Z]+$` pattern := `^[a-zA-Z]+$`
reg := regexp.MustCompile(pattern) reg := regexp.MustCompile(pattern)
if len(randStr) != 6 || !reg.MatchString(randStr) { randStr := RandString(6)
utils.LogFailedTestInfo(t, "RandString", "RandString(6)", "RandString(6) should be 6 letters ", randStr)
t.FailNow() assert := internal.NewAssert(t, "TestRandString")
} assert.Equal(6, len(randStr))
assert.Equal(true, reg.MatchString(randStr))
} }
func TestRandInt(t *testing.T) { func TestRandInt(t *testing.T) {
res1 := RandInt(1, 10) assert := internal.NewAssert(t, "TestRandInt")
if res1 < 1 || res1 >= 10 {
utils.LogFailedTestInfo(t, "RandInt", "RandInt(1, 10)", "RandInt(1, 10) should between [1, 10) ", res1)
t.FailNow()
}
res2 := RandInt(1, 1) r1 := RandInt(1, 10)
if res2 != 1 { assert.GreaterOrEqual(r1, 1)
utils.LogFailedTestInfo(t, "RandInt", "RandInt(1, 1)", "RandInt(1, 1) should be 1 ", res2) assert.Less(r1, 10)
t.FailNow()
}
res3 := RandInt(10, 1) r2 := RandInt(1, 1)
if res3 < 1 || res3 >= 10 { assert.Equal(1, r2)
utils.LogFailedTestInfo(t, "RandInt", "RandInt(10, 1)", "RandInt(10, 1) should between [1, 10) ", res3)
t.FailNow() r3 := RandInt(10, 1)
} assert.GreaterOrEqual(r1, 1)
assert.Less(r3, 10)
} }
func TestRandBytes(t *testing.T) { func TestRandBytes(t *testing.T) {
assert := internal.NewAssert(t, "TestRandBytes")
randBytes := RandBytes(4) randBytes := RandBytes(4)
if len(randBytes) != 4 { assert.Equal(4, len(randBytes))
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes)
t.FailNow()
}
v := reflect.ValueOf(randBytes) v := reflect.ValueOf(randBytes)
et := v.Type().Elem() elemType := v.Type().Elem()
if v.Kind() != reflect.Slice || et.Kind() != reflect.Uint8 { assert.Equal(reflect.Slice, v.Kind())
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(4)", "RandBytes(4) should return 4 element of []bytes", randBytes) assert.Equal(reflect.Uint8, elemType.Kind())
t.FailNow()
}
randErr := RandBytes(0)
if randErr != nil {
utils.LogFailedTestInfo(t, "RandBytes", "RandBytes(0)", "RandBytes(0) should return nil", randErr)
t.FailNow()
}
assert.Equal([]byte{}, RandBytes(0))
} }

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

@@ -7,16 +7,18 @@ package slice
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"math/rand"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"unsafe"
) )
// Contain check if the value is in the slice or not // Contain check if the value is in the iterable type or not
func Contain(slice interface{}, value interface{}) bool { func Contain(iterableType interface{}, value interface{}) bool {
v := reflect.ValueOf(slice) v := reflect.ValueOf(iterableType)
switch reflect.TypeOf(slice).Kind() {
switch kind := reflect.TypeOf(iterableType).Kind(); kind {
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
if v.Index(i).Interface() == value { if v.Index(i).Interface() == value {
@@ -29,14 +31,44 @@ func Contain(slice interface{}, value interface{}) bool {
return true return true
} }
case reflect.String: case reflect.String:
s := fmt.Sprintf("%v", slice) s := iterableType.(string)
ss := fmt.Sprintf("%v", value) ss, ok := value.(string)
if !ok {
panic("kind mismatch")
}
return strings.Contains(s, ss) return strings.Contains(s, ss)
default:
panic(fmt.Sprintf("kind %s is not support", iterableType))
} }
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{}
@@ -70,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 {
@@ -100,15 +214,37 @@ func Every(slice, function interface{}) bool {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
} }
var indexes []int var currentLength int
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() { if flag.Bool() {
indexes = append(indexes, i) currentLength++
} }
} }
return len(indexes) == sv.Len() return currentLength == sv.Len()
}
// None return true if all the values in the slice mismatch the criteria
// The function signature should be func(index int, value interface{}) bool .
func None(slice, function interface{}) bool {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
var currentLength int
for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if !flag.Bool() {
currentLength++
}
}
return currentLength == sv.Len()
} }
// Some return true if any of the values in the list pass the predicate function. // Some return true if any of the values in the list pass the predicate function.
@@ -144,24 +280,20 @@ func Filter(slice, function interface{}) interface{} {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
} }
var indexes []int res := reflect.MakeSlice(sv.Type(), 0, 0)
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() { if flag.Bool() {
indexes = append(indexes, i) res = reflect.Append(res, sv.Index(i))
} }
} }
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
for i := range indexes {
res.Index(i).Set(sv.Index(indexes[i]))
}
return res.Interface() return res.Interface()
} }
// Find iterates over elements of slice, returning the first one that passes a truth test on function. // Count iterates over elements of slice, returns a count of all matched elements
// The function signature should be func(index int, value interface{}) bool . // The function signature should be func(index int, value interface{}) bool .
func Find(slice, function interface{}) interface{} { func Count(slice, function interface{}) int {
sv := sliceValue(slice) sv := sliceValue(slice)
fn := functionValue(function) fn := functionValue(function)
@@ -170,7 +302,55 @@ func Find(slice, function interface{}) interface{} {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
} }
var index int var counter int
for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() {
counter++
}
}
return counter
}
// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices
// The function signature should be func(index int, value interface{}) bool .
func GroupBy(slice, function interface{}) (interface{}, interface{}) {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
groupB := reflect.MakeSlice(sv.Type(), 0, 0)
groupA := reflect.MakeSlice(sv.Type(), 0, 0)
for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() {
groupA = reflect.Append(groupA, sv.Index(i))
} else {
groupB = reflect.Append(groupB, sv.Index(i))
}
}
return groupA.Interface(), groupB.Interface()
}
// Find iterates over elements of slice, returning the first one that passes a truth test on function.
// The function signature should be func(index int, value interface{}) bool .
func Find(slice, function interface{}) (interface{}, bool) {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
}
index := -1
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() { if flag.Bool() {
@@ -178,7 +358,53 @@ func Find(slice, function interface{}) interface{} {
break break
} }
} }
return sv.Index(index).Interface()
if index == -1 {
var none interface{}
return none, false
}
return sv.Index(index).Interface(), true
}
// FlattenDeep flattens slice recursive
func FlattenDeep(slice interface{}) interface{} {
sv := sliceValue(slice)
st := sliceElemType(sv.Type())
tmp := reflect.MakeSlice(reflect.SliceOf(st), 0, 0)
res := flattenRecursive(sv, tmp)
return res.Interface()
}
func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
for i := 0; i < value.Len(); i++ {
item := value.Index(i)
kind := item.Kind()
if kind == reflect.Slice {
result = flattenRecursive(item, result)
} else {
result = reflect.Append(result, item)
}
}
return result
}
// ForEach iterates over elements of slice and invokes function for each element
// The function signature should be func(index int, value interface{}).
func ForEach(slice, function interface{}) {
sv := sliceValue(slice)
fn := functionValue(function)
elemType := sv.Type().Elem()
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
panic("function param should be of type func(int, " + elemType.String() + ")" + elemType.String())
}
for i := 0; i < sv.Len(); i++ {
fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})
}
} }
// Map creates an slice of values by running each element of `slice` thru `function`. // Map creates an slice of values by running each element of `slice` thru `function`.
@@ -252,52 +478,34 @@ func InterfaceSlice(slice interface{}) []interface{} {
// StringSlice convert param to slice of string. // StringSlice convert param to slice of string.
func StringSlice(slice interface{}) []string { func StringSlice(slice interface{}) []string {
var res []string
v := sliceValue(slice) v := sliceValue(slice)
out := make([]string, v.Len())
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
res = append(res, fmt.Sprint(v.Index(i))) v, ok := v.Index(i).Interface().(string)
if !ok {
panic("invalid element type")
}
out[i] = v
} }
return res return out
} }
// IntSlice convert param to slice of int. // IntSlice convert param to slice of int.
func IntSlice(slice interface{}) ([]int, error) { func IntSlice(slice interface{}) []int {
var res []int
sv := sliceValue(slice) sv := sliceValue(slice)
out := make([]int, sv.Len())
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface() v, ok := sv.Index(i).Interface().(int)
switch v := v.(type) { if !ok {
case int: panic("invalid element type")
res = append(res, v)
default:
return nil, errors.New("InvalidSliceElementType")
} }
out[i] = v
} }
return res, nil 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.
@@ -322,6 +530,37 @@ func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error
return v.Interface(), nil return v.Interface(), nil
} }
// Drop creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0
func Drop(slice interface{}, n int) interface{} {
sv := sliceValue(slice)
if n == 0 {
return slice
}
svLen := sv.Len()
if math.Abs(float64(n)) >= float64(svLen) {
return reflect.MakeSlice(sv.Type(), 0, 0).Interface()
}
if n > 0 {
res := reflect.MakeSlice(sv.Type(), svLen-n, svLen-n)
for i := 0; i < res.Len(); i++ {
res.Index(i).Set(sv.Index(i + n))
}
return res.Interface()
}
res := reflect.MakeSlice(sv.Type(), svLen+n, svLen+n)
for i := 0; i < res.Len(); i++ {
res.Index(i).Set(sv.Index(i))
}
return res.Interface()
}
// InsertByIndex insert the element into slice at index. // InsertByIndex insert the element into slice at index.
// Insert value: s = append(s[:i], append([]T{x}, s[i:]...)...) // Insert value: s = append(s[:i], append([]T{x}, s[i:]...)...)
// Insert slice: a = append(a[:i], append(b, a[i:]...)...) // Insert slice: a = append(a[:i], append(b, a[i:]...)...)
@@ -363,6 +602,7 @@ func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}
if index < 0 || index >= v.Len() { if index < 0 || index >= v.Len() {
return slice, errors.New("InvalidSliceIndex") return slice, errors.New("InvalidSliceIndex")
} }
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) { if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
return slice, errors.New("InvalidValueType") return slice, errors.New("InvalidValueType")
} }
@@ -380,7 +620,7 @@ func Unique(slice interface{}) interface{} {
} }
var temp []interface{} var temp []interface{}
len := 0
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface() v := sv.Index(i).Interface()
skip := true skip := true
@@ -392,12 +632,11 @@ func Unique(slice interface{}) interface{} {
} }
if skip { if skip {
temp = append(temp, v) temp = append(temp, v)
len++
} }
} }
res := reflect.MakeSlice(sv.Type(), len, len) res := reflect.MakeSlice(sv.Type(), len(temp), len(temp))
for i := 0; i < len; i++ { for i := 0; i < len(temp); i++ {
res.Index(i).Set(reflect.ValueOf(temp[i])) res.Index(i).Set(reflect.ValueOf(temp[i]))
} }
return res.Interface() return res.Interface()
@@ -423,21 +662,21 @@ func Union(slices ...interface{}) interface{} {
return nil return nil
} }
// append all slices, then unique it // append all slices, then unique it
var allSlice []interface{} var allSlices []interface{}
len := 0 len := 0
for i := range slices { for i := range slices {
sv := sliceValue(slices[i]) sv := sliceValue(slices[i])
len += sv.Len() len += sv.Len()
for j := 0; j < sv.Len(); j++ { for j := 0; j < sv.Len(); j++ {
v := sv.Index(j).Interface() v := sv.Index(j).Interface()
allSlice = append(allSlice, v) allSlices = append(allSlices, v)
} }
} }
sv := sliceValue(slices[0]) sv := sliceValue(slices[0])
res := reflect.MakeSlice(sv.Type(), len, len) res := reflect.MakeSlice(sv.Type(), len, len)
for i := 0; i < len; i++ { for i := 0; i < len; i++ {
res.Index(i).Set(reflect.ValueOf(allSlice[i])) res.Index(i).Set(reflect.ValueOf(allSlices[i]))
} }
return Unique(res.Interface()) return Unique(res.Interface())
@@ -479,19 +718,32 @@ func Intersection(slices ...interface{}) interface{} {
// ReverseSlice return slice of element order is reversed to the given slice // ReverseSlice return slice of element order is reversed to the given slice
func ReverseSlice(slice interface{}) { func ReverseSlice(slice interface{}) {
v := sliceValue(slice) sv := sliceValue(slice)
swp := reflect.Swapper(v.Interface()) swp := reflect.Swapper(sv.Interface())
for i, j := 0, v.Len()-1; i < j; i, j = i+1, j-1 { for i, j := 0, sv.Len()-1; i < j; i, j = i+1, j-1 {
swp(i, j) swp(i, j)
} }
} }
// Shuffle creates an slice of shuffled values
func Shuffle(slice interface{}) interface{} {
sv := sliceValue(slice)
length := sv.Len()
res := reflect.MakeSlice(sv.Type(), length, length)
for i, v := range rand.Perm(length) {
res.Index(i).Set(sv.Index(v))
}
return res.Interface()
}
// SortByField return sorted slice by field // SortByField return sorted slice by field
// Slice element should be struct, field type should be int, uint, string, or bool // 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 // default sortType is ascending (asc), if descending order, set sortType to desc
func SortByField(slice interface{}, field string, sortType ...string) error { func SortByField(slice interface{}, field string, sortType ...string) error {
v := sliceValue(slice) sv := sliceValue(slice)
t := v.Type().Elem() t := sv.Type().Elem()
if t.Kind() == reflect.Ptr { if t.Kind() == reflect.Ptr {
t = t.Elem() t = t.Elem()
@@ -507,37 +759,54 @@ func SortByField(slice interface{}, field string, sortType ...string) error {
} }
// Create a less function based on the field's kind. // Create a less function based on the field's kind.
var less func(a, b reflect.Value) bool var compare func(a, b reflect.Value) bool
switch sf.Type.Kind() { switch sf.Type.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } if len(sortType) > 0 && sortType[0] == "desc" {
compare = func(a, b reflect.Value) bool { return a.Int() > b.Int() }
} else {
compare = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } if len(sortType) > 0 && sortType[0] == "desc" {
compare = func(a, b reflect.Value) bool { return a.Uint() > b.Uint() }
} else {
compare = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
}
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
less = func(a, b reflect.Value) bool { return a.Float() < b.Float() } if len(sortType) > 0 && sortType[0] == "desc" {
compare = func(a, b reflect.Value) bool { return a.Float() > b.Float() }
} else {
compare = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
}
case reflect.String: case reflect.String:
less = func(a, b reflect.Value) bool { return a.String() < b.String() } if len(sortType) > 0 && sortType[0] == "desc" {
compare = func(a, b reflect.Value) bool { return a.String() > b.String() }
} else {
compare = func(a, b reflect.Value) bool { return a.String() < b.String() }
}
case reflect.Bool: case reflect.Bool:
less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } if len(sortType) > 0 && sortType[0] == "desc" {
compare = func(a, b reflect.Value) bool { return a.Bool() && !b.Bool() }
} else {
compare = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
}
default: default:
return fmt.Errorf("field type %s not supported", sf.Type) return fmt.Errorf("field type %s not supported", sf.Type)
} }
sort.Slice(slice, func(i, j int) bool { sort.Slice(slice, func(i, j int) bool {
a := v.Index(i) a := sv.Index(i)
b := v.Index(j) b := sv.Index(j)
if t.Kind() == reflect.Ptr { if t.Kind() == reflect.Ptr {
a = a.Elem() a = a.Elem()
b = b.Elem() b = b.Elem()
} }
a = a.FieldByIndex(sf.Index) a = a.FieldByIndex(sf.Index)
b = b.FieldByIndex(sf.Index) b = b.FieldByIndex(sf.Index)
return less(a, b) return compare(a, b)
}) })
if sortType[0] == "desc" {
ReverseSlice(slice)
}
return nil return nil
} }
@@ -547,6 +816,7 @@ func Without(slice interface{}, values ...interface{}) interface{} {
if sv.Len() == 0 { if sv.Len() == 0 {
return slice return slice
} }
var indexes []int var indexes []int
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface() v := sv.Index(i).Interface()

View File

@@ -4,96 +4,73 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestContain(t *testing.T) { func TestContain(t *testing.T) {
t1 := []string{"a", "b", "c", "d"} assert := internal.NewAssert(t, "TestContain")
contain(t, t1, "a", true)
contain(t, t1, "e", false)
var t2 []string assert.Equal(true, Contain([]string{"a", "b", "c"}, "a"))
contain(t, t2, "1", false) assert.Equal(false, Contain([]string{"a", "b", "c"}, "d"))
assert.Equal(true, Contain([]string{""}, ""))
assert.Equal(false, Contain([]string{}, ""))
m := make(map[string]int) var m = map[string]int{"a": 1}
m["a"] = 1 assert.Equal(true, Contain(m, "a"))
contain(t, m, "a", true) assert.Equal(false, Contain(m, "b"))
contain(t, m, "b", false)
s := "hello" assert.Equal(true, Contain("abc", "a"))
contain(t, s, "h", true) assert.Equal(false, Contain("abc", "d"))
contain(t, s, "s", false)
} }
func contain(t *testing.T, test interface{}, value interface{}, expected bool) { func TestContainSubSlice(t *testing.T) {
res := Contain(test, value) assert := internal.NewAssert(t, "TestContainSubSlice")
if res != expected { assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
utils.LogFailedTestInfo(t, "Contain", test, expected, res) assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"}))
t.FailNow()
} 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) {
t1 := []string{"a", "b", "c", "d", "e"} assert := internal.NewAssert(t, "TestChunk")
r1 := [][]interface{}{ arr := []string{"a", "b", "c", "d", "e"}
{"a"}, r1 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
{"b"}, assert.Equal(r1, Chunk(InterfaceSlice(arr), 1))
{"c"},
{"d"},
{"e"},
}
chunk(t, InterfaceSlice(t1), 1, r1)
r2 := [][]interface{}{ r2 := [][]interface{}{{"a", "b"}, {"c", "d"}, {"e"}}
{"a", "b"}, assert.Equal(r2, Chunk(InterfaceSlice(arr), 2))
{"c", "d"},
{"e"},
}
chunk(t, InterfaceSlice(t1), 2, r2)
r3 := [][]interface{}{ r3 := [][]interface{}{{"a", "b", "c"}, {"d", "e"}}
{"a", "b", "c"}, assert.Equal(r3, Chunk(InterfaceSlice(arr), 3))
{"d", "e"},
}
chunk(t, InterfaceSlice(t1), 3, r3)
r4 := [][]interface{}{ r4 := [][]interface{}{{"a", "b", "c", "d"}, {"e"}}
{"a", "b", "c", "d"}, assert.Equal(r4, Chunk(InterfaceSlice(arr), 4))
{"e"},
}
chunk(t, InterfaceSlice(t1), 4, r4)
r5 := [][]interface{}{
{"a"},
{"b"},
{"c"},
{"d"},
{"e"},
}
chunk(t, InterfaceSlice(t1), 5, r5)
r5 := [][]interface{}{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
assert.Equal(r5, Chunk(InterfaceSlice(arr), 5))
} }
func chunk(t *testing.T, test []interface{}, num int, expected [][]interface{}) { func TestCompact(t *testing.T) {
res := Chunk(test, num) assert := internal.NewAssert(t, "TesCompact")
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "Chunk", test, expected, res) assert.Equal([]int{}, Compact([]int{0}))
t.FailNow() assert.Equal([]int{1, 2, 3}, Compact([]int{0, 1, 2, 3}))
} assert.Equal([]string{}, Compact([]string{""}))
assert.Equal([]string{" "}, Compact([]string{" "}))
assert.Equal([]string{"a", "b", "0"}, Compact([]string{"", "a", "b", "0"}))
assert.Equal([]bool{true, true}, Compact([]bool{false, true, true}))
} }
func TestConvertSlice(t *testing.T) { func TestConcat(t *testing.T) {
//t1 := []string{"1","2"} assert := internal.NewAssert(t, "Concat")
//aInt, _ := strconv.ParseInt("1", 10, 64)
//bInt, _ := strconv.ParseInt("2", 10, 64) assert.Equal([]int{0}, Concat([]int{}, 0))
//expected :=[]int64{aInt, bInt} 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}))
//a := ConvertSlice(t1, reflect.TypeOf(expected)) assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
//if !reflect.DeepEqual(a, expected) { assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, 5))
// utils.LogFailedTestInfo(t, "ConvertSlice", t1, expected, a)
// t.FailNow()
//}
} }
func TestEvery(t *testing.T) { func TestEvery(t *testing.T) {
@@ -101,42 +78,44 @@ func TestEvery(t *testing.T) {
isEven := func(i, num int) bool { isEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
res := Every(nums, isEven)
if res != false { assert := internal.NewAssert(t, "TestEvery")
utils.LogFailedTestInfo(t, "Every", nums, false, res) assert.Equal(false, Every(nums, isEven))
t.FailNow() }
func TestNone(t *testing.T) {
nums := []int{1, 2, 3, 5}
check := func(i, num int) bool {
return num%2 == 1
} }
assert := internal.NewAssert(t, "TestNone")
assert.Equal(false, None(nums, check))
} }
func TestSome(t *testing.T) { func TestSome(t *testing.T) {
nums := []int{1, 2, 3, 5} nums := []int{1, 2, 3, 5}
isEven := func(i, num int) bool { hasEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
res := Some(nums, isEven)
if res != true { assert := internal.NewAssert(t, "TestSome")
utils.LogFailedTestInfo(t, "Some", nums, true, res) assert.Equal(true, Some(nums, hasEven))
t.FailNow()
}
} }
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
nums := []int{1, 2, 3, 4, 5} nums := []int{1, 2, 3, 4, 5}
even := func(i, num int) bool { isEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
e1 := []int{2, 4}
r1 := Filter(nums, even) assert := internal.NewAssert(t, "TestFilter")
if !reflect.DeepEqual(r1, e1) { assert.Equal([]int{2, 4}, Filter(nums, isEven))
utils.LogFailedTestInfo(t, "Filter", nums, e1, r1)
t.FailNow()
}
type student struct { type student struct {
name string name string
age int age int
} }
students := []student{ students := []student{
{"a", 10}, {"a", 10},
{"b", 11}, {"b", 11},
@@ -144,8 +123,7 @@ func TestFilter(t *testing.T) {
{"d", 13}, {"d", 13},
{"e", 14}, {"e", 14},
} }
studentsOfAageGreat12 := []student{
e2 := []student{
{"d", 13}, {"d", 13},
{"e", 14}, {"e", 14},
} }
@@ -153,12 +131,31 @@ func TestFilter(t *testing.T) {
return s.age > 12 return s.age > 12
} }
r2 := Filter(students, filterFunc) assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc))
if !reflect.DeepEqual(r2, e2) { }
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
t.FailNow() func TestGroupBy(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
evenFunc := func(i, num int) bool {
return (num % 2) == 0
}
expectedEven := []int{2, 4, 6}
expectedOdd := []int{1, 3, 5}
even, odd := GroupBy(nums, evenFunc)
assert := internal.NewAssert(t, "TestGroupBy")
assert.Equal(expectedEven, even)
assert.Equal(expectedOdd, odd)
}
func TestCount(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
evenFunc := func(i, num int) bool {
return (num % 2) == 0
} }
assert := internal.NewAssert(t, "TestCount")
assert.Equal(3, Count(nums, evenFunc))
} }
func TestFind(t *testing.T) { func TestFind(t *testing.T) {
@@ -166,24 +163,57 @@ func TestFind(t *testing.T) {
even := func(i, num int) bool { even := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
res := Find(nums, even) res, ok := Find(nums, even)
if res != 2 { if !ok {
utils.LogFailedTestInfo(t, "Find", nums, 2, res) t.Fatal("found nothing")
t.FailNow()
} }
assert := internal.NewAssert(t, "TestFind")
assert.Equal(2, res)
}
func TestFindFoundNothing(t *testing.T) {
nums := []int{1, 1, 1, 1, 1, 1}
findFunc := func(i, num int) bool {
return num > 1
}
_, ok := Find(nums, findFunc)
// if ok {
// t.Fatal("found something")
// }
assert := internal.NewAssert(t, "TestFindFoundNothing")
assert.Equal(false, ok)
}
func TestFlattenDeep(t *testing.T) {
input := [][][]string{{{"a", "b"}}, {{"c", "d"}}}
expected := []string{"a", "b", "c", "d"}
assert := internal.NewAssert(t, "TestFlattenDeep")
assert.Equal(expected, FlattenDeep(input))
}
func TestForEach(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5}
expected := []int{3, 4, 5, 6, 7}
var numbersAddTwo []int
ForEach(numbers, func(index int, value int) {
numbersAddTwo = append(numbersAddTwo, value+2)
})
assert := internal.NewAssert(t, "TestForEach")
assert.Equal(expected, numbersAddTwo)
} }
func TestMap(t *testing.T) { func TestMap(t *testing.T) {
s1 := []int{1, 2, 3, 4} nums := []int{1, 2, 3, 4}
multiplyTwo := func(i, num int) int { multiplyTwo := func(i, num int) int {
return num * 2 return num * 2
} }
e1 := []int{2, 4, 6, 8}
r1 := Map(s1, multiplyTwo) assert := internal.NewAssert(t, "TestMap")
if !reflect.DeepEqual(r1, e1) { assert.Equal([]int{2, 4, 6, 8}, Map(nums, multiplyTwo))
utils.LogFailedTestInfo(t, "Map", s1, e1, r1)
t.FailNow()
}
type student struct { type student struct {
name string name string
@@ -194,8 +224,7 @@ func TestMap(t *testing.T) {
{"b", 2}, {"b", 2},
{"c", 3}, {"c", 3},
} }
studentsOfAdd10Aage := []student{
e2 := []student{
{"a", 11}, {"a", 11},
{"b", 12}, {"b", 12},
{"c", 13}, {"c", 13},
@@ -204,11 +233,8 @@ func TestMap(t *testing.T) {
s.age += 10 s.age += 10
return s return s
} }
r2 := Map(students, mapFunc)
if !reflect.DeepEqual(r2, e2) { assert.Equal(studentsOfAdd10Aage, Map(students, mapFunc))
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
t.FailNow()
}
} }
func TestReduce(t *testing.T) { func TestReduce(t *testing.T) {
@@ -216,218 +242,154 @@ func TestReduce(t *testing.T) {
{}, {},
{1}, {1},
{1, 2, 3, 4}} {1, 2, 3, 4}}
expected := []int{0, 1, 10} expected := []int{0, 1, 10}
f := func(i, v1, v2 int) int { f := func(i, v1, v2 int) int {
return v1 + v2 return v1 + v2
} }
assert := internal.NewAssert(t, "TestReduce")
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := Reduce(cases[i], f, 0) actual := Reduce(cases[i], f, 0)
if res != expected[i] { assert.Equal(expected[i], actual)
utils.LogFailedTestInfo(t, "Reduce", cases[i], expected[i], res)
t.FailNow()
}
} }
} }
func TestIntSlice(t *testing.T) { func TestIntSlice(t *testing.T) {
var t1 []interface{} var nums []interface{}
t1 = append(t1, 1, 2, 3, 4, 5) nums = append(nums, 1, 2, 3)
expect := []int{1, 2, 3, 4, 5}
intSlice(t, t1, expect)
}
func intSlice(t *testing.T, test interface{}, expected []int) { assert := internal.NewAssert(t, "TestIntSlice")
res, err := IntSlice(test) assert.Equal([]int{1, 2, 3}, IntSlice(nums))
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) { func TestStringSlice(t *testing.T) {
var t1 []interface{} var strs []interface{}
t1 = append(t1, "a", "b", "c", "d", "e") strs = append(strs, "a", "b", "c")
expect := []string{"a", "b", "c", "d", "e"}
stringSlice(t, t1, expect)
}
func stringSlice(t *testing.T, test interface{}, expected []string) { assert := internal.NewAssert(t, "TestStringSlice")
res := StringSlice(test) assert.Equal([]string{"a", "b", "c"}, StringSlice(strs))
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "StringSlice", test, expected, res)
t.FailNow()
}
} }
func TestInterfaceSlice(t *testing.T) { func TestInterfaceSlice(t *testing.T) {
t1 := []string{"a", "b", "c", "d", "e"} strs := []string{"a", "b", "c"}
expect := []interface{}{"a", "b", "c", "d", "e"} expect := []interface{}{"a", "b", "c"}
interfaceSlice(t, t1, expect)
}
func interfaceSlice(t *testing.T, test interface{}, expected []interface{}) { assert := internal.NewAssert(t, "TestInterfaceSlice")
res := InterfaceSlice(test) assert.Equal(expect, InterfaceSlice(strs))
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "InterfaceSlice", test, expected, res)
t.FailNow()
}
} }
func TestDeleteByIndex(t *testing.T) { func TestDeleteByIndex(t *testing.T) {
origin := []string{"a", "b", "c", "d", "e"} assert := internal.NewAssert(t, "TestDeleteByIndex")
t1 := []string{"a", "b", "c", "d", "e"} t1 := []string{"a", "b", "c", "d", "e"}
r1 := []string{"b", "c", "d", "e"} r1 := []string{"b", "c", "d", "e"}
deleteByIndex(t, origin, t1, 0, 0, r1) a1, _ := DeleteByIndex(t1, 0)
assert.Equal(r1, a1)
t1 = []string{"a", "b", "c", "d", "e"} t2 := []string{"a", "b", "c", "d", "e"}
r2 := []string{"a", "b", "c", "e"} r2 := []string{"a", "b", "c", "e"}
deleteByIndex(t, origin, t1, 3, 0, r2) a2, _ := DeleteByIndex(t2, 3)
assert.Equal(r2, a2)
t1 = []string{"a", "b", "c", "d", "e"} t3 := []string{"a", "b", "c", "d", "e"}
r3 := []string{"a", "b", "c", "d"} r3 := []string{"c", "d", "e"}
deleteByIndex(t, origin, t1, 4, 0, r3) a3, _ := DeleteByIndex(t3, 0, 2)
assert.Equal(r3, a3)
t1 = []string{"a", "b", "c", "d", "e"} t4 := []string{"a", "b", "c", "d", "e"}
r4 := []string{"c", "d", "e"} r4 := []string{}
deleteByIndex(t, origin, t1, 0, 2, r4) a4, _ := DeleteByIndex(t4, 0, 5)
assert.Equal(r4, a4)
t1 = []string{"a", "b", "c", "d", "e"} t5 := []string{"a", "b", "c", "d", "e"}
r5 := []string{} // var r5 []string{} failed _, err := DeleteByIndex(t5, 1, 1)
deleteByIndex(t, origin, t1, 0, 5, r5) assert.IsNotNil(err)
// failed _, err = DeleteByIndex(t5, 0, 6)
//t1 = []string{"a", "b", "c", "d","e"} assert.IsNotNil(err)
//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{}) { func TestDrop(t *testing.T) {
var res interface{} assert := internal.NewAssert(t, "TestInterfaceSlice")
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) { assert.Equal([]int{}, Drop([]int{}, 0))
utils.LogFailedTestInfo(t, "DeleteByIndex", origin, expected, res) assert.Equal([]int{}, Drop([]int{}, 1))
t.FailNow() assert.Equal([]int{}, Drop([]int{}, -1))
}
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0))
assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4}, Drop([]int{1, 2, 3, 4, 5}, -1))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6))
} }
func TestInsertByIndex(t *testing.T) { func TestInsertByIndex(t *testing.T) {
assert := internal.NewAssert(t, "TestInsertByIndex")
t1 := []string{"a", "b", "c"} t1 := []string{"a", "b", "c"}
r1, _ := InsertByIndex(t1, 0, "1")
assert.Equal([]string{"1", "a", "b", "c"}, r1)
r1 := []string{"1", "a", "b", "c"} r2, _ := InsertByIndex(t1, 1, "1")
insertByIndex(t, t1, 0, "1", r1) assert.Equal([]string{"a", "1", "b", "c"}, r2)
r2 := []string{"a", "1", "b", "c"} r3, _ := InsertByIndex(t1, 3, "1")
insertByIndex(t, t1, 1, "1", r2) assert.Equal([]string{"a", "b", "c", "1"}, r3)
r3 := []string{"a", "b", "c", "1"} r4, _ := InsertByIndex(t1, 0, []string{"1", "2", "3"})
insertByIndex(t, t1, 3, "1", r3) assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, r4)
r4 := []string{"1", "2", "3", "a", "b", "c"} r5, _ := InsertByIndex(t1, 3, []string{"1", "2", "3"})
insertByIndex(t, t1, 0, []string{"1", "2", "3"}, r4) assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, r5)
r5 := []string{"a", "1", "2", "3", "b", "c"} _, err := InsertByIndex(t1, 4, "1")
insertByIndex(t, t1, 1, []string{"1", "2", "3"}, r5) assert.IsNotNil(err)
r6 := []string{"a", "b", "1", "2", "3", "c"} _, err = InsertByIndex(t1, 0, 1)
insertByIndex(t, t1, 2, []string{"1", "2", "3"}, r6) assert.IsNotNil(err)
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) { func TestUpdateByIndex(t *testing.T) {
assert := internal.NewAssert(t, "TestUpdateByIndex")
t1 := []string{"a", "b", "c"} t1 := []string{"a", "b", "c"}
r1 := []string{"1", "b", "c"} r1, _ := UpdateByIndex(t1, 0, "1")
updateByIndex(t, t1, 0, "1", r1) assert.Equal([]string{"1", "b", "c"}, r1)
t1 = []string{"a", "b", "c"} t2 := []string{"a", "b", "c"}
r2 := []string{"a", "1", "c"} r2, _ := UpdateByIndex(t2, 1, "1")
updateByIndex(t, t1, 1, "1", r2) assert.Equal([]string{"a", "1", "c"}, r2)
t1 = []string{"a", "b", "c"} _, err := UpdateByIndex([]string{"a", "b", "c"}, 4, "1")
r3 := []string{"a", "b", "1"} assert.IsNotNil(err)
updateByIndex(t, t1, 2, "1", r3)
} _, err = UpdateByIndex([]string{"a", "b", "c"}, 0, 1)
assert.IsNotNil(err)
func updateByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
res, err := UpdateByIndex(test, index, value)
if err != nil {
t.Error("UpdateByIndex Error: " + err.Error())
}
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "UpdateByIndex", test, expected, res)
t.FailNow()
}
} }
func TestUnique(t *testing.T) { func TestUnique(t *testing.T) {
t1 := []int{1, 2, 2, 3} assert := internal.NewAssert(t, "TestUnique")
e1 := []int{1, 2, 3}
r1 := Unique(t1)
if !reflect.DeepEqual(r1, e1) {
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
t.FailNow()
}
t2 := []string{"a", "a", "b", "c"} assert.Equal([]int{1, 2, 3}, Unique([]int{1, 2, 2, 3}))
e2 := []string{"a", "b", "c"} assert.Equal([]string{"a", "b", "c"}, Unique([]string{"a", "a", "b", "c"}))
r2 := Unique(t2)
if !reflect.DeepEqual(r2, e2) {
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
t.FailNow()
}
} }
func TestUnion(t *testing.T) { func TestUnion(t *testing.T) {
assert := internal.NewAssert(t, "TestUnion")
s1 := []int{1, 3, 4, 6} s1 := []int{1, 3, 4, 6}
s2 := []int{1, 2, 5, 6} s2 := []int{1, 2, 5, 6}
s3 := []int{0, 4, 5, 7} s3 := []int{0, 4, 5, 7}
expected1 := []int{1, 3, 4, 6, 2, 5, 0, 7} assert.Equal([]int{1, 3, 4, 6, 2, 5, 0, 7}, Union(s1, s2, s3))
res1 := Union(s1, s2, s3) assert.Equal([]int{1, 3, 4, 6, 2, 5}, Union(s1, s2))
if !reflect.DeepEqual(res1, expected1) { assert.Equal([]int{1, 3, 4, 6}, Union(s1))
utils.LogFailedTestInfo(t, "Union", s1, expected1, res1)
t.FailNow()
}
expected2 := []int{1, 3, 4, 6}
res2 := Union(s1)
if !reflect.DeepEqual(res2, expected2) {
utils.LogFailedTestInfo(t, "Union", s1, expected2, res2)
t.FailNow()
}
} }
func TestIntersection(t *testing.T) { func TestIntersection(t *testing.T) {
@@ -448,45 +410,49 @@ func TestIntersection(t *testing.T) {
Intersection(s1), Intersection(s1),
Intersection(s1, s4), Intersection(s1, s4),
} }
for i := 0; i < len(res); i++ {
if !reflect.DeepEqual(res[i], expected[i]) {
utils.LogFailedTestInfo(t, "Intersection", "Intersection", expected[i], res[i])
t.FailNow()
}
}
assert := internal.NewAssert(t, "TestIntersection")
for i := 0; i < len(res); i++ {
assert.Equal(res[i], expected[i])
}
assert.IsNil(Intersection())
} }
func TestReverseSlice(t *testing.T) { func TestReverseSlice(t *testing.T) {
assert := internal.NewAssert(t, "TestIntersection")
s1 := []int{1, 2, 3, 4, 5} s1 := []int{1, 2, 3, 4, 5}
e1 := []int{5, 4, 3, 2, 1}
ReverseSlice(s1) ReverseSlice(s1)
if !reflect.DeepEqual(s1, e1) { assert.Equal([]int{5, 4, 3, 2, 1}, s1)
utils.LogFailedTestInfo(t, "ReverseSlice", s1, e1, s1)
t.FailNow()
}
s2 := []string{"a", "b", "c", "d", "e"} s2 := []string{"a", "b", "c", "d", "e"}
e2 := []string{"e", "d", "c", "b", "a"}
ReverseSlice(s2) ReverseSlice(s2)
if !reflect.DeepEqual(s2, e2) { assert.Equal([]string{"e", "d", "c", "b", "a"}, s2)
utils.LogFailedTestInfo(t, "ReverseSlice", s2, e2, s2)
t.FailNow()
}
} }
func TestDifference(t *testing.T) { func TestDifference(t *testing.T) {
assert := internal.NewAssert(t, "TestIntersection")
s1 := []int{1, 2, 3, 4, 5} s1 := []int{1, 2, 3, 4, 5}
s2 := []int{4, 5, 6} s2 := []int{4, 5, 6}
e1 := []int{1, 2, 3} assert.Equal([]int{1, 2, 3}, Difference(s1, s2))
r1 := Difference(s1, s2)
if !reflect.DeepEqual(r1, e1) {
utils.LogFailedTestInfo(t, "Difference", s1, e1, r1)
t.FailNow()
}
} }
func TestSortByField(t *testing.T) { func TestDifferenceBy(t *testing.T) {
assert := internal.NewAssert(t, "TestDifferenceBy")
s1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6
s2 := []int{3, 4, 5} //after add one: 4 5 6
addOne := func(i int, v int) int {
return v + 1
}
assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne))
}
func TestSortByFielDesc(t *testing.T) {
assert := internal.NewAssert(t, "TestWithout")
type student struct { type student struct {
name string name string
age int age int
@@ -497,8 +463,7 @@ func TestSortByField(t *testing.T) {
{"c", 5}, {"c", 5},
{"d", 6}, {"d", 6},
} }
studentsOfSortByAge := []student{
sortByAge := []student{
{"b", 15}, {"b", 15},
{"a", 10}, {"a", 10},
{"d", 6}, {"d", 6},
@@ -506,24 +471,61 @@ func TestSortByField(t *testing.T) {
} }
err := SortByField(students, "age", "desc") err := SortByField(students, "age", "desc")
if err != nil { assert.IsNil(err)
t.Error("IntSlice Error: " + err.Error())
assert.Equal(students, studentsOfSortByAge)
}
func TestSortByFieldAsc(t *testing.T) {
assert := internal.NewAssert(t, "TestSortByField")
type student struct {
name string
age int
}
students := []student{
{"a", 10},
{"b", 15},
{"c", 5},
{"d", 6},
}
studentsOfSortByAge := []student{
{"c", 5},
{"d", 6},
{"a", 10},
{"b", 15},
} }
if !reflect.DeepEqual(students, sortByAge) { err := SortByField(students, "age")
utils.LogFailedTestInfo(t, "SortByField", students, sortByAge, students) assert.IsNil(err)
t.FailNow()
}
assert.Equal(students, studentsOfSortByAge)
} }
func TestWithout(t *testing.T) { func TestWithout(t *testing.T) {
s := []int{1, 2, 3, 4, 5} assert := internal.NewAssert(t, "TestWithout")
expected := []int{3, 4, 5} assert.Equal([]int{3, 4, 5}, Without([]int{1, 2, 3, 4, 5}, 1, 2))
res := Without(s, 1, 2) assert.Equal([]int{1, 2, 3, 4, 5}, Without([]int{1, 2, 3, 4, 5}))
}
if !reflect.DeepEqual(res, expected) {
utils.LogFailedTestInfo(t, "Without", s, expected, res) func TestShuffle(t *testing.T) {
t.FailNow() assert := internal.NewAssert(t, "TestShuffle")
}
s := []int{1, 2, 3, 4, 5}
res := Shuffle(s)
t.Log("Shuffle result: ", res)
assert.Equal(reflect.TypeOf(s), reflect.TypeOf(res))
rv := reflect.ValueOf(res)
assert.Equal(5, rv.Len())
assert.Equal(true, rv.Kind() == reflect.Slice)
assert.Equal(true, rv.Type().Elem().Kind() == reflect.Int)
assert.Equal(true, Contain(res, 1))
assert.Equal(true, Contain(res, 2))
assert.Equal(true, Contain(res, 3))
assert.Equal(true, Contain(res, 4))
assert.Equal(true, Contain(res, 5))
} }

View File

@@ -52,3 +52,14 @@ func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bo
} }
return true return true
} }
// sliceElemType get slice element type
func sliceElemType(reflectType reflect.Type) reflect.Type {
for {
if reflectType.Kind() != reflect.Slice {
return reflectType
}
reflectType = reflectType.Elem()
}
}

View File

@@ -8,6 +8,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
) )
// CamelCase covert string to camelCase string. // CamelCase covert string to camelCase string.
@@ -53,26 +54,28 @@ 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 {
return "" return ""
} }
res := "" r, size := utf8.DecodeRuneInString(s)
for i, v := range []rune(s) { r = unicode.ToLower(r)
if i == 0 {
if v >= 65 && v <= 96 { return string(r) + s[size:]
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. // PadEnd pads string on the right side if it's shorter than size.
@@ -214,3 +217,34 @@ func ReverseStr(s string) string {
} }
return string(r) return string(r)
} }
// Wrap a string with another string.
func Wrap(str string, wrapWith string) string {
if str == "" || wrapWith == "" {
return str
}
var sb strings.Builder
sb.WriteString(wrapWith)
sb.WriteString(str)
sb.WriteString(wrapWith)
return sb.String()
}
// Unwrap a given string from anther string. will change str value
func Unwrap(str string, wrapToken string) string {
if str == "" || wrapToken == "" {
return str
}
firstIndex := strings.Index(str, wrapToken)
lastIndex := strings.LastIndex(str, wrapToken)
if firstIndex == 0 && lastIndex > 0 && lastIndex <= len(str)-1 {
if len(wrapToken) <= lastIndex {
str = str[len(wrapToken):lastIndex]
}
}
return str
}

View File

@@ -3,198 +3,177 @@ package strutil
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestCamelCase(t *testing.T) { func TestCamelCase(t *testing.T) {
camelCase(t, "foo_bar", "fooBar") assert := internal.NewAssert(t, "TestCamelCase")
camelCase(t, "Foo-Bar", "fooBar")
camelCase(t, "Foo&bar", "fooBar")
camelCase(t, "foo bar", "fooBar")
}
func camelCase(t *testing.T, test string, expected string) { assert.Equal("fooBar", CamelCase("foo_bar"))
res := CamelCase(test) assert.Equal("fooBar", CamelCase("Foo-Bar"))
if res != expected { assert.Equal("fooBar", CamelCase("Foo&bar"))
utils.LogFailedTestInfo(t, "CamelCase", test, expected, res) assert.Equal("fooBar", CamelCase("foo bar"))
t.FailNow()
} assert.NotEqual("FooBar", CamelCase("foo_bar"))
} }
func TestCapitalize(t *testing.T) { func TestCapitalize(t *testing.T) {
capitalize(t, "foo", "Foo") assert := internal.NewAssert(t, "TestCapitalize")
capitalize(t, "fOO", "Foo")
capitalize(t, "FOo", "Foo")
}
func capitalize(t *testing.T, test string, expected string) { assert.Equal("Foo", Capitalize("foo"))
res := Capitalize(test) assert.Equal("Foo", Capitalize("Foo"))
if res != expected { assert.Equal("Foo", Capitalize("Foo"))
utils.LogFailedTestInfo(t, "Capitalize", test, expected, res)
t.FailNow() assert.NotEqual("foo", Capitalize("Foo"))
}
} }
func TestKebabCase(t *testing.T) { func TestKebabCase(t *testing.T) {
kebabCase(t, "Foo Bar-", "foo-bar") assert := internal.NewAssert(t, "TestKebabCase")
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) { assert.Equal("foo-bar", KebabCase("Foo Bar-"))
res := KebabCase(test) assert.Equal("foo-bar", KebabCase("foo_Bar"))
if res != expected { assert.Equal("foo-bar", KebabCase("fooBar"))
utils.LogFailedTestInfo(t, "KebabCase", test, expected, res) assert.Equal("f-o-o-b-a-r", KebabCase("__FOO_BAR__"))
t.FailNow()
} assert.NotEqual("foo_bar", KebabCase("fooBar"))
} }
func TestSnakeCase(t *testing.T) { func TestSnakeCase(t *testing.T) {
snakeCase(t, "Foo Bar-", "foo_bar") assert := internal.NewAssert(t, "TestSnakeCase")
snakeCase(t, "foo_Bar", "foo_bar")
snakeCase(t, "fooBar", "foo_bar") assert.Equal("foo_bar", SnakeCase("Foo Bar-"))
snakeCase(t, "__FOO_BAR__", "f_o_o_b_a_r") assert.Equal("foo_bar", SnakeCase("foo_Bar"))
snakeCase(t, "aBbc-s$@a&%_B.B^C", "a_bbc_s_a_b_b_c") assert.Equal("foo_bar", SnakeCase("fooBar"))
assert.Equal("f_o_o_b_a_r", SnakeCase("__FOO_BAR__"))
assert.Equal("a_bbc_s_a_b_b_c", SnakeCase("aBbc-s$@a&%_B.B^C"))
assert.NotEqual("foo-bar", SnakeCase("foo_Bar"))
} }
func snakeCase(t *testing.T, test string, expected string) { func TestUpperFirst(t *testing.T) {
res := SnakeCase(test) assert := internal.NewAssert(t, "TestLowerFirst")
if res != expected {
utils.LogFailedTestInfo(t, "SnakeCase", test, expected, res) assert.Equal("Foo", UpperFirst("foo"))
t.FailNow() 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) {
lowerFirst(t, "foo", "foo") assert := internal.NewAssert(t, "TestLowerFirst")
lowerFirst(t, "BAR", "bAR")
lowerFirst(t, "FOo", "fOo")
}
func lowerFirst(t *testing.T, test string, expected string) { assert.Equal("foo", LowerFirst("foo"))
res := LowerFirst(test) assert.Equal("bAR", LowerFirst("BAR"))
if res != expected { assert.Equal("fOo", LowerFirst("FOo"))
utils.LogFailedTestInfo(t, "LowerFirst", test, expected, res) assert.Equal("fOo大", LowerFirst("FOo大"))
t.FailNow()
} assert.NotEqual("Bar", LowerFirst("BAR"))
} }
func TestPadEnd(t *testing.T) { func TestPadEnd(t *testing.T) {
padEnd(t, "a", 1, "b", "a") assert := internal.NewAssert(t, "TestPadEnd")
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) { assert.Equal("a", PadEnd("a", 1, "b"))
res := PadEnd(source, size, fillString) assert.Equal("ab", PadEnd("a", 2, "b"))
if res != expected { assert.Equal("abcdmn", PadEnd("abcd", 6, "mno"))
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res) assert.Equal("abcdmm", PadEnd("abcd", 6, "m"))
t.FailNow() assert.Equal("abcaba", PadEnd("abc", 6, "ab"))
}
assert.NotEqual("ba", PadEnd("a", 2, "b"))
} }
func TestPadStart(t *testing.T) { func TestPadStart(t *testing.T) {
padStart(t, "a", 1, "b", "a") assert := internal.NewAssert(t, "TestPadStart")
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) { assert.Equal("a", PadStart("a", 1, "b"))
res := PadStart(source, size, fillString) assert.Equal("ba", PadStart("a", 2, "b"))
if res != expected { assert.Equal("mnabcd", PadStart("abcd", 6, "mno"))
utils.LogFailedTestInfo(t, "PadEnd", source, expected, res) assert.Equal("mmabcd", PadStart("abcd", 6, "m"))
t.FailNow() assert.Equal("abaabc", PadStart("abc", 6, "ab"))
}
assert.NotEqual("ab", PadStart("a", 2, "b"))
} }
func TestBefore(t *testing.T) { func TestBefore(t *testing.T) {
before(t, "lancet", "", "lancet") assert := internal.NewAssert(t, "TestBefore")
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) { assert.Equal("lancet", Before("lancet", ""))
res := Before(source, char) assert.Equal("github.com", Before("github.com/test/lancet", "/"))
if res != expected { assert.Equal("github.com/", Before("github.com/test/lancet", "test"))
utils.LogFailedTestInfo(t, "Before", source, expected, res)
t.FailNow()
}
} }
func TestBeforeLast(t *testing.T) { func TestBeforeLast(t *testing.T) {
beforeLast(t, "lancet", "", "lancet") assert := internal.NewAssert(t, "TestBeforeLast")
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) { assert.Equal("lancet", BeforeLast("lancet", ""))
res := BeforeLast(source, char) assert.Equal("github.com/test", BeforeLast("github.com/test/lancet", "/"))
if res != expected { assert.Equal("github.com/test/", BeforeLast("github.com/test/test/lancet", "test"))
utils.LogFailedTestInfo(t, "BeforeLast", source, expected, res)
t.FailNow() assert.NotEqual("github.com/", BeforeLast("github.com/test/test/lancet", "test"))
}
} }
func TestAfter(t *testing.T) { func TestAfter(t *testing.T) {
after(t, "lancet", "", "lancet") assert := internal.NewAssert(t, "TestAfter")
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) { assert.Equal("lancet", After("lancet", ""))
res := After(source, char) assert.Equal("test/lancet", After("github.com/test/lancet", "/"))
if res != expected { assert.Equal("/lancet", After("github.com/test/lancet", "test"))
utils.LogFailedTestInfo(t, "After", source, expected, res)
t.FailNow()
}
} }
func TestAfterLast(t *testing.T) { func TestAfterLast(t *testing.T) {
afterLast(t, "lancet", "", "lancet") assert := internal.NewAssert(t, "TestAfterLast")
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) { assert.Equal("lancet", AfterLast("lancet", ""))
res := AfterLast(source, char) assert.Equal("lancet", AfterLast("github.com/test/lancet", "/"))
if res != expected { assert.Equal("/lancet", AfterLast("github.com/test/lancet", "test"))
utils.LogFailedTestInfo(t, "AfterLast", source, expected, res) assert.Equal("/lancet", AfterLast("github.com/test/test/lancet", "test"))
t.FailNow()
} assert.NotEqual("/test/lancet", AfterLast("github.com/test/test/lancet", "test"))
} }
func TestIsString(t *testing.T) { func TestIsString(t *testing.T) {
isString(t, "lancet", true) assert := internal.NewAssert(t, "TestIsString")
isString(t, 1, false)
isString(t, true, false)
isString(t, []string{}, false)
}
func isString(t *testing.T, test interface{}, expected bool) { assert.Equal(true, IsString("lancet"))
res := IsString(test) assert.Equal(true, IsString(""))
if res != expected { assert.Equal(false, IsString(1))
utils.LogFailedTestInfo(t, "IsString", test, expected, res) assert.Equal(false, IsString(true))
t.FailNow() assert.Equal(false, IsString([]string{}))
}
} }
func TestReverseStr(t *testing.T) { func TestReverseStr(t *testing.T) {
reverseStr(t, "abc", "cba") assert := internal.NewAssert(t, "TestReverseStr")
reverseStr(t, "12345", "54321")
//failed assert.Equal("cba", ReverseStr("abc"))
//reverseStr(t, "abc", "abc") assert.Equal("54321", ReverseStr("12345"))
} }
func reverseStr(t *testing.T, test string, expected string) { func TestWrap(t *testing.T) {
res := ReverseStr(test) assert := internal.NewAssert(t, "TestWrap")
if res != expected {
utils.LogFailedTestInfo(t, "ReverseStr", test, expected, res) assert.Equal("ab", Wrap("ab", ""))
t.FailNow() assert.Equal("", Wrap("", "*"))
} assert.Equal("*ab*", Wrap("ab", "*"))
assert.Equal("\"ab\"", Wrap("ab", "\""))
assert.Equal("'ab'", Wrap("ab", "'"))
}
func TestUnwrap(t *testing.T) {
assert := internal.NewAssert(t, "TestUnwrap")
assert.Equal("", Unwrap("", "*"))
assert.Equal("ab", Unwrap("ab", ""))
assert.Equal("ab", Unwrap("ab", "*"))
assert.Equal("*ab*", Unwrap("**ab**", "*"))
assert.Equal("ab", Unwrap("**ab**", "**"))
assert.Equal("ab", Unwrap("\"ab\"", "\""))
assert.Equal("*ab", Unwrap("*ab", "*"))
assert.Equal("ab*", Unwrap("ab*", "*"))
assert.Equal("*", Unwrap("***", "*"))
assert.Equal("", Unwrap("**", "*"))
assert.Equal("***", Unwrap("***", "**"))
assert.Equal("**", Unwrap("**", "**"))
} }

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

@@ -1,16 +0,0 @@
// 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"
)
// LogFailedTestInfo log test failed info for internal use
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)
}

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

@@ -3,281 +3,279 @@ package validator
import ( import (
"testing" "testing"
"github.com/duke-git/lancet/utils" "github.com/duke-git/lancet/internal"
) )
func TestIsNumberStr(t *testing.T) { func TestIsAllUpper(t *testing.T) {
isNumberStr(t, "3.", true) assert := internal.NewAssert(t, "TestIsAllUpper")
isNumberStr(t, "+3.", true)
isNumberStr(t, "-3.", true) assert.Equal(true, IsAllUpper("ABC"))
isNumberStr(t, "+3e2", true) assert.Equal(false, IsAllUpper(""))
isNumberStr(t, "abc", false) 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 isNumberStr(t *testing.T, source string, expected bool) { func TestIsAllLower(t *testing.T) {
res := IsNumberStr(source) assert := internal.NewAssert(t, "TestIsAllLower")
if res != expected {
utils.LogFailedTestInfo(t, "IsNumberStr", source, expected, res) assert.Equal(true, IsAllLower("abc"))
t.FailNow() 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) {
assert := internal.NewAssert(t, "TestIsNumberStr")
assert.Equal(true, IsNumberStr("3."))
assert.Equal(true, IsNumberStr("+3."))
assert.Equal(true, IsNumberStr("-3."))
assert.Equal(true, IsNumberStr("+3e2"))
assert.Equal(false, IsNumberStr("abc"))
} }
func TestIsFloatStr(t *testing.T) { func TestIsFloatStr(t *testing.T) {
isFloatStr(t, "3.", true) assert := internal.NewAssert(t, "TestIsFloatStr")
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) { assert.Equal(true, IsFloatStr("3."))
res := IsFloatStr(source) assert.Equal(true, IsFloatStr("+3."))
if res != expected { assert.Equal(true, IsFloatStr("-3."))
utils.LogFailedTestInfo(t, "IsFloatStr", source, expected, res) assert.Equal(true, IsFloatStr("12"))
t.FailNow() assert.Equal(false, IsFloatStr("abc"))
}
} }
func TestIsIntStr(t *testing.T) { func TestIsIntStr(t *testing.T) {
isIntStr(t, "+3", true) assert := internal.NewAssert(t, "TestIsIntStr")
isIntStr(t, "-3", true)
isIntStr(t, "3.", false) assert.Equal(true, IsIntStr("+3"))
isIntStr(t, "abc", false) assert.Equal(true, IsIntStr("-3"))
assert.Equal(false, IsIntStr("3."))
assert.Equal(false, IsIntStr("abc"))
} }
func isIntStr(t *testing.T, source string, expected bool) { func TestIsPort(t *testing.T) {
res := IsIntStr(source) assert := internal.NewAssert(t, "TestIsPort")
if res != expected {
utils.LogFailedTestInfo(t, "IsIntStr", source, expected, res) assert.Equal(true, IsPort("1"))
t.FailNow() 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) {
isIp(t, "127.0.0.1", true) assert := internal.NewAssert(t, "TestIsIntStr")
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) { assert.Equal(true, IsIp("127.0.0.1"))
res := IsIp(source) assert.Equal(true, IsIp("::0:0:0:0:0:0:1"))
if res != expected { assert.Equal(false, IsIp("127.0.0"))
utils.LogFailedTestInfo(t, "IsIp", source, expected, res) assert.Equal(false, IsIp("127"))
t.FailNow()
}
} }
func TestIsIpV4(t *testing.T) { func TestIsIpV4(t *testing.T) {
isIpV4(t, "127.0.0.1", true) assert := internal.NewAssert(t, "TestIsIpV4")
isIpV4(t, "::0:0:0:0:0:0:1", false)
}
func isIpV4(t *testing.T, source string, expected bool) { assert.Equal(true, IsIpV4("127.0.0.1"))
res := IsIpV4(source) assert.Equal(false, IsIpV4("::0:0:0:0:0:0:1"))
if res != expected {
utils.LogFailedTestInfo(t, "IsIpV4", source, expected, res)
t.FailNow()
}
} }
func TestIsIpV6(t *testing.T) { func TestIsIpV6(t *testing.T) {
isIpV6(t, "127.0.0.1", false) assert := internal.NewAssert(t, "TestIsIpV6")
isIpV6(t, "::0:0:0:0:0:0:1", true)
assert.Equal(false, IsIpV6("127.0.0.1"))
assert.Equal(true, IsIpV6("::0:0:0:0:0:0:1"))
} }
func isIpV6(t *testing.T, source string, expected bool) { func TestIsUrl(t *testing.T) {
res := IsIpV6(source) assert := internal.NewAssert(t, "TestIsUrl")
if res != expected {
utils.LogFailedTestInfo(t, "IsIpV6", source, expected, res) assert.Equal(true, IsUrl("http://abc.com"))
t.FailNow() 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) {
isDns(t, "abc.com", true) assert := internal.NewAssert(t, "TestIsDns")
isDns(t, "a.b.com", false)
}
func isDns(t *testing.T, source string, expected bool) { assert.Equal(true, IsDns("abc.com"))
res := IsDns(source) assert.Equal(false, IsDns("a.b.com"))
if res != expected { assert.Equal(false, IsDns("http://abc.com"))
utils.LogFailedTestInfo(t, "IsDns", source, expected, res)
t.FailNow()
}
} }
func TestIsEmail(t *testing.T) { func TestIsEmail(t *testing.T) {
isEmail(t, "abc@xyz.com", true) assert := internal.NewAssert(t, "TestIsEmail")
isEmail(t, "a.b@@com", false)
}
func isEmail(t *testing.T, source string, expected bool) { assert.Equal(true, IsEmail("abc@xyz.com"))
res := IsEmail(source) assert.Equal(false, IsEmail("a.b@@com"))
if res != expected {
utils.LogFailedTestInfo(t, "IsEmail", source, expected, res)
t.FailNow()
}
} }
func TestContainChinese(t *testing.T) { func TestContainChinese(t *testing.T) {
containChinese(t, "你好", true) assert := internal.NewAssert(t, "TestContainChinese")
containChinese(t, "hello", false)
containChinese(t, "hello你好", true)
}
func containChinese(t *testing.T, source string, expected bool) { assert.Equal(true, ContainChinese("你好"))
res := ContainChinese(source) assert.Equal(true, ContainChinese("你好hello"))
if res != expected { assert.Equal(false, ContainChinese("hello"))
utils.LogFailedTestInfo(t, "IsContainChineseChar", source, expected, res)
t.FailNow()
}
} }
func TestIsChineseMobile(t *testing.T) { func TestIsChineseMobile(t *testing.T) {
isChineseMobile(t, "13263527980", true) assert := internal.NewAssert(t, "TestIsChineseMobile")
isChineseMobile(t, "434324324", false)
}
func isChineseMobile(t *testing.T, source string, expected bool) { assert.Equal(true, IsChineseMobile("13263527980"))
res := IsChineseMobile(source) assert.Equal(false, IsChineseMobile("434324324"))
if res != expected {
utils.LogFailedTestInfo(t, "IsChineseMobile", source, expected, res)
t.FailNow()
}
} }
func TestIsChinesePhone(t *testing.T) { func TestIsChinesePhone(t *testing.T) {
isChinesePhone(t, "010-32116675", true) assert := internal.NewAssert(t, "TestIsChinesePhone")
isChinesePhone(t, "0464-8756213", true)
isChinesePhone(t, "123-87562", false) assert.Equal(true, IsChinesePhone("010-32116675"))
} assert.Equal(true, IsChinesePhone("0464-8756213"))
assert.Equal(false, IsChinesePhone("123-87562"))
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) { func TestIsChineseIdNum(t *testing.T) {
isChineseIdNum(t, "210911192105130715", true) assert := internal.NewAssert(t, "TestIsChineseIdNum")
isChineseIdNum(t, "21091119210513071X", true)
isChineseIdNum(t, "21091119210513071x", true)
isChineseIdNum(t, "123456", false)
}
func isChineseIdNum(t *testing.T, source string, expected bool) { assert.Equal(true, IsChineseIdNum("210911192105130715"))
res := IsChineseIdNum(source) assert.Equal(true, IsChineseIdNum("21091119210513071X"))
if res != expected { assert.Equal(true, IsChineseIdNum("21091119210513071x"))
utils.LogFailedTestInfo(t, "IsChineseIdNum", source, expected, res) assert.Equal(false, IsChineseIdNum("123456"))
t.FailNow()
}
} }
func TestIsCreditCard(t *testing.T) { func TestIsCreditCard(t *testing.T) {
isCreditCard(t, "4111111111111111", true) assert := internal.NewAssert(t, "TestIsCreditCard")
isCreditCard(t, "123456", false)
}
func isCreditCard(t *testing.T, source string, expected bool) { assert.Equal(true, IsCreditCard("4111111111111111"))
res := IsCreditCard(source) assert.Equal(false, IsCreditCard("123456"))
if res != expected {
utils.LogFailedTestInfo(t, "IsCreditCard", source, expected, res)
t.FailNow()
}
} }
func TestIsBase64(t *testing.T) { func TestIsBase64(t *testing.T) {
isBase64(t, "aGVsbG8=", true) assert := internal.NewAssert(t, "TestIsBase64")
isBase64(t, "123456", false)
}
func isBase64(t *testing.T, source string, expected bool) { assert.Equal(true, IsBase64("aGVsbG8="))
res := IsBase64(source) assert.Equal(false, IsBase64("123456"))
if res != expected {
utils.LogFailedTestInfo(t, "IsBase64", source, expected, res)
t.FailNow()
}
} }
func TestIsEmptyString(t *testing.T) { func TestIsEmptyString(t *testing.T) {
isEmptyString(t, "111", false) assert := internal.NewAssert(t, "TestIsEmptyString")
isEmptyString(t, " ", false)
isEmptyString(t, "\t", false)
isEmptyString(t, "", true)
}
func isEmptyString(t *testing.T, source string, expected bool) { assert.Equal(true, IsEmptyString(""))
res := IsEmptyString(source) assert.Equal(false, IsEmptyString("111"))
if res != expected { assert.Equal(false, IsEmptyString(" "))
utils.LogFailedTestInfo(t, "IsEmptyString", source, expected, res) assert.Equal(false, IsEmptyString("\t"))
t.FailNow()
}
} }
func TestIsAlpha(t *testing.T) { func TestIsAlpha(t *testing.T) {
isAlpha(t, "abc", true) assert := internal.NewAssert(t, "TestIsAlpha")
isAlpha(t, "111", false)
isAlpha(t, " ", false)
isAlpha(t, "\t", false)
isAlpha(t, "", false)
}
func isAlpha(t *testing.T, source string, expected bool) { assert.Equal(true, IsAlpha("abc"))
res := IsAlpha(source) assert.Equal(false, IsAlpha("111"))
if res != expected { assert.Equal(false, IsAlpha(" "))
utils.LogFailedTestInfo(t, "IsAlpha", source, expected, res) assert.Equal(false, IsAlpha("\t"))
t.FailNow() assert.Equal(false, IsAlpha(""))
}
} }
func TestIsRegexMatch(t *testing.T) { func TestIsRegexMatch(t *testing.T) {
isRegexMatch(t, "abc", `^[a-zA-Z]+$`, true) assert := internal.NewAssert(t, "TestIsRegexMatch")
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) { assert.Equal(true, IsRegexMatch("abc", `^[a-zA-Z]+$`))
res := IsRegexMatch(source, regex) assert.Equal(false, IsRegexMatch("1ab", `^[a-zA-Z]+$`))
if res != expected { assert.Equal(false, IsRegexMatch("", `^[a-zA-Z]+$`))
utils.LogFailedTestInfo(t, "IsRegexMatch", source, expected, res)
t.FailNow()
}
} }
func TestIsStrongPassword(t *testing.T) { func TestIsStrongPassword(t *testing.T) {
isStrongPassword(t, "abc", 3, false) assert := internal.NewAssert(t, "TestIsStrongPassword")
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) { assert.Equal(false, IsStrongPassword("abc", 3))
res := IsStrongPassword(source, length) assert.Equal(false, IsStrongPassword("abc123", 6))
if res != expected { assert.Equal(false, IsStrongPassword("abcABC", 6))
utils.LogFailedTestInfo(t, "IsStrongPassword", source, expected, res) assert.Equal(false, IsStrongPassword("abc123@#$", 9))
t.FailNow() assert.Equal(false, IsStrongPassword("abcABC123@#$", 16))
} assert.Equal(true, IsStrongPassword("abcABC123@#$", 12))
assert.Equal(true, IsStrongPassword("abcABC123@#$", 10))
} }
func TestIsWeakPassword(t *testing.T) { func TestIsWeakPassword(t *testing.T) {
isWeakPassword(t, "abc", true) assert := internal.NewAssert(t, "TestIsWeakPassword")
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) { assert.Equal(true, IsWeakPassword("abc"))
res := IsWeakPassword(source) assert.Equal(true, IsWeakPassword("123"))
if res != expected { assert.Equal(true, IsWeakPassword("abc123"))
utils.LogFailedTestInfo(t, "IsWeakPassword", source, expected, res) assert.Equal(true, IsWeakPassword("abcABC123"))
t.FailNow() assert.Equal(false, IsWeakPassword("abc123@#$"))
}
} }