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. +

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) [![Release](https://img.shields.io/badge/release-1.1.10-green.svg)](https://github.com/duke-git/lancet/releases) @@ -13,13 +11,12 @@ [![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) -
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的启发。 +

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) [![Release](https://img.shields.io/badge/release-1.1.10-green.svg)](https://github.com/duke-git/lancet/releases) @@ -13,27 +11,25 @@ [![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) -
简体中文 | [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 @@

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.1.10-green.svg)](https://github.com/duke-git/lancet/releases) +[![Release](https://img.shields.io/badge/release-1.2.0-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) [![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) @@ -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 @@

![Go version](https://img.shields.io/badge/go-%3E%3D1.16-9cf) -[![Release](https://img.shields.io/badge/release-1.1.10-green.svg)](https://github.com/duke-git/lancet/releases) +[![Release](https://img.shields.io/badge/release-1.2.0-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) [![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) @@ -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" }