From bd976642f6bab41529c3a00f99a884f73920ce7f Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Thu, 13 Jan 2022 16:18:49 +0800
Subject: [PATCH 1/7] feat: add try package for executing a function repeatedly
---
retry/retry.go | 89 +++++++++++++++++++++++++++++++++++++++++++++
retry/retry_test.go | 80 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 retry/retry.go
create mode 100644 retry/retry_test.go
diff --git a/retry/retry.go b/retry/retry.go
new file mode 100644
index 0000000..f58c4fe
--- /dev/null
+++ b/retry/retry.go
@@ -0,0 +1,89 @@
+// 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 = 5
+ DefaultRetryDuration = time.Second * 3
+)
+
+// RetryConfig is config for retry
+type RetryConfig struct {
+ context context.Context
+ retryTimes uint
+ retryDuration time.Duration
+}
+
+// RetryFn 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)
+}
diff --git a/retry/retry_test.go b/retry/retry_test.go
new file mode 100644
index 0000000..f2f0815
--- /dev/null
+++ b/retry/retry_test.go
@@ -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)
+}
From 22b3c4dd422d3122039a40a2747f71205082daa5 Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Thu, 13 Jan 2022 20:19:41 +0800
Subject: [PATCH 2/7] feat: add validator functions, IsAllUpper, IsAllLower,
ContainUpper, ContainLower, ContainLetter, IsJSON and IsPort
---
validator/validator.go | 95 +++++++++++++++++++++++++--------
validator/validator_test.go | 102 ++++++++++++++++++++++++++++++++++++
2 files changed, 175 insertions(+), 22 deletions(-)
diff --git a/validator/validator.go b/validator/validator.go
index 8b6128e..bd1e1b8 100644
--- a/validator/validator.go
+++ b/validator/validator.go
@@ -5,17 +5,72 @@
package validator
import (
+ "encoding/json"
"net"
"regexp"
"strconv"
+ "strings"
"unicode"
)
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
// IsAlpha checks if the string contains only letters (a-zA-Z)
-func IsAlpha(s string) bool {
- return isAlphaRegexMatcher.MatchString(s)
+func IsAlpha(str string) bool {
+ 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)
+}
+
+// Is 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.
@@ -24,16 +79,16 @@ func IsNumberStr(s string) bool {
}
// IsFloatStr check if the string can convert to a float.
-func IsFloatStr(s string) bool {
- _, e := strconv.ParseFloat(s, 64)
+func IsFloatStr(str string) bool {
+ _, e := strconv.ParseFloat(str, 64)
return e == nil
}
var isIntStrRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
// IsIntStr check if the string can convert to a integer.
-func IsIntStr(s string) bool {
- return isIntStrRegexMatcher.MatchString(s)
+func IsIntStr(str string) bool {
+ return isIntStrRegexMatcher.MatchString(str)
}
// IsIp check if the string is a ip address.
@@ -48,13 +103,7 @@ func IsIpV4(ipstr string) bool {
if ip == nil {
return false
}
- for i := 0; i < len(ipstr); i++ {
- switch ipstr[i] {
- case '.':
- return true
- }
- }
- return false
+ return strings.Contains(ipstr, ".")
}
// IsIpV6 check if the string is a ipv6 address.
@@ -63,11 +112,13 @@ func IsIpV6(ipstr string) bool {
if ip == nil {
return false
}
- for i := 0; i < len(ipstr); i++ {
- switch ipstr[i] {
- case ':':
- return true
- }
+ return strings.Contains(ipstr, ":")
+}
+
+// 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
}
@@ -130,14 +181,14 @@ func IsBase64(base64 string) bool {
}
// IsEmptyString check if the string is empty.
-func IsEmptyString(s string) bool {
- return len(s) == 0
+func IsEmptyString(str string) bool {
+ return len(str) == 0
}
// IsRegexMatch check if the string match the regexp
-func IsRegexMatch(s, regex string) bool {
+func IsRegexMatch(str, regex string) bool {
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
diff --git a/validator/validator_test.go b/validator/validator_test.go
index c500199..6d07bb6 100644
--- a/validator/validator_test.go
+++ b/validator/validator_test.go
@@ -6,6 +6,96 @@ import (
"github.com/duke-git/lancet/internal"
)
+func TestIsAllUpper(t *testing.T) {
+ assert := internal.NewAssert(t, "TestIsAllUpper")
+
+ assert.Equal(true, IsAllUpper("ABC"))
+ assert.Equal(false, IsAllUpper(""))
+ assert.Equal(false, IsAllUpper("abc"))
+ assert.Equal(false, IsAllUpper("aBC"))
+ assert.Equal(false, IsAllUpper("1BC"))
+ assert.Equal(false, IsAllUpper("1bc"))
+ assert.Equal(false, IsAllUpper("123"))
+ assert.Equal(false, IsAllUpper("你好"))
+ assert.Equal(false, IsAllUpper("A&"))
+ assert.Equal(false, IsAllUpper("&@#$%^&*"))
+}
+
+func TestIsAllLower(t *testing.T) {
+ assert := internal.NewAssert(t, "TestIsAllLower")
+
+ assert.Equal(true, IsAllLower("abc"))
+ assert.Equal(false, IsAllLower("ABC"))
+ assert.Equal(false, IsAllLower(""))
+ assert.Equal(false, IsAllLower("aBC"))
+ assert.Equal(false, IsAllLower("1BC"))
+ assert.Equal(false, IsAllLower("1bc"))
+ assert.Equal(false, IsAllLower("123"))
+ assert.Equal(false, IsAllLower("你好"))
+ assert.Equal(false, IsAllLower("A&"))
+ assert.Equal(false, IsAllLower("&@#$%^&*"))
+}
+
+func TestContainLower(t *testing.T) {
+ assert := internal.NewAssert(t, "TestContainLower")
+
+ assert.Equal(true, ContainLower("abc"))
+ assert.Equal(true, ContainLower("aBC"))
+ assert.Equal(true, ContainLower("1bc"))
+ assert.Equal(true, ContainLower("a&"))
+
+ assert.Equal(false, ContainLower("ABC"))
+ assert.Equal(false, ContainLower(""))
+ assert.Equal(false, ContainLower("1BC"))
+ assert.Equal(false, ContainLower("123"))
+ assert.Equal(false, ContainLower("你好"))
+ assert.Equal(false, ContainLower("&@#$%^&*"))
+}
+
+func TestContainUpper(t *testing.T) {
+ assert := internal.NewAssert(t, "TestContainUpper")
+
+ assert.Equal(true, ContainUpper("ABC"))
+ assert.Equal(true, ContainUpper("aBC"))
+ assert.Equal(true, ContainUpper("1BC"))
+ assert.Equal(true, ContainUpper("A&"))
+
+ assert.Equal(false, ContainUpper("abc"))
+ assert.Equal(false, ContainUpper(""))
+ assert.Equal(false, ContainUpper("1bc"))
+ assert.Equal(false, ContainUpper("123"))
+ assert.Equal(false, ContainUpper("你好"))
+ assert.Equal(false, ContainUpper("&@#$%^&*"))
+}
+
+func TestContainLetter(t *testing.T) {
+ assert := internal.NewAssert(t, "TestContainLetter")
+
+ assert.Equal(true, ContainLetter("ABC"))
+ assert.Equal(true, ContainLetter("1Bc"))
+ assert.Equal(true, ContainLetter("1ab"))
+ assert.Equal(true, ContainLetter("A&"))
+
+ assert.Equal(false, ContainLetter(""))
+ assert.Equal(false, ContainLetter("123"))
+ assert.Equal(false, ContainLetter("你好"))
+ assert.Equal(false, ContainLetter("&@#$%^&*"))
+}
+
+func TestIsJSON(t *testing.T) {
+ assert := internal.NewAssert(t, "TestIsJSON")
+
+ assert.Equal(true, IsJSON("{}"))
+ assert.Equal(true, IsJSON("{\"name\": \"test\"}"))
+ assert.Equal(true, IsJSON("[]"))
+ assert.Equal(true, IsJSON("123"))
+
+ assert.Equal(false, IsJSON(""))
+ assert.Equal(false, IsJSON("abc"))
+ assert.Equal(false, IsJSON("你好"))
+ assert.Equal(false, IsJSON("&@#$%^&*"))
+}
+
func TestIsNumberStr(t *testing.T) {
assert := internal.NewAssert(t, "TestIsNumberStr")
@@ -35,6 +125,18 @@ func TestIsIntStr(t *testing.T) {
assert.Equal(false, IsIntStr("abc"))
}
+func TestIsPort(t *testing.T) {
+ assert := internal.NewAssert(t, "TestIsPort")
+
+ assert.Equal(true, IsPort("1"))
+ assert.Equal(true, IsPort("65535"))
+ assert.Equal(false, IsPort("abc"))
+ assert.Equal(false, IsPort("123abc"))
+ assert.Equal(false, IsPort(""))
+ assert.Equal(false, IsPort("-1"))
+ assert.Equal(false, IsPort("65536"))
+}
+
func TestIsIp(t *testing.T) {
assert := internal.NewAssert(t, "TestIsIntStr")
From aeebd63eda46e39081c8872809e67ad8d0b404b5 Mon Sep 17 00:00:00 2001
From: Dan Anstis
Date: Fri, 14 Jan 2022 14:33:53 +1000
Subject: [PATCH 3/7] docs(readme): fix convertor import example (#23)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b42bed2..27605a9 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ func main() {
#### 1. convertor contains some functions for data convertion
- Support conversion between commonly used data types.
-- Usage: import "github.com/duke-git/lancet/cryptor"
+- Usage: import "github.com/duke-git/lancet/convertor"
```go
package main
From c424b88d40faab00ce4670176a653d5f39a013bc Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Sun, 16 Jan 2022 22:17:51 +0800
Subject: [PATCH 4/7] update style of readme file
---
README.md | 18 +++++++++---------
README_zh-CN.md | 22 +++++++++-------------
2 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/README.md b/README.md
index 27605a9..0a1f3c6 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
-
-
Lancet
-
- Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
-
-
+
Lancet
+
+ Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
+

[](https://github.com/duke-git/lancet/releases)
@@ -13,13 +11,12 @@
[](https://codecov.io/gh/duke-git/lancet)
[](https://github.com/duke-git/lancet/blob/main/LICENSE)
-
English | [简体中文](./README_zh-CN.md)
-
### Feature
+---
- 👏 Comprehensive, efficient and reusable.
- 💪 140+ common go util functions, support string, slice, datetime, net, crypt...
@@ -27,12 +24,14 @@ English | [简体中文](./README_zh-CN.md)
- 🌍 Unit test for every exported function.
### Installation
+---
```go
go get github.com/duke-git/lancet
```
### Usage
+---
Lancet organizes the code into package structure, and you need to import the corresponding package name when use it. For example, if you use string-related functions,import the strutil package like below:
```go
@@ -40,6 +39,7 @@ import "github.com/duke-git/lancet/strutil"
```
### Example
+---
Here takes the string function ReverseStr (reverse order string) as an example, and the strutil package needs to be imported.
@@ -59,7 +59,7 @@ func main() {
```
### API Documentation
-
+---
#### 1. convertor contains some functions for data convertion
- Support conversion between commonly used data types.
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 60a3fb4..09b1a95 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -1,9 +1,7 @@
-
-
Lancet
-
- lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
-
-
+
Lancet
+
+ lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
+

[](https://github.com/duke-git/lancet/releases)
@@ -13,27 +11,25 @@
[](https://codecov.io/gh/duke-git/lancet)
[](https://github.com/duke-git/lancet/blob/main/LICENSE)
-
简体中文 | [English](./README.md)
-
### 特性
-
+---
- 👏 全面、高效、可复用
- 💪 140+常用go工具函数,支持string、slice、datetime、net、crypt...
- 💅 只依赖go标准库
- 🌍 所有导出函数单元测试覆盖率100%
### 安装
-
+---
```go
go get github.com/duke-git/lancet
```
### 用法
-
+---
lancet是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入strutil包:
```go
@@ -41,7 +37,7 @@ import "github.com/duke-git/lancet/strutil"
```
### 例子
-
+---
此处以字符串工具函数ReverseStr(逆序字符串)为例,需要导入strutil包:
```go
@@ -60,7 +56,7 @@ func main() {
```
### API文档
-
+---
#### 1. convertor数据转换包
- 转换函数支持常用数据类型之间的转换
From f368854b2d540a4f43dc849897f51e58a4ce4da0 Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Mon, 17 Jan 2022 10:40:41 +0800
Subject: [PATCH 5/7] update text style of readme file
---
README.md | 40 ++++++++++++++++++----------------------
README_zh-CN.md | 43 +++++++++++++++++++++----------------------
2 files changed, 39 insertions(+), 44 deletions(-)
diff --git a/README.md b/README.md
index 0a1f3c6..9b8ce5d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Lancet
+# Lancet
Lancet is a comprehensive, efficient, and reusable util function library of go. Inspired by the java apache common package and lodash.js.
@@ -15,31 +15,28 @@
English | [简体中文](./README_zh-CN.md)
-### Feature
----
+## Feature
- 👏 Comprehensive, efficient and reusable.
- 💪 140+ common go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depend on the go standard library.
- 🌍 Unit test for every exported function.
-### Installation
----
+## Installation
```go
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:
```go
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.
@@ -58,9 +55,8 @@ func main() {
}
```
-### API Documentation
----
-#### 1. convertor contains some functions for data convertion
+## API Documentation
+### 1. convertor contains some functions for data convertion
- Support conversion between commonly used data types.
- Usage: import "github.com/duke-git/lancet/convertor"
@@ -98,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
```
-#### 2. cryptor is for data encryption and decryption
+### 2. cryptor is for data encryption and decryption
- Support md5, hmac, aes, des, ras.
- Usage: import "github.com/duke-git/lancet/cryptor"
@@ -157,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
- Usage: import "github.com/duke-git/lancet/datetime"
@@ -192,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
```
-#### 4. fileutil basic functions for file operations
+### 4. fileutil basic functions for file operations
- Basic functions for file operations.
- Usage: import "github.com/duke-git/lancet/fileutil"
@@ -229,7 +225,7 @@ func Zip(fpath string, destPath string) error //create zip file, fpath could be
func UnZip(zipFile string, destPath string) error //unzip the file and save it to destPath
```
-#### 5. formatter is for data format
+### 5. formatter is for data format
- Contain some formatting function
- Usage: import "github.com/duke-git/lancet/formatter"
@@ -254,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
```
-#### 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.
- Usage: import "github.com/duke-git/lancet/function"
@@ -290,7 +286,7 @@ 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.
- Usage: import "github.com/duke-git/lancet/netutil".
@@ -338,7 +334,7 @@ func ConvertMapToQueryString(param map[string]interface{}) string //convert map
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.
- Usage: import "github.com/duke-git/lancet/random".
@@ -367,7 +363,7 @@ func RandInt(min, max int) int //generate random int
func RandString(length int) string //generate random string
```
-#### 9. slice is for process slice
+### 9. slice is for process slice
- Contain function for process slice.
- Usage: import "github.com/duke-git/lancet/slice"
@@ -425,7 +421,7 @@ func GroupBy(slice, function interface{}) (interface{}, interface{}) // groups s
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
+### 10. strutil is for processing string
- Contain functions to precess string
- Usage: import "github.com/duke-git/lancet/strutil"
@@ -467,7 +463,7 @@ func Wrap(str string, wrapWith string) string //wrap a string with another strin
func Unwrap(str string, wrapToken string) string //unwrap a given string from anther string. will change str value
```
-#### 11. validator is for data validation
+### 11. validator is for data validation
- Contain function for data validation.
- Usage: import "github.com/duke-git/lancet/validator".
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 09b1a95..f17166c 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -1,4 +1,4 @@
-Lancet
+# Lancet
lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
@@ -15,29 +15,29 @@
简体中文 | [English](./README.md)
-### 特性
----
+## 特性
+
- 👏 全面、高效、可复用
- 💪 140+常用go工具函数,支持string、slice、datetime、net、crypt...
- 💅 只依赖go标准库
- 🌍 所有导出函数单元测试覆盖率100%
-### 安装
----
+## 安装
+
```go
go get github.com/duke-git/lancet
```
-### 用法
----
+## 用法
+
lancet是以包的结构组织代码的,使用时需要导入相应的包名。例如:如果使用字符串相关函数,需要导入strutil包:
```go
import "github.com/duke-git/lancet/strutil"
```
-### 例子
----
+## 例子
+
此处以字符串工具函数ReverseStr(逆序字符串)为例,需要导入strutil包:
```go
@@ -55,9 +55,8 @@ func main() {
}
```
-### API文档
----
-#### 1. convertor数据转换包
+## API文档
+### 1. convertor数据转换包
- 转换函数支持常用数据类型之间的转换
- 导入包:import "github.com/duke-git/lancet/convertor"
@@ -95,7 +94,7 @@ func ToString(value interface{}) string //interface转成string
func StructToMap(value interface{}) (map[string]interface{}, error) //struct串转成map, 需要设置struct tag `json`
```
-#### 2. cryptor加解密包
+### 2. cryptor加解密包
- 加密函数支持md5, hmac, aes, des, ras
- 导入包:import "github.com/duke-git/lancet/cryptor"
@@ -154,7 +153,7 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte //RSA解密
```
-#### 3. datetime日期时间处理包
+### 3. datetime日期时间处理包
- 处理日期时间
- 导入包:import "github.com/duke-git/lancet/datetime"
@@ -189,7 +188,7 @@ func FormatTimeToStr(t time.Time, format string) string //时间格式化字符
func FormatStrToTime(str, format string) time.Time //字符串转换成时间
```
-#### 4. fileutil文件处理包
+### 4. fileutil文件处理包
- 文件处理常用函数
- 导入包:import "github.com/duke-git/lancet/fileutil"
@@ -226,7 +225,7 @@ func Zip(fpath string, destPath string) error //压缩文件fpath参数可以是
func UnZip(zipFile string, destPath string) error //解压文件,并将文件存储在destPath目录中
```
-#### 5. formatter格式化处理包
+### 5. formatter格式化处理包
- 格式化相关处理函数
- 导入包:import "github.com/duke-git/lancet/formatter"
@@ -251,7 +250,7 @@ func main() {
func Comma(v interface{}, symbol string) string //用逗号每隔3位分割数字/字符串
```
-#### 6. function包可以控制函数执行,支持部分函数式编程
+### 6. function包可以控制函数执行,支持部分函数式编程
- 控制函数执行,支持部分函数式编程
- 导入包:import "github.com/duke-git/lancet/function"
@@ -287,7 +286,7 @@ func (w *Watcher) Reset() {} //重置代码watcher
func (w *Watcher) GetElapsedTime() time.Duration //get code excution elapsed time.
```
-#### 7. netutil网络处理包
+### 7. netutil网络处理包
- 处理ip, http请求相关函数
- 导入包:import "github.com/duke-git/lancet/netutil"
@@ -335,7 +334,7 @@ func ConvertMapToQueryString(param map[string]interface{}) string //将map转换
func ParseHttpResponse(resp *http.Response, obj interface{}) error //将http响应解码成特定interface
```
-#### 8. random随机数处理包
+### 8. random随机数处理包
- 生成和处理随机数
- 导入包:import "github.com/duke-git/lancet/random"
@@ -364,7 +363,7 @@ func RandInt(min, max int) int //生成随机int
func RandString(length int) string //生成随机string
```
-#### 9. slice切片操作包
+### 9. slice切片操作包
- 切片操作相关函数
- 导入包:import "github.com/duke-git/lancet/slice"
@@ -422,7 +421,7 @@ func GroupBy(slice, function interface{}) (interface{}, interface{}) //根据函
func Count(slice, function interface{}) int
```
-#### 10. strutil字符串处理包
+### 10. strutil字符串处理包
- 字符串操作相关函数
- 导入包:import "github.com/duke-git/lancet/strutil"
@@ -464,7 +463,7 @@ func Unwrap(str string, wrapToken string) string //解包裹字符串 Wrap("*abc
func SnakeCase(s string) string //字符串转为SnakeCase, "fooBar" -> "foo_bar"
```
-#### 11. validator验证器包
+### 11. validator验证器包
- 数据校验相关函数
- 导入包:import "github.com/duke-git/lancet/validator"
From f3749c52b910fbf74737878387a5ddbe651d5d2a Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Mon, 17 Jan 2022 11:54:03 +0800
Subject: [PATCH 6/7] feat: add system package
---
system/os.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++
system/os_test.go | 37 +++++++++++++++++++++++++
2 files changed, 106 insertions(+)
create mode 100644 system/os.go
create mode 100644 system/os_test.go
diff --git a/system/os.go b/system/os.go
new file mode 100644
index 0000000..7158bad
--- /dev/null
+++ b/system/os.go
@@ -0,0 +1,69 @@
+// 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 system 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) (err error, stdout, stderr string) {
+ var out bytes.Buffer
+ var errout bytes.Buffer
+
+ cmd := exec.Command("/bin/bash", "-c", command)
+ cmd.Stdout = &out
+ cmd.Stderr = &errout
+ err = cmd.Run()
+
+ if err != nil {
+ stderr = string(errout.Bytes())
+ }
+ stdout = string(out.Bytes())
+
+ return
+}
diff --git a/system/os_test.go b/system/os_test.go
new file mode 100644
index 0000000..4126d70
--- /dev/null
+++ b/system/os_test.go
@@ -0,0 +1,37 @@
+package system
+
+import (
+ "testing"
+
+ "github.com/duke-git/lancet/internal"
+)
+
+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"))
+}
+
+func TestExecCommand(t *testing.T) {
+ assert := internal.NewAssert(t, "TestExecCommand")
+
+ err, out, errout := ExecCommand("ls")
+ assert.IsNil(err)
+
+ err, out, errout = ExecCommand("abc")
+ t.Log("std out: ", out)
+ t.Log("std err: ", errout)
+ if err != nil {
+ t.Logf("error: %v\n", err)
+ }
+
+ assert.IsNotNil(err)
+}
From 764a6fe107efbb0aae231666a802960c5e3e054b Mon Sep 17 00:00:00 2001
From: dudaodong
Date: Mon, 17 Jan 2022 14:49:20 +0800
Subject: [PATCH 7/7] release v1.2.0
---
README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++---
README_zh-CN.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++---
system/os.go | 2 +-
3 files changed, 174 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 9b8ce5d..d132b58 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@

-[](https://github.com/duke-git/lancet/releases)
+[](https://github.com/duke-git/lancet/releases)
[](https://pkg.go.dev/github.com/duke-git/lancet)
[](https://goreportcard.com/report/github.com/duke-git/lancet)
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -18,7 +18,7 @@ English | [简体中文](./README_zh-CN.md)
## Feature
- 👏 Comprehensive, efficient and reusable.
-- 💪 140+ common go util functions, support string, slice, datetime, net, crypt...
+- 💪 160+ common go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depend on the go standard library.
- 🌍 Unit test for every exported function.
@@ -363,7 +363,49 @@ func RandInt(min, max int) int //generate random int
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.
- Usage: import "github.com/duke-git/lancet/slice"
@@ -421,7 +463,7 @@ func GroupBy(slice, function interface{}) (interface{}, interface{}) // groups s
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
- Usage: import "github.com/duke-git/lancet/strutil"
@@ -462,8 +504,41 @@ func SnakeCase(s string) string //covert string to snake_case "fooBar" -> "foo_b
func Wrap(str string, wrapWith string) string //wrap a string with another string.
func Unwrap(str string, wrapToken string) string //unwrap a given string from anther string. will change str value
```
+### 12. system contain some functions about os, runtime, shell command.
-### 11. validator is for data validation
+- Generate random string and int.
+- Usage: import "github.com/duke-git/lancet/system".
+
+```go
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "github.com/duke-git/lancet/system"
+)
+
+func main() {
+ envFoo := system.GetOsEnv("foo")
+ fmt.Println(envFoo)
+}
+```
+
+- Function list:
+
+```go
+func IsWindows() bool //check if current os is windows
+func IsLinux() bool //check if current os is linux
+func IsMac() bool //check if current os is macos
+func GetOsEnv(key string) string //gets the value of the environment variable named by the key.
+func SetOsEnv(key, value string) error //sets the value of the environment variable named by the key.
+func RemoveOsEnv(key string) error //remove a single environment variable.
+func CompareOsEnv(key, comparedEnv string) bool //gets env named by the key and compare it with comparedEnv
+func ExecCommand(command string) (err error, stdout, stderr string) //use shell /bin/bash -c to execute command
+```
+
+### 13. validator is for data validation
- Contain function for data validation.
- Usage: import "github.com/duke-git/lancet/validator".
@@ -491,6 +566,12 @@ func main() {
func ContainChinese(s string) bool //check if the string contain mandarin chinese
func IsAlpha(s string) bool //checks if the string contains only letters (a-zA-Z)
func IsBase64(base64 string) bool //check if the string is base64 string
+func 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 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
diff --git a/README_zh-CN.md b/README_zh-CN.md
index f17166c..96f8182 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -4,7 +4,7 @@

-[](https://github.com/duke-git/lancet/releases)
+[](https://github.com/duke-git/lancet/releases)
[](https://pkg.go.dev/github.com/duke-git/lancet)
[](https://goreportcard.com/report/github.com/duke-git/lancet)
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -18,7 +18,7 @@
## 特性
- 👏 全面、高效、可复用
-- 💪 140+常用go工具函数,支持string、slice、datetime、net、crypt...
+- 💪 160+常用go工具函数,支持string、slice、datetime、net、crypt...
- 💅 只依赖go标准库
- 🌍 所有导出函数单元测试覆盖率100%
@@ -363,7 +363,49 @@ func RandInt(min, max int) int //生成随机int
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"
@@ -421,7 +463,7 @@ func GroupBy(slice, function interface{}) (interface{}, interface{}) //根据函
func Count(slice, function interface{}) int
```
-### 10. strutil字符串处理包
+### 11. strutil字符串处理包
- 字符串操作相关函数
- 导入包:import "github.com/duke-git/lancet/strutil"
@@ -463,7 +505,41 @@ func Unwrap(str string, wrapToken string) string //解包裹字符串 Wrap("*abc
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) (err error, stdout, stderr string) //执行shell命令(/bin/bash)
+```
+
+### 13. validator验证器包
- 数据校验相关函数
- 导入包:import "github.com/duke-git/lancet/validator"
@@ -491,6 +567,12 @@ func main() {
func ContainChinese(s string) bool //判断字符串中是否含有中文字符
func IsAlpha(s string) bool //判断字符串是否只含有字母
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 IsChineseIdNum(id string) bool //判断字符串是否是身份证号
func IsChinesePhone(phone string) bool //判断字符串是否是座机电话号码
diff --git a/system/os.go b/system/os.go
index 7158bad..5e5d23b 100644
--- a/system/os.go
+++ b/system/os.go
@@ -16,7 +16,7 @@ func IsWindows() bool {
return runtime.GOOS == "windows"
}
-// IsLinux check if current os system is linux
+// IsLinux check if current os is linux
func IsLinux() bool {
return runtime.GOOS == "linux"
}