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

Compare commits

..

33 Commits

Author SHA1 Message Date
dudaodong
b7e5d946f1 release v2.1.12 2022-12-15 16:55:20 +08:00
dudaodong
687db4ce79 update readme file 2022-12-15 16:44:30 +08:00
dudaodong
a9a4bb8841 update readme file 2022-12-15 16:42:22 +08:00
dudaodong
bc6cb5f61b fix code issue in doc 2022-12-15 16:38:50 +08:00
dudaodong
2c57266f8e test: update some test functions 2022-12-15 15:22:01 +08:00
dudaodong
57e49c9520 doc: update document for funcations CamelCase/KebabCase/UpperKebabCase/SnakeCase/UpperSnakeCase 2022-12-14 21:42:43 +08:00
dudaodong
985c9a5d9a refactor: refact CamelCase function 2022-12-14 21:27:29 +08:00
dudaodong
5cfb11f036 feat: fix and add SnakeCase/UpperSnakeCase 2022-12-14 21:17:30 +08:00
dudaodong
5b3d48a1e7 feat: fix and add KebabCase/UpperKebabCase 2022-12-14 21:09:22 +08:00
dudaodong
d0576e028f clean code 2022-12-13 19:52:37 +08:00
dudaodong
76bdec2b54 test: add cases for Capitalize 2022-12-13 16:16:16 +08:00
dudaodong
fa20aba3a7 fix: fix CamelCase function bug 2022-12-13 14:23:32 +08:00
dudaodong
7a4a429e23 test: add cases for Capitalize 2022-12-12 21:11:19 +08:00
dudaodong
a70ec6ad1e refactor: use constraints from golang.org/x/exp/constraints 2022-12-11 11:25:34 +08:00
dudaodong
e435fa271b doc: update doc for Comma 2022-12-10 21:03:16 +08:00
dudaodong
1533d00891 refactor: update Comma function 2022-12-10 20:59:27 +08:00
dudaodong
8b99641de0 test: remove print info in test function 2022-12-10 19:19:49 +08:00
dudaodong
251f899f18 fix: TestExecCommand failed in linux/macos 2022-12-10 19:12:19 +08:00
dudaodong
00407e5182 fix: fix lint issue 2022-12-10 19:09:18 +08:00
dudaodong
4e457ad672 change: change update and insert function in link datastruture 2022-12-10 17:45:29 +08:00
dudaodong
7d8d9c3543 fix: fix lint issue 2022-12-10 16:55:06 +08:00
dudaodong
1197e8d1b6 fix: fix lint issue 2022-12-10 16:53:41 +08:00
dudaodong
13bbe19ab2 fix: fix lint issue 2022-12-10 16:41:40 +08:00
dudaodong
2725575d2f doc: add doc for IsGBK' 2022-12-09 19:28:45 +08:00
dudaodong
037d2729ce doc: update doc for ExecCommand 2022-12-09 19:20:15 +08:00
dudaodong
09d98745b0 fix: fix ExecCommand bug in windows 2022-12-09 19:10:55 +08:00
dudaodong
af5cfe6da1 feat: add IsGBK validator 2022-12-09 17:36:59 +08:00
Mickls
d59259bbe0 feat: A more reasonable IndexOf function (#66) 2022-12-09 11:31:40 +08:00
dudaodong
3189628d54 fix: fix AesCfbDecrypt 2022-12-08 15:07:40 +08:00
dudaodong
62c5e251a5 remove website 2022-12-08 14:55:21 +08:00
dudaodong
6e6444c8c0 refator: clean code 2022-12-06 20:20:36 +08:00
dudaodong
dd613e98b2 refator: clean code 2022-12-06 20:00:41 +08:00
dudaodong
2d905ab03e fix: fix word misspelling 2022-12-04 11:25:02 +08:00
64 changed files with 1290 additions and 729 deletions

View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.11-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.1.12-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![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) [![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)
@@ -19,7 +19,7 @@
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>
English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.io/lancet-docs/en/) English | [简体中文](./README_zh-CN.md)
## Feature ## Feature
@@ -38,10 +38,10 @@ English | [简体中文](./README_zh-CN.md) | [Website](https://uvdream.github.i
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
``` ```
2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.3.4. </b> 2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.3.5. </b>
```go ```go
go get github.com/duke-git/lancet@v1.3.3 // below go1.18, install latest version of v1.x.x go get github.com/duke-git/lancet@v1.3.5 // below go1.18, install latest version of v1.x.x
``` ```
## Usage ## Usage
@@ -371,7 +371,6 @@ import "github.com/duke-git/lancet/v2/netutil"
- [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#SendRequest) - [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#SendRequest)
- [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#DecodeResponse) - [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#DecodeResponse)
- [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#StructToUrlValues) - [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#StructToUrlValues)
- [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpGet) - [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpGet)
- [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpDelete) - [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpDelete)
- [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPost) - [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#HttpPost)
@@ -488,12 +487,14 @@ import "github.com/duke-git/lancet/v2/strutil"
- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Capitalize) - [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Capitalize)
- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#IsString) - [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#IsString)
- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#KebabCase) - [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#KebabCase)
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperKebabCase)
- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#LowerFirst) - [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#LowerFirst)
- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperFirst) - [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperFirst)
- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd) - [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd)
- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadStart) - [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadStart)
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Reverse) - [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Reverse)
- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SnakeCase) - [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SnakeCase)
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperSnakeCase)
- [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SplitEx) - [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SplitEx)
- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Wrap) - [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Wrap)
- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Unwrap) - [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Unwrap)
@@ -551,6 +552,7 @@ import "github.com/duke-git/lancet/v2/validator"
- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsUrl) - [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsUrl)
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsWeakPassword) - [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsWeakPassword)
- [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsZeroValue) - [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsZeroValue)
- [IsGBK](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsGBK)
### 20. xerror package implements helpers for errors. ### 20. xerror package implements helpers for errors.

View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.11-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.1.12-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![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) [![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 @@
lancet柳叶刀是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。 lancet柳叶刀是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。
</p> </p>
简体中文 | [English](./README.md) | [文档](https://uvdream.github.io/lancet-docs) 简体中文 | [English](./README.md)
## 特性 ## 特性
@@ -37,10 +37,10 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
``` ```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.4。</b> 2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.5。</b>
```go ```go
go get github.com/duke-git/lancet@v1.3.3 // 使用go1.18以下版本, 必须安装v1.x.x版本 go get github.com/duke-git/lancet@v1.3.5 // 使用go1.18以下版本, 必须安装v1.x.x版本
``` ```
## 用法 ## 用法
@@ -370,7 +370,6 @@ import "github.com/duke-git/lancet/v2/netutil"
- [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#SendRequest) - [SendRequest](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#SendRequest)
- [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#DecodeResponse) - [DecodeResponse](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#DecodeResponse)
- [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#StructToUrlValues) - [StructToUrlValues](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#StructToUrlValues)
- [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpGet) - [HttpGet<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpGet)
- [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpDelete) - [HttpDelete<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpDelete)
- [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPost) - [HttpPost<sup>deprecated</sup>](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPost)
@@ -485,12 +484,14 @@ import "github.com/duke-git/lancet/v2/strutil"
- [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Capitalize) - [Capitalize](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Capitalize)
- [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#IsString) - [IsString](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#IsString)
- [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#KebabCase) - [KebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#KebabCase)
- [UpperKebabCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperKebabCase)
- [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#LowerFirst) - [LowerFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#LowerFirst)
- [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperFirst) - [UpperFirst](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperFirst)
- [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd) - [PadEnd](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd)
- [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadStart) - [PadStart](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadStart)
- [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Reverse) - [Reverse](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Reverse)
- [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SnakeCase) - [SnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SnakeCase)
- [UpperSnakeCase](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperSnakeCase)
- [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitEx) - [SplitEx](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitEx)
- [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Wrap) - [Wrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Wrap)
- [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Unwrap) - [Unwrap](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Unwrap)
@@ -548,6 +549,7 @@ import "github.com/duke-git/lancet/v2/validator"
- [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsUrl) - [IsUrl](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsUrl)
- [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsWeakPassword) - [IsWeakPassword](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsWeakPassword)
- [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsZeroValue) - [IsZeroValue](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsZeroValue)
- [IsGBK](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsGBK)
### 20. xerror 包实现一些错误处理函数 ### 20. xerror 包实现一些错误处理函数

View File

@@ -158,7 +158,7 @@ func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-ch
var stream <-chan any var stream <-chan any
select { select {
case maybeStream, ok := <-chanStream: case maybeStream, ok := <-chanStream:
if ok == false { if !ok {
return return
} }
stream = maybeStream stream = maybeStream

View File

@@ -97,40 +97,43 @@ func ToString(value any) string {
return "" return ""
} }
switch value.(type) { switch val := value.(type) {
case float32: case float32:
return strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32) return strconv.FormatFloat(float64(val), 'f', -1, 32)
case float64: case float64:
return strconv.FormatFloat(value.(float64), 'f', -1, 64) return strconv.FormatFloat(val, 'f', -1, 64)
case int: case int:
return strconv.FormatInt(int64(value.(int)), 10) return strconv.FormatInt(int64(val), 10)
case int8: case int8:
return strconv.FormatInt(int64(value.(int8)), 10) return strconv.FormatInt(int64(val), 10)
case int16: case int16:
return strconv.FormatInt(int64(value.(int16)), 10) return strconv.FormatInt(int64(val), 10)
case int32: case int32:
return strconv.FormatInt(int64(value.(int32)), 10) return strconv.FormatInt(int64(val), 10)
case int64: case int64:
return strconv.FormatInt(value.(int64), 10) return strconv.FormatInt(val, 10)
case uint: case uint:
return strconv.FormatUint(uint64(value.(uint)), 10) return strconv.FormatUint(uint64(val), 10)
case uint8: case uint8:
return strconv.FormatUint(uint64(value.(uint8)), 10) return strconv.FormatUint(uint64(val), 10)
case uint16: case uint16:
return strconv.FormatUint(uint64(value.(uint16)), 10) return strconv.FormatUint(uint64(val), 10)
case uint32: case uint32:
return strconv.FormatUint(uint64(value.(uint32)), 10) return strconv.FormatUint(uint64(val), 10)
case uint64: case uint64:
return strconv.FormatUint(value.(uint64), 10) return strconv.FormatUint(val, 10)
case string: case string:
return value.(string) return val
case []byte: case []byte:
return string(value.([]byte)) return string(val)
default: default:
newValue, _ := json.Marshal(value) b, err := json.Marshal(val)
return string(newValue) if err != nil {
return ""
}
return string(b)
// todo: maybe we should't supprt other type convertion // todo: maybe we should't supprt other type conversion
// v := reflect.ValueOf(value) // v := reflect.ValueOf(value)
// log.Panicf("Unsupported data type: %s ", v.String()) // log.Panicf("Unsupported data type: %s ", v.String())
// return "" // return ""

View File

@@ -27,14 +27,9 @@ func TestToChannel(t *testing.T) {
assert := internal.NewAssert(t, "TestToChannel") assert := internal.NewAssert(t, "TestToChannel")
ch := ToChannel([]int{1, 2, 3}) ch := ToChannel([]int{1, 2, 3})
val1, _ := <-ch assert.Equal(1, <-ch)
assert.Equal(1, val1) assert.Equal(2, <-ch)
assert.Equal(3, <-ch)
val2, _ := <-ch
assert.Equal(2, val2)
val3, _ := <-ch
assert.Equal(3, val3)
_, ok := <-ch _, ok := <-ch
assert.Equal(false, ok) assert.Equal(false, ok)
@@ -254,6 +249,7 @@ func TestDecodeByte(t *testing.T) {
var obj string var obj string
byteData := []byte{6, 12, 0, 3, 97, 98, 99} byteData := []byte{6, 12, 0, 3, 97, 98, 99}
DecodeByte(byteData, &obj) err := DecodeByte(byteData, &obj)
assert.IsNil(err)
assert.Equal("abc", obj) assert.Equal("abc", obj)
} }

View File

@@ -17,15 +17,19 @@ import (
// AesEcbEncrypt encrypt data with key use AES ECB algorithm // AesEcbEncrypt encrypt data with key use AES ECB algorithm
// len(key) should be 16, 24 or 32 // len(key) should be 16, 24 or 32
func AesEcbEncrypt(data, key []byte) []byte { func AesEcbEncrypt(data, key []byte) []byte {
cipher, _ := aes.NewCipher(generateAesKey(key))
length := (len(data) + aes.BlockSize) / aes.BlockSize length := (len(data) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize) plain := make([]byte, length*aes.BlockSize)
copy(plain, data) copy(plain, data)
pad := byte(len(plain) - len(data)) pad := byte(len(plain) - len(data))
for i := len(data); i < len(plain); i++ { for i := len(data); i < len(plain); i++ {
plain[i] = pad plain[i] = pad
} }
encrypted := make([]byte, len(plain)) encrypted := make([]byte, len(plain))
cipher, _ := aes.NewCipher(generateAesKey(key))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be]) cipher.Encrypt(encrypted[bs:be], plain[bs:be])
} }
@@ -108,27 +112,32 @@ func AesCfbEncrypt(data, key []byte) []byte {
encrypted := make([]byte, aes.BlockSize+len(data)) encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err) panic(err)
} }
stream := cipher.NewCFBEncrypter(block, iv) stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data) stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted return encrypted
} }
// AesCfbDecrypt decrypt data with key use AES CFB algorithm // AesCfbDecrypt decrypt data with key use AES CFB algorithm
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32 // len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
func AesCfbDecrypt(encrypted, key []byte) []byte { func AesCfbDecrypt(encrypted, key []byte) []byte {
block, _ := aes.NewCipher(key)
if len(encrypted) < aes.BlockSize { if len(encrypted) < aes.BlockSize {
panic("encrypted data is too short") panic("encrypted data is too short")
} }
block, _ := aes.NewCipher(key)
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:] encrypted = encrypted[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv) stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted) stream.XORKeyStream(encrypted, encrypted)
return encrypted return encrypted
} }
@@ -139,6 +148,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
if err != nil { if err != nil {
panic(err) panic(err)
} }
data = pkcs7Padding(data, aes.BlockSize) data = pkcs7Padding(data, aes.BlockSize)
encrypted := make([]byte, aes.BlockSize+len(data)) encrypted := make([]byte, aes.BlockSize+len(data))
iv := encrypted[:aes.BlockSize] iv := encrypted[:aes.BlockSize]
@@ -148,6 +158,7 @@ func AesOfbEncrypt(data, key []byte) []byte {
stream := cipher.NewOFB(block, iv) stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encrypted[aes.BlockSize:], data) stream.XORKeyStream(encrypted[aes.BlockSize:], data)
return encrypted return encrypted
} }
@@ -170,5 +181,6 @@ func AesOfbDecrypt(data, key []byte) []byte {
mode.XORKeyStream(decrypted, data) mode.XORKeyStream(decrypted, data)
decrypted = pkcs7UnPadding(decrypted) decrypted = pkcs7UnPadding(decrypted)
return decrypted return decrypted
} }

View File

@@ -15,7 +15,6 @@ import (
// DesEcbEncrypt encrypt data with key use DES ECB algorithm // DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8 // len(key) should be 8
func DesEcbEncrypt(data, key []byte) []byte { func DesEcbEncrypt(data, key []byte) []byte {
cipher, _ := des.NewCipher(generateDesKey(key))
length := (len(data) + des.BlockSize) / des.BlockSize length := (len(data) + des.BlockSize) / des.BlockSize
plain := make([]byte, length*des.BlockSize) plain := make([]byte, length*des.BlockSize)
copy(plain, data) copy(plain, data)
@@ -26,6 +25,8 @@ func DesEcbEncrypt(data, key []byte) []byte {
} }
encrypted := make([]byte, len(plain)) encrypted := make([]byte, len(plain))
cipher, _ := des.NewCipher(generateDesKey(key))
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be]) cipher.Encrypt(encrypted[bs:be], plain[bs:be])
} }
@@ -59,6 +60,7 @@ func DesCbcEncrypt(data, key []byte) []byte {
encrypted := make([]byte, des.BlockSize+len(data)) encrypted := make([]byte, des.BlockSize+len(data))
iv := encrypted[:des.BlockSize] iv := encrypted[:des.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err) panic(err)
} }

View File

@@ -33,7 +33,11 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
pem.Encode(file, &block) err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close() file.Close()
// public key // public key
@@ -49,12 +53,16 @@ func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
Bytes: derpText, Bytes: derpText,
} }
//file,err = os.Create("rsa_public.pem")
file, err = os.Create(pubKeyFile) file, err = os.Create(pubKeyFile)
if err != nil { if err != nil {
return err return err
} }
pem.Encode(file, &block)
err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close() file.Close()
return nil return nil
@@ -72,7 +80,11 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
} }
defer file.Close() defer file.Close()
buf := make([]byte, fileInfo.Size()) buf := make([]byte, fileInfo.Size())
file.Read(buf)
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf) block, _ := pem.Decode(buf)
@@ -101,7 +113,11 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
} }
buf := make([]byte, fileInfo.Size()) buf := make([]byte, fileInfo.Size())
defer file.Close() defer file.Close()
file.Read(buf)
_, err = file.Read(buf)
if err != nil {
panic(err)
}
block, _ := pem.Decode(buf) block, _ := pem.Decode(buf)

View File

@@ -146,9 +146,9 @@ func (h *MaxHeap[T]) PrintStructure() {
lastNum := powerTwo(level - 1) lastNum := powerTwo(level - 1)
lastLen := lastNum + (lastNum - 1) lastLen := lastNum + (lastNum - 1)
heapTree := make([][]string, level, level) heapTree := make([][]string, level)
for i := 0; i < level; i++ { for i := 0; i < level; i++ {
heapTree[i] = make([]string, lastLen, lastLen) heapTree[i] = make([]string, lastLen)
for j := 0; j < lastLen; j++ { for j := 0; j < lastLen; j++ {
heapTree[i][j] = "" heapTree[i][j] = ""
} }
@@ -169,9 +169,9 @@ func (h *MaxHeap[T]) PrintStructure() {
for n := 0; n < lastLen; n++ { for n := 0; n < lastLen; n++ {
val := heapTree[m][n] val := heapTree[m][n]
if val == "" { if val == "" {
fmt.Printf(" ") fmt.Print(" ")
} else { } else {
fmt.Printf(val) fmt.Print(val)
} }
} }
fmt.Println() fmt.Println()

View File

@@ -30,8 +30,6 @@ func TestMaxHeap_BuildMaxHeap(t *testing.T) {
assert.Equal(expected, heap.data) assert.Equal(expected, heap.data)
assert.Equal(12, heap.Size()) assert.Equal(12, heap.Size())
heap.PrintStructure()
} }
func TestMaxHeap_Push(t *testing.T) { func TestMaxHeap_Push(t *testing.T) {

View File

@@ -1,13 +1,12 @@
package datastructure package datastructure
import ( import (
"errors"
"fmt" "fmt"
"github.com/duke-git/lancet/v2/datastructure" "github.com/duke-git/lancet/v2/datastructure"
) )
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the link, Next pointer points to a next node of the link. // DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl.
type DoublyLink[T any] struct { type DoublyLink[T any] struct {
Head *datastructure.LinkNode[T] Head *datastructure.LinkNode[T]
length int length int
@@ -19,30 +18,30 @@ func NewDoublyLink[T any]() *DoublyLink[T] {
} }
// InsertAtHead insert value into doubly linklist at head index // InsertAtHead insert value into doubly linklist at head index
func (link *DoublyLink[T]) InsertAtHead(value T) { func (dl *DoublyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
size := link.Size() size := dl.Size()
if size == 0 { if size == 0 {
link.Head = newNode dl.Head = newNode
link.length++ dl.length++
return return
} }
newNode.Next = link.Head newNode.Next = dl.Head
newNode.Pre = nil newNode.Pre = nil
link.Head.Pre = newNode dl.Head.Pre = newNode
link.Head = newNode dl.Head = newNode
link.length++ dl.length++
} }
// InsertAtTail insert value into doubly linklist at tail index // InsertAtTail insert value into doubly linklist at tail index
func (link *DoublyLink[T]) InsertAtTail(value T) { func (dl *DoublyLink[T]) InsertAtTail(value T) {
current := link.Head current := dl.Head
if current == nil { if current == nil {
link.InsertAtHead(value) dl.InsertAtHead(value)
return return
} }
@@ -55,28 +54,29 @@ func (link *DoublyLink[T]) InsertAtTail(value T) {
newNode.Pre = current newNode.Pre = current
current.Next = newNode current.Next = newNode
link.length++ dl.length++
} }
// InsertAt insert value into doubly linklist at index // InsertAt insert value into doubly linklist at index
func (link *DoublyLink[T]) InsertAt(index int, value T) error { // param `index` should between [0, length], if index do not meet the conditions, do nothing
size := link.length func (dl *DoublyLink[T]) InsertAt(index int, value T) {
size := dl.length
if index < 0 || index > size { if index < 0 || index > size {
return errors.New("param index should between 0 and the length of doubly link.") return
} }
if index == 0 { if index == 0 {
link.InsertAtHead(value) dl.InsertAtHead(value)
return nil return
} }
if index == size { if index == size {
link.InsertAtTail(value) dl.InsertAtTail(value)
return nil return
} }
i := 0 i := 0
current := link.Head current := dl.Head
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
@@ -85,38 +85,36 @@ func (link *DoublyLink[T]) InsertAt(index int, value T) error {
newNode.Pre = current newNode.Pre = current
current.Next = newNode current.Next = newNode
link.length++ dl.length++
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("doubly link list no exist")
} }
// DeleteAtHead delete value in doubly linklist at head index // DeleteAtHead delete value in doubly linklist at head index
func (link *DoublyLink[T]) DeleteAtHead() error { func (dl *DoublyLink[T]) DeleteAtHead() {
if link.Head == nil { if dl.Head == nil {
return errors.New("doubly link list no exist") return
}
current := link.Head
link.Head = current.Next
link.Head.Pre = nil
link.length--
return nil
} }
// DeleteAtTail delete value in doubly linklist at tail index current := dl.Head
func (link *DoublyLink[T]) DeleteAtTail() error { dl.Head = current.Next
if link.Head == nil { dl.Head.Pre = nil
return errors.New("doubly link list no exist") dl.length--
} }
current := link.Head
// DeleteAtTail delete value in doubly linklist at tail
func (dl *DoublyLink[T]) DeleteAtTail() {
if dl.Head == nil {
return
}
current := dl.Head
if current.Next == nil { if current.Next == nil {
return link.DeleteAtHead() dl.DeleteAtHead()
} }
for current.Next.Next != nil { for current.Next.Next != nil {
@@ -124,45 +122,44 @@ func (link *DoublyLink[T]) DeleteAtTail() error {
} }
current.Next = nil current.Next = nil
link.length-- dl.length--
return nil
} }
// DeleteAt delete value in doubly linklist at index // DeleteAt delete value in doubly linklist at index
func (link *DoublyLink[T]) DeleteAt(index int) error { // param `index` should be [0, len(DoublyLink)-1]
if link.Head == nil { func (dl *DoublyLink[T]) DeleteAt(index int) {
return errors.New("doubly link list no exist") if dl.Head == nil {
return
} }
current := link.Head
current := dl.Head
if current.Next == nil || index == 0 { if current.Next == nil || index == 0 {
return link.DeleteAtHead() dl.DeleteAtHead()
} }
if index == link.length-1 { if index == dl.length-1 {
return link.DeleteAtTail() dl.DeleteAtTail()
} }
if index < 0 || index > link.length-1 { if index < 0 || index > dl.length-1 {
return errors.New("param index should between 0 and link size -1.") return
} }
i := 0 i := 0
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- dl.length--
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("delete error")
} }
// Reverse the linked list // Reverse the linked list
func (link *DoublyLink[T]) Reverse() { func (dl *DoublyLink[T]) Reverse() {
current := link.Head current := dl.Head
var temp *datastructure.LinkNode[T] var temp *datastructure.LinkNode[T]
for current != nil { for current != nil {
@@ -173,20 +170,20 @@ func (link *DoublyLink[T]) Reverse() {
} }
if temp != nil { if temp != nil {
link.Head = temp.Pre dl.Head = temp.Pre
} }
} }
// GetMiddleNode return node at middle index of linked list // GetMiddleNode return node at middle index of linked list
func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if link.Head == nil { if dl.Head == nil {
return nil return nil
} }
if link.Head.Next == nil { if dl.Head.Next == nil {
return link.Head return dl.Head
} }
fast := link.Head fast := dl.Head
slow := link.Head slow := dl.Head
for fast != nil { for fast != nil {
fast = fast.Next fast = fast.Next
@@ -202,14 +199,14 @@ func (link *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
} }
// Size return the count of doubly linked list // Size return the count of doubly linked list
func (link *DoublyLink[T]) Size() int { func (dl *DoublyLink[T]) Size() int {
return link.length return dl.length
} }
// Values return slice of all doubly linklist node value // Values return slice of all doubly linklist node value
func (link *DoublyLink[T]) Values() []T { func (dl *DoublyLink[T]) Values() []T {
result := []T{} result := []T{}
current := link.Head current := dl.Head
for current != nil { for current != nil {
result = append(result, current.Value) result = append(result, current.Value)
current = current.Next current = current.Next
@@ -218,8 +215,8 @@ func (link *DoublyLink[T]) Values() []T {
} }
// Print all nodes info of a linked list // Print all nodes info of a linked list
func (link *DoublyLink[T]) Print() { func (dl *DoublyLink[T]) Print() {
current := link.Head current := dl.Head
info := "[ " info := "[ "
for current != nil { for current != nil {
info += fmt.Sprintf("%+v, ", current) info += fmt.Sprintf("%+v, ", current)
@@ -229,13 +226,13 @@ func (link *DoublyLink[T]) Print() {
fmt.Println(info) fmt.Println(info)
} }
// IsEmpty checks if link is empty or not // IsEmpty checks if dl is empty or not
func (link *DoublyLink[T]) IsEmpty() bool { func (dl *DoublyLink[T]) IsEmpty() bool {
return link.length == 0 return dl.length == 0
} }
// Clear all nodes in doubly linklist // Clear all nodes in doubly linklist
func (link *DoublyLink[T]) Clear() { func (dl *DoublyLink[T]) Clear() {
link.Head = nil dl.Head = nil
link.length = 0 dl.length = 0
} }

View File

@@ -41,29 +41,24 @@ func TestDoublyLink_InsertAt(t *testing.T) {
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.InsertAt(1, 1) link.InsertAt(1, 1) //do nothing
assert.IsNotNil(err)
link.InsertAt(0, 1) link.InsertAt(0, 1)
link.InsertAt(1, 2) link.InsertAt(1, 2)
link.InsertAt(2, 4) link.InsertAt(2, 4)
link.InsertAt(2, 3) link.InsertAt(2, 3)
link.Print()
expected := []int{1, 2, 3, 4} expected := []int{1, 2, 3, 4}
values := link.Values() values := link.Values()
assert.Equal(expected, values) assert.Equal(expected, values)
} }
func TestDoublyLink_DeleteAtHead(t *testing.T) { func TestDoublyLink_DeleteAtHead(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAtHead() link.DeleteAtHead()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -71,7 +66,6 @@ func TestDoublyLink_DeleteAtHead(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtHead() link.DeleteAtHead()
link.Print()
expected := []int{2, 3, 4} expected := []int{2, 3, 4}
values := link.Values() values := link.Values()
@@ -83,8 +77,7 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAtTail() link.DeleteAtTail()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -92,7 +85,6 @@ func TestDoublyLink_DeleteAtTail(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtTail() link.DeleteAtTail()
link.Print()
expected := []int{1, 2, 3} expected := []int{1, 2, 3}
values := link.Values() values := link.Values()
@@ -104,8 +96,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt") assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt")
link := NewDoublyLink[int]() link := NewDoublyLink[int]()
err := link.DeleteAt(0) link.DeleteAt(0)
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -113,11 +104,7 @@ func TestDoublyLink_DeleteAt(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.InsertAtTail(5) link.InsertAtTail(5)
err = link.DeleteAt(5) link.DeleteAt(0)
assert.IsNotNil(err)
err = link.DeleteAt(0)
assert.IsNil(err)
assert.Equal([]int{2, 3, 4, 5}, link.Values()) assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3) link.DeleteAt(3)

View File

@@ -1,14 +1,13 @@
package datastructure package datastructure
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"github.com/duke-git/lancet/v2/datastructure" "github.com/duke-git/lancet/v2/datastructure"
) )
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the link. // SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl.
type SinglyLink[T any] struct { type SinglyLink[T any] struct {
Head *datastructure.LinkNode[T] Head *datastructure.LinkNode[T]
length int length int
@@ -20,18 +19,18 @@ func NewSinglyLink[T any]() *SinglyLink[T] {
} }
// InsertAtHead insert value into singly linklist at head index // InsertAtHead insert value into singly linklist at head index
func (link *SinglyLink[T]) InsertAtHead(value T) { func (sl *SinglyLink[T]) InsertAtHead(value T) {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
newNode.Next = link.Head newNode.Next = sl.Head
link.Head = newNode sl.Head = newNode
link.length++ sl.length++
} }
// InsertAtTail insert value into singly linklist at tail index // InsertAtTail insert value into singly linklist at tail index
func (link *SinglyLink[T]) InsertAtTail(value T) { func (sl *SinglyLink[T]) InsertAtTail(value T) {
current := link.Head current := sl.Head
if current == nil { if current == nil {
link.InsertAtHead(value) sl.InsertAtHead(value)
return return
} }
@@ -43,65 +42,63 @@ func (link *SinglyLink[T]) InsertAtTail(value T) {
newNode.Next = nil newNode.Next = nil
current.Next = newNode current.Next = newNode
link.length++ sl.length++
} }
// InsertAt insert value into singly linklist at index // InsertAt insert value into singly linklist at index
func (link *SinglyLink[T]) InsertAt(index int, value T) error { // param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing
size := link.length func (sl *SinglyLink[T]) InsertAt(index int, value T) {
size := sl.length
if index < 0 || index > size { if index < 0 || index > size {
return errors.New("param index should between 0 and the length of singly link.") return
} }
if index == 0 { if index == 0 {
link.InsertAtHead(value) sl.InsertAtHead(value)
return nil return
} }
if index == size { if index == size {
link.InsertAtTail(value) sl.InsertAtTail(value)
return nil return
} }
i := 0 i := 0
current := link.Head current := sl.Head
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
newNode := datastructure.NewLinkNode(value) newNode := datastructure.NewLinkNode(value)
newNode.Next = current.Next newNode.Next = current.Next
current.Next = newNode current.Next = newNode
link.length++ sl.length++
return
return nil
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("singly link list no exist")
} }
// DeleteAtHead delete value in singly linklist at head index // DeleteAtHead delete value in singly linklist at head index
func (link *SinglyLink[T]) DeleteAtHead() error { func (sl *SinglyLink[T]) DeleteAtHead() {
if link.Head == nil { if sl.Head == nil {
return errors.New("singly link list no exist") return
}
current := link.Head
link.Head = current.Next
link.length--
return nil
} }
// DeleteAtTail delete value in singly linklist at tail index current := sl.Head
func (link *SinglyLink[T]) DeleteAtTail() error { sl.Head = current.Next
if link.Head == nil { sl.length--
return errors.New("singly link list no exist")
} }
current := link.Head
// DeleteAtTail delete value in singly linklist at tail
func (sl *SinglyLink[T]) DeleteAtTail() {
if sl.Head == nil {
return
}
current := sl.Head
if current.Next == nil { if current.Next == nil {
return link.DeleteAtHead() sl.DeleteAtHead()
} }
for current.Next.Next != nil { for current.Next.Next != nil {
@@ -109,68 +106,66 @@ func (link *SinglyLink[T]) DeleteAtTail() error {
} }
current.Next = nil current.Next = nil
link.length-- sl.length--
return nil
} }
// DeleteAt delete value in singly linklist at index // DeleteAt delete value in singly linklist at index
func (link *SinglyLink[T]) DeleteAt(index int) error { // param `index` should be [0, len(SinglyLink)-1]
if link.Head == nil { func (sl *SinglyLink[T]) DeleteAt(index int) {
return errors.New("singly link list no exist") if sl.Head == nil {
return
} }
current := link.Head current := sl.Head
if current.Next == nil || index == 0 { if current.Next == nil || index == 0 {
return link.DeleteAtHead() sl.DeleteAtHead()
} }
if index == link.length-1 { if index == sl.length-1 {
return link.DeleteAtTail() sl.DeleteAtTail()
} }
if index < 0 || index > link.length-1 { if index < 0 || index > sl.length-1 {
return errors.New("param index should between 0 and link size -1.") return
} }
i := 0 i := 0
for current != nil { for current != nil {
if i == index-1 { if i == index-1 {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- sl.length--
return nil return
} }
i++ i++
current = current.Next current = current.Next
} }
return errors.New("delete error")
} }
// DeleteValue delete value in singly linklist // DeleteValue delete value in singly linklist
func (link *SinglyLink[T]) DeleteValue(value T) { func (sl *SinglyLink[T]) DeleteValue(value T) {
if link.Head == nil { if sl.Head == nil {
return return
} }
dummyHead := datastructure.NewLinkNode(value) dummyHead := datastructure.NewLinkNode(value)
dummyHead.Next = link.Head dummyHead.Next = sl.Head
current := dummyHead current := dummyHead
for current.Next != nil { for current.Next != nil {
if reflect.DeepEqual(current.Next.Value, value) { if reflect.DeepEqual(current.Next.Value, value) {
current.Next = current.Next.Next current.Next = current.Next.Next
link.length-- sl.length--
} else { } else {
current = current.Next current = current.Next
} }
} }
link.Head = dummyHead.Next sl.Head = dummyHead.Next
} }
// Reverse the linked list // Reverse the linked list
func (link *SinglyLink[T]) Reverse() { func (sl *SinglyLink[T]) Reverse() {
var pre, next *datastructure.LinkNode[T] var pre, next *datastructure.LinkNode[T]
current := link.Head current := sl.Head
for current != nil { for current != nil {
next = current.Next next = current.Next
@@ -179,19 +174,19 @@ func (link *SinglyLink[T]) Reverse() {
current = next current = next
} }
link.Head = pre sl.Head = pre
} }
// GetMiddleNode return node at middle index of linked list // GetMiddleNode return node at middle index of linked list
func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] { func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
if link.Head == nil { if sl.Head == nil {
return nil return nil
} }
if link.Head.Next == nil { if sl.Head.Next == nil {
return link.Head return sl.Head
} }
fast := link.Head fast := sl.Head
slow := link.Head slow := sl.Head
for fast != nil { for fast != nil {
fast = fast.Next fast = fast.Next
@@ -207,14 +202,14 @@ func (link *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
} }
// Size return the count of singly linked list // Size return the count of singly linked list
func (link *SinglyLink[T]) Size() int { func (sl *SinglyLink[T]) Size() int {
return link.length return sl.length
} }
// Values return slice of all singly linklist node value // Values return slice of all singly linklist node value
func (link *SinglyLink[T]) Values() []T { func (sl *SinglyLink[T]) Values() []T {
result := []T{} result := []T{}
current := link.Head current := sl.Head
for current != nil { for current != nil {
result = append(result, current.Value) result = append(result, current.Value)
current = current.Next current = current.Next
@@ -222,20 +217,20 @@ func (link *SinglyLink[T]) Values() []T {
return result return result
} }
// IsEmpty checks if link is empty or not // IsEmpty checks if sl is empty or not
func (link *SinglyLink[T]) IsEmpty() bool { func (sl *SinglyLink[T]) IsEmpty() bool {
return link.length == 0 return sl.length == 0
} }
// Clear all the node in singly linklist // Clear all the node in singly linklist
func (link *SinglyLink[T]) Clear() { func (sl *SinglyLink[T]) Clear() {
link.Head = nil sl.Head = nil
link.length = 0 sl.length = 0
} }
// Print all nodes info of a linked list // Print all nodes info of a linked list
func (link *SinglyLink[T]) Print() { func (sl *SinglyLink[T]) Print() {
current := link.Head current := sl.Head
info := "[ " info := "[ "
for current != nil { for current != nil {
info += fmt.Sprintf("%+v, ", current) info += fmt.Sprintf("%+v, ", current)

View File

@@ -41,25 +41,12 @@ func TestSinglyLink_InsertAt(t *testing.T) {
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.InsertAt(1, 1) link.InsertAt(1, 1) //do nothing
assert.IsNotNil(err)
err = link.InsertAt(0, 1) link.InsertAt(0, 1)
if err != nil { link.InsertAt(1, 2)
t.FailNow() link.InsertAt(2, 4)
} link.InsertAt(2, 3)
err = link.InsertAt(1, 2)
if err != nil {
t.FailNow()
}
err = link.InsertAt(2, 4)
if err != nil {
t.FailNow()
}
err = link.InsertAt(2, 3)
if err != nil {
t.FailNow()
}
link.Print() link.Print()
@@ -73,8 +60,8 @@ func TestSinglyLink_DeleteAtHead(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAtHead()
assert.IsNotNil(err) link.DeleteAtHead()
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -94,8 +81,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAtTail()
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -103,7 +88,6 @@ func TestSinglyLink_DeleteAtTail(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.DeleteAtTail() link.DeleteAtTail()
link.Print()
expected := []int{1, 2, 3} expected := []int{1, 2, 3}
values := link.Values() values := link.Values()
@@ -133,8 +117,6 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt") assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt")
link := NewSinglyLink[int]() link := NewSinglyLink[int]()
err := link.DeleteAt(0)
assert.IsNotNil(err)
link.InsertAtTail(1) link.InsertAtTail(1)
link.InsertAtTail(2) link.InsertAtTail(2)
@@ -142,11 +124,7 @@ func TestSinglyLink_DeleteAt(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.InsertAtTail(5) link.InsertAtTail(5)
err = link.DeleteAt(5) link.DeleteAt(0)
assert.IsNotNil(err)
err = link.DeleteAt(0)
assert.IsNil(err)
assert.Equal([]int{2, 3, 4, 5}, link.Values()) assert.Equal([]int{2, 3, 4, 5}, link.Values())
link.DeleteAt(3) link.DeleteAt(3)
@@ -167,7 +145,6 @@ func TestSinglyLink_Reverse(t *testing.T) {
link.InsertAtTail(4) link.InsertAtTail(4)
link.Reverse() link.Reverse()
link.Print()
assert.Equal([]int{4, 3, 2, 1}, link.Values()) assert.Equal([]int{4, 3, 2, 1}, link.Values())
} }

View File

@@ -156,7 +156,7 @@ func (l *List[T]) DeleteAt(index int) {
return return
} }
if index == size-1 { if index == size-1 {
data = append(data[:index]) data = data[:index]
} else { } else {
data = append(data[:index], data[index+1:]...) data = append(data[:index], data[index+1:]...)
} }
@@ -174,7 +174,7 @@ func (l *List[T]) DeleteIf(f func(T) bool) int {
continue continue
} }
if index == size-1 { if index == size-1 {
data = append(data[:index]) data = data[:index]
} else { } else {
data = append(data[:index], data[index+1:]...) data = append(data[:index], data[index+1:]...)
index-- index--
@@ -221,7 +221,7 @@ func (l *List[T]) IsEmpty() bool {
// Clear the data of list // Clear the data of list
func (l *List[T]) Clear() { func (l *List[T]) Clear() {
l.data = make([]T, 0, 0) l.data = make([]T, 0)
} }
// Clone return a copy of list // Clone return a copy of list
@@ -235,7 +235,7 @@ func (l *List[T]) Clone() *List[T] {
// Merge two list, return new list, don't change original list // Merge two list, return new list, don't change original list
func (l *List[T]) Merge(other *List[T]) *List[T] { func (l *List[T]) Merge(other *List[T]) *List[T] {
l1, l2 := len(l.data), len(other.data) l1, l2 := len(l.data), len(other.data)
ml := NewList(make([]T, l1+l2, l1+l2)) ml := NewList(make([]T, l1+l2))
data := append([]T{}, append(l.data, other.data...)...) data := append([]T{}, append(l.data, other.data...)...)
ml.data = data ml.data = data
@@ -274,7 +274,7 @@ func (l *List[T]) Unique() {
data := l.data data := l.data
size := len(data) size := len(data)
uniqueData := make([]T, 0, 0) uniqueData := make([]T, 0)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
value := data[i] value := data[i]
skip := true skip := true
@@ -305,7 +305,7 @@ func (l *List[T]) Union(other *List[T]) *List[T] {
// Intersection creates a new list whose element both be contained in list l and other // Intersection creates a new list whose element both be contained in list l and other
func (l *List[T]) Intersection(other *List[T]) *List[T] { func (l *List[T]) Intersection(other *List[T]) *List[T] {
result := NewList(make([]T, 0, 0)) result := NewList(make([]T, 0))
for _, v := range l.data { for _, v := range l.data {
if other.Contain(v) { if other.Contain(v) {

View File

@@ -18,8 +18,6 @@ func TestArrayQueue_Enqueue(t *testing.T) {
data := queue.Data() data := queue.Data()
size := queue.Size() size := queue.Size()
queue.Print()
assert.Equal(expected, data) assert.Equal(expected, data)
assert.Equal(3, size) assert.Equal(3, size)
} }
@@ -35,7 +33,6 @@ func TestArrayQueue_Dequeue(t *testing.T) {
val, ok := queue.Dequeue() val, ok := queue.Dequeue()
assert.Equal(true, ok) assert.Equal(true, ok)
queue.Print()
assert.Equal(1, val) assert.Equal(1, val)
assert.Equal([]int{2, 3}, queue.Data()) assert.Equal([]int{2, 3}, queue.Data())
} }
@@ -50,8 +47,6 @@ func TestArrayQueue_Front(t *testing.T) {
val := queue.Front() val := queue.Front()
queue.Print()
assert.Equal(1, val) assert.Equal(1, val)
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
} }
@@ -66,8 +61,6 @@ func TestArrayQueue_Back(t *testing.T) {
val := queue.Back() val := queue.Back()
queue.Print()
assert.Equal(3, val) assert.Equal(3, val)
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
} }

View File

@@ -10,31 +10,43 @@ func TestCircularQueue_Enqueue(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Enqueue") assert := internal.NewAssert(t, "TestCircularQueue_Enqueue")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](6)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err := queue.Enqueue(1)
assert.IsNil(err)
err = queue.Enqueue(2)
assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
err = queue.Enqueue(4)
assert.IsNil(err)
err = queue.Enqueue(5)
assert.IsNil(err)
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data()) assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
assert.Equal(5, queue.Size()) assert.Equal(5, queue.Size())
err := queue.Enqueue(6) err = queue.Enqueue(6)
assert.IsNotNil(err) assert.IsNotNil(err)
} }
func TestCircularQueue_Dequeue(t *testing.T) { func TestCircularQueue_Dequeue(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_DeQueue") assert := internal.NewAssert(t, "TestCircularQueue_DeQueue")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](4)
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4) err = queue.Enqueue(2)
queue.Enqueue(5) assert.IsNil(err)
err = queue.Enqueue(3)
assert.IsNil(err)
val, err := queue.Dequeue() val, err := queue.Dequeue()
assert.IsNil(err) assert.IsNil(err)
@@ -43,11 +55,7 @@ func TestCircularQueue_Dequeue(t *testing.T) {
assert.Equal(false, queue.IsFull()) assert.Equal(false, queue.IsFull())
val, _ = queue.Dequeue() val, _ = queue.Dequeue()
queue.Print()
assert.Equal(2, *val) assert.Equal(2, *val)
queue.Enqueue(6)
queue.Print()
assert.Equal(false, queue.IsFull()) assert.Equal(false, queue.IsFull())
} }
@@ -55,55 +63,52 @@ func TestCircularQueue_Front(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Front") assert := internal.NewAssert(t, "TestCircularQueue_Front")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](6)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err := queue.Enqueue(1)
assert.IsNil(err)
queue.Dequeue() err = queue.Enqueue(2)
queue.Dequeue() assert.IsNil(err)
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print() err = queue.Enqueue(3)
assert.IsNil(err)
val := queue.Front() val := queue.Front()
assert.Equal(3, val) assert.IsNil(err)
assert.Equal(5, queue.Size()) assert.Equal(1, val)
assert.Equal(3, queue.Size())
} }
func TestCircularQueue_Back(t *testing.T) { func TestCircularQueue_Back(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Back") assert := internal.NewAssert(t, "TestCircularQueue_Back")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](3)
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err = queue.Enqueue(2)
assert.Equal(5, queue.Back()) assert.IsNil(err)
queue.Dequeue() assert.Equal(2, queue.Back())
queue.Dequeue()
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print() val, _ := queue.Dequeue()
assert.Equal(7, queue.Back()) assert.Equal(1, *val)
err = queue.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, queue.Back())
} }
func TestCircularQueue_Contain(t *testing.T) { func TestCircularQueue_Contain(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Contain") assert := internal.NewAssert(t, "TestCircularQueue_Contain")
queue := NewCircularQueue[int](2) queue := NewCircularQueue[int](2)
queue.Enqueue(1) err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(true, queue.Contain(1)) assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(2)) assert.Equal(false, queue.Contain(2))
} }
@@ -115,7 +120,9 @@ func TestCircularQueue_Clear(t *testing.T) {
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size()) assert.Equal(0, queue.Size())
queue.Enqueue(1) err := queue.Enqueue(1)
assert.IsNil(err)
assert.Equal(false, queue.IsEmpty()) assert.Equal(false, queue.IsEmpty())
assert.Equal(1, queue.Size()) assert.Equal(1, queue.Size())
@@ -127,22 +134,12 @@ func TestCircularQueue_Clear(t *testing.T) {
func TestCircularQueue_Data(t *testing.T) { func TestCircularQueue_Data(t *testing.T) {
assert := internal.NewAssert(t, "TestCircularQueue_Data") assert := internal.NewAssert(t, "TestCircularQueue_Data")
queue := NewCircularQueue[int](6) queue := NewCircularQueue[int](3)
queue.Enqueue(1) err := queue.Enqueue(1)
queue.Enqueue(2) assert.IsNil(err)
queue.Enqueue(3)
queue.Enqueue(4)
queue.Enqueue(5)
queue.Print() err = queue.Enqueue(2)
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data()) assert.IsNil(err)
queue.Dequeue()
queue.Dequeue()
queue.Enqueue(6)
queue.Enqueue(7)
queue.Print()
assert.Equal([]int{3, 4, 5, 6, 7}, queue.Data())
assert.Equal([]int{1, 2}, queue.Data())
} }

View File

@@ -14,8 +14,6 @@ func TestLinkedQueue_Enqueue(t *testing.T) {
queue.Enqueue(2) queue.Enqueue(2)
queue.Enqueue(3) queue.Enqueue(3)
queue.Print()
assert.Equal([]int{1, 2, 3}, queue.Data()) assert.Equal([]int{1, 2, 3}, queue.Data())
assert.Equal(3, queue.Size()) assert.Equal(3, queue.Size())
} }

View File

@@ -23,19 +23,24 @@ func TestPriorityQueue_Enqueue(t *testing.T) {
assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue") assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue")
comparator := &intComparator{} comparator := &intComparator{}
pq := NewPriorityQueue[int](10, comparator) pq := NewPriorityQueue[int](3, comparator)
assert.Equal(true, pq.IsEmpty()) assert.Equal(true, pq.IsEmpty())
assert.Equal(false, pq.IsFull()) assert.Equal(false, pq.IsFull())
for i := 1; i < 11; i++ { err := pq.Enqueue(1)
pq.Enqueue(i) assert.IsNil(err)
}
err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(true, pq.IsFull()) assert.Equal(true, pq.IsFull())
queueData := pq.Data() queueData := pq.Data()
assert.Equal([]int{10, 9, 6, 7, 8, 2, 5, 1, 4, 3}, queueData) assert.Equal([]int{3, 1, 2}, queueData)
} }
@@ -43,22 +48,23 @@ func TestPriorityQueue_Dequeue(t *testing.T) {
assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue") assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue")
comparator := &intComparator{} comparator := &intComparator{}
pq := NewPriorityQueue[int](10, comparator) pq := NewPriorityQueue[int](3, comparator)
_, ok := pq.Dequeue() _, ok := pq.Dequeue()
assert.Equal(false, ok) assert.Equal(false, ok)
for i := 1; i < 11; i++ { err := pq.Enqueue(1)
pq.Enqueue(i) assert.IsNil(err)
}
assert.Equal(10, pq.Size()) err = pq.Enqueue(2)
assert.IsNil(err)
err = pq.Enqueue(3)
assert.IsNil(err)
assert.Equal(3, pq.Size())
val, ok := pq.Dequeue() val, ok := pq.Dequeue()
assert.Equal(true, ok) assert.Equal(true, ok)
assert.Equal(10, val) assert.Equal(3, val)
assert.Equal([]int{9, 8, 6, 7, 3, 2, 5, 1, 4}, pq.Data())
assert.Equal(9, pq.Size())
} }

View File

@@ -14,8 +14,6 @@ func TestLinkedStack_Push(t *testing.T) {
stack.Push(2) stack.Push(2)
stack.Push(3) stack.Push(3)
stack.Print()
expected := []int{3, 2, 1} expected := []int{3, 2, 1}
values := stack.Data() values := stack.Data()
size := stack.Size() size := stack.Size()

View File

@@ -27,8 +27,6 @@ func TestBSTree_Insert(t *testing.T) {
bstree.Insert(5) bstree.Insert(5)
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
} }
func TestBSTree_PreOrderTraverse(t *testing.T) { func TestBSTree_PreOrderTraverse(t *testing.T) {
@@ -86,8 +84,6 @@ func TestBSTree_LevelOrderTraverse(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
acturl := bstree.LevelOrderTraverse() acturl := bstree.LevelOrderTraverse()
t.Log(acturl) t.Log(acturl)
assert.Equal([]int{6, 5, 7, 2, 4}, acturl) assert.Equal([]int{6, 5, 7, 2, 4}, acturl)
@@ -103,10 +99,8 @@ func TestBSTree_Delete(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
bstree.Delete(4) bstree.Delete(4)
bstree.Print()
acturl1 := bstree.InOrderTraverse() acturl1 := bstree.InOrderTraverse()
t.Log(acturl1) t.Log(acturl1)
assert.Equal([]int{2, 5, 6, 7}, acturl1) assert.Equal([]int{2, 5, 6, 7}, acturl1)
@@ -129,8 +123,6 @@ func TestBSTree_Depth(t *testing.T) {
bstree.Insert(2) bstree.Insert(2)
bstree.Insert(4) bstree.Insert(4)
bstree.Print()
assert.Equal(bstree.Depth(), 4) assert.Equal(bstree.Depth(), 4)
} }
@@ -150,8 +142,6 @@ func TestBSTree_IsSubTree(t *testing.T) {
subTree.Insert(4) subTree.Insert(4)
subTree.Insert(6) subTree.Insert(6)
subTree.Print()
assert.Equal(true, superTree.HasSubTree(subTree)) assert.Equal(true, superTree.HasSubTree(subTree))
assert.Equal(false, subTree.HasSubTree(superTree)) assert.Equal(false, subTree.HasSubTree(superTree))
} }

View File

@@ -38,35 +38,35 @@ func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
return data return data
} }
func preOrderPrint[T any](node *datastructure.TreeNode[T]) { // func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
preOrderPrint(node.Left) // preOrderPrint(node.Left)
preOrderPrint(node.Right) // preOrderPrint(node.Right)
} // }
func postOrderPrint[T any](node *datastructure.TreeNode[T]) { // func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
preOrderPrint(node.Left) // postOrderPrint(node.Left)
preOrderPrint(node.Right) // postOrderPrint(node.Right)
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
} // }
func inOrderPrint[T any](node *datastructure.TreeNode[T]) { // func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
if node == nil { // if node == nil {
return // return
} // }
inOrderPrint(node.Left) // inOrderPrint(node.Left)
fmt.Printf("%v, ", node.Value) // fmt.Printf("%v, ", node.Value)
inOrderPrint(node.Right) // inOrderPrint(node.Right)
} // }
func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) { func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) {
var q []*datastructure.TreeNode[T] // queue var q []*datastructure.TreeNode[T] // queue

View File

@@ -132,12 +132,12 @@ func main() {
### <span id="SinglyLink_InsertAt">InsertAt</span> ### <span id="SinglyLink_InsertAt">InsertAt</span>
<p>Insert value into singly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p> <p>Insert value into singly linklist at index, param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) InsertAt(index int, value T) error func (link *SinglyLink[T]) InsertAt(index int, value T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -152,6 +152,8 @@ import (
func main() { func main() {
lk := link.NewSinglyLink[int]() lk := link.NewSinglyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -228,12 +230,12 @@ func main() {
### <span id="SinglyLink_DeleteAt">DeleteAt</span> ### <span id="SinglyLink_DeleteAt">DeleteAt</span>
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p> <p>Delete value at specific index, param `index` should be [0, len(SinglyLink)-1]</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAt(index int) error func (link *SinglyLink[T]) DeleteAt(index int)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -253,9 +255,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -268,7 +269,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtHead() error func (link *SinglyLink[T]) DeleteAtHead()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -288,9 +289,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -304,7 +304,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtTail() error func (link *SinglyLink[T]) DeleteAtTail()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -323,9 +323,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```
@@ -628,12 +627,12 @@ func main() {
### <span id="DoublyLink_InsertAt">InsertAt</span> ### <span id="DoublyLink_InsertAt">InsertAt</span>
<p>Insert value into doubly linklist at index, index shoud be great or equal 0 and less or equal number of link nodes</p> <p>Insert value into doubly linklist at index, param `index` should between [0, len(DoublyLink)], if index do not meet the conditions, do nothing</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) InsertAt(index int, value T) error func (link *DoublyLink[T]) InsertAt(index int, value T)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -648,6 +647,8 @@ import (
func main() { func main() {
lk := link.NewDoublyLink[int]() lk := link.NewDoublyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -724,12 +725,12 @@ func main() {
### <span id="DoublyLink_DeleteAt">DeleteAt</span> ### <span id="DoublyLink_DeleteAt">DeleteAt</span>
<p>Delete value at specific index, index shoud be great or equal 0 and less or less than number of link nodes - 1</p> <p>Delete value at specific index, param `index` should be [0, len(DoublyLink)-1]</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAt(index int) error func (link *DoublyLink[T]) DeleteAt(index int)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -749,9 +750,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -764,7 +764,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtHead() error func (link *DoublyLink[T]) DeleteAtHead()
``` ```
<b>Example:</b> <b>Example:</b>
@@ -784,9 +784,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```

View File

@@ -137,7 +137,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) InsertAt(index int, value T) error func (link *SinglyLink[T]) InsertAt(index int, value T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -152,6 +152,8 @@ import (
func main() { func main() {
lk := link.NewSinglyLink[int]() lk := link.NewSinglyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -233,7 +235,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAt(index int) error func (link *SinglyLink[T]) DeleteAt(index int)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -253,9 +255,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -268,7 +269,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtHead() error func (link *SinglyLink[T]) DeleteAtHead()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -288,9 +289,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -304,7 +304,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *SinglyLink[T]) DeleteAtTail() error func (link *SinglyLink[T]) DeleteAtTail()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -323,9 +323,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```
@@ -633,7 +632,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) InsertAt(index int, value T) error func (link *DoublyLink[T]) InsertAt(index int, value T)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -648,6 +647,8 @@ import (
func main() { func main() {
lk := link.NewDoublyLink[int]() lk := link.NewDoublyLink[int]()
lk.InsertAt(1, 1) //do nothing
lk.InsertAt(0, 1) lk.InsertAt(0, 1)
lk.InsertAt(1, 2) lk.InsertAt(1, 2)
lk.InsertAt(2, 3) lk.InsertAt(2, 3)
@@ -729,7 +730,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAt(index int) error func (link *DoublyLink[T]) DeleteAt(index int)
``` ```
<b>例子:</b> <b>例子:</b>
@@ -749,9 +750,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAt(3) lk.DeleteAt(3)
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2, 3} fmt.Println(lk.Values()) //[]int{1, 2, 3}
} }
``` ```
@@ -764,7 +764,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtHead() error func (link *DoublyLink[T]) DeleteAtHead()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -784,9 +784,8 @@ func main() {
lk.InsertAtTail(3) lk.InsertAtTail(3)
lk.InsertAtTail(4) lk.InsertAtTail(4)
err := lk.DeleteAtHead() lk.DeleteAtHead()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{2, 3, 4} fmt.Println(lk.Values()) //[]int{2, 3, 4}
} }
``` ```
@@ -800,7 +799,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func (link *DoublyLink[T]) DeleteAtTail() error func (link *DoublyLink[T]) DeleteAtTail()
``` ```
<b>例子:</b> <b>例子:</b>
@@ -819,9 +818,8 @@ func main() {
lk.InsertAtTail(2) lk.InsertAtTail(2)
lk.InsertAtTail(3) lk.InsertAtTail(3)
err := lk.DeleteAtTail() lk.DeleteAtTail()
fmt.Println(err) //nil
fmt.Println(lk.Values()) //[]int{1, 2} fmt.Println(lk.Values()) //[]int{1, 2}
} }
``` ```

View File

@@ -28,13 +28,12 @@ import (
### <span id="Comma">Comma</span> ### <span id="Comma">Comma</span>
<p>Add comma to number by every 3 numbers from right. ahead by symbol char. <p>Add comma to a number value by every 3 numbers from right to left. ahead by symbol char. if value is a invalid number string like "aa", return empty string.</p>
Param should be number or numberic string.</p>
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Comma(v any, symbol string) string func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
``` ```
<b>Example:</b> <b>Example:</b>

View File

@@ -28,12 +28,12 @@ import (
### <span id="Comma">Comma</span> ### <span id="Comma">Comma</span>
<p>用逗号每隔3位分割数字/字符串,签名添加符号。参数必须是数字或者可以转为数字的字符串</p> <p>用逗号每隔3位分割数字/字符串,支持前缀添加符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Comma(v any, symbol string) string func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
``` ```
<b>例子:</b> <b>例子:</b>

View File

@@ -45,7 +45,7 @@ import (
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Average[T lancetconstraints.Number](numbers ...T) T func Average[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -157,7 +157,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Max[T lancetconstraints.Number](numbers ...T) T func Max[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>
@@ -223,7 +223,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Min[T lancetconstraints.Number](numbers ...T) T func Min[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>Example:</b> <b>Example:</b>

View File

@@ -44,7 +44,7 @@ import (
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Average[T lancetconstraints.Number](numbers ...T) T func Average[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -154,7 +154,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Max[T lancetconstraints.Number](numbers ...T) T func Max[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>
@@ -220,7 +220,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Min[T lancetconstraints.Number](numbers ...T) T func Min[T constraints.Integer | constraints.Float](numbers ...T) T
``` ```
<b>例子:</b> <b>例子:</b>

View File

@@ -1138,7 +1138,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string) func Sort[T constraints.Ordered](slice []T, sortOrder ...string)
``` ```
<b>Example:</b> <b>Example:</b>

View File

@@ -1135,7 +1135,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string) func Sort[T constraints.Ordered](slice []T, sortOrder ...string)
``` ```
<b>例子:</b> <b>例子:</b>

View File

@@ -28,12 +28,14 @@ import (
- [Capitalize](#Capitalize) - [Capitalize](#Capitalize)
- [IsString](#IsString) - [IsString](#IsString)
- [KebabCase](#KebabCase) - [KebabCase](#KebabCase)
- [UpperKebabCase](#UpperKebabCase)
- [LowerFirst](#LowerFirst) - [LowerFirst](#LowerFirst)
- [UpperFirst](#UpperFirst) - [UpperFirst](#UpperFirst)
- [PadEnd](#PadEnd) - [PadEnd](#PadEnd)
- [PadStart](#PadStart) - [PadStart](#PadStart)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [SnakeCase](#SnakeCase) - [SnakeCase](#SnakeCase)
- [UpperSnakeCase](#UpperSnakeCase)
- [SplitEx](#SplitEx) - [SplitEx](#SplitEx)
- [Wrap](#Wrap) - [Wrap](#Wrap)
- [Unwrap](#Unwrap) - [Unwrap](#Unwrap)
@@ -165,10 +167,8 @@ func main() {
``` ```
### <span id="CamelCase">CamelCase</span> ### <span id="CamelCase">CamelCase</span>
<p>Covert string to camelCase string.</p> <p>Coverts string to camelCase string, non letters and numbers will be ignored.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -195,6 +195,75 @@ func main() {
s4 := strutil.CamelCase("foo bar") s4 := strutil.CamelCase("foo bar")
fmt.Println(s4) //fooBar fmt.Println(s4) //fooBar
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s4) //foo11Bar
}
```
### <span id="KebabCase">KebabCase</span>
<p>KebabCase covert string to kebab-case, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func KebabCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.KebabCase("Foo Bar-")
fmt.Println(s1) //foo-bar
s2 := strutil.KebabCase("foo_Bar")
fmt.Println(s2) //foo-bar
s3 := strutil.KebabCase("fooBar")
fmt.Println(s3) //foo-bar
s4 := strutil.KebabCase("__FOO_BAR__")
fmt.Println(s4) //foo-bar
}
```
### <span id="UpperKebabCase">UpperKebabCase</span>
<p>UpperKebabCase covert string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func KebabCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperKebabCase("Foo Bar-")
fmt.Println(s1) //FOO-BAR
s2 := strutil.UpperKebabCase("foo_Bar")
fmt.Println(s2) //FOO-BAR
s3 := strutil.UpperKebabCase("fooBar")
fmt.Println(s3) //FOO-BAR
s4 := strutil.UpperKebabCase("__FOO_BAR__")
fmt.Println(s4) //FOO-BAR
} }
``` ```
@@ -457,7 +526,7 @@ func main() {
### <span id="SnakeCase">SnakeCase</span> ### <span id="SnakeCase">SnakeCase</span>
<p>Covert string to snake_case.</p> <p>Coverts string to snake_case, non letters and numbers will be ignored.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -483,14 +552,47 @@ func main() {
fmt.Println(s3) //foo_bar fmt.Println(s3) //foo_bar
s4 := strutil.SnakeCase("__FOO_BAR__") s4 := strutil.SnakeCase("__FOO_BAR__")
fmt.Println(s4) //f_o_o_b_a_r fmt.Println(s4) //foo_bar
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //a_bbc_s_a_b_b_c fmt.Println(s5) //foo_1_1_bar
} }
``` ```
### <span id="UpperSnakeCase">UpperSnakeCase</span>
<p>Coverts string to upper KEBAB-CASE, non letters and numbers will be ignored.</p>
<b>Signature:</b>
```go
func SnakeCase(s string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperSnakeCase("Foo Bar-")
fmt.Println(s1) //FOO_BAR
s2 := strutil.UpperSnakeCase("foo_Bar")
fmt.Println(s2) //FOO_BAR
s3 := strutil.UpperSnakeCase("fooBar")
fmt.Println(s3) //FOO_BAR
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
fmt.Println(s4) //FOO_BAR
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //FOO_1_1_BAR
}
```
### <span id="SplitEx">SplitEx</span> ### <span id="SplitEx">SplitEx</span>

View File

@@ -28,12 +28,14 @@ import (
- [Capitalize](#Capitalize) - [Capitalize](#Capitalize)
- [IsString](#IsString) - [IsString](#IsString)
- [KebabCase](#KebabCase) - [KebabCase](#KebabCase)
- [UpperKebabCase](#UpperKebabCase)
- [LowerFirst](#LowerFirst) - [LowerFirst](#LowerFirst)
- [UpperFirst](#UpperFirst) - [UpperFirst](#UpperFirst)
- [PadEnd](#PadEnd) - [PadEnd](#PadEnd)
- [PadStart](#PadStart) - [PadStart](#PadStart)
- [Reverse](#Reverse) - [Reverse](#Reverse)
- [SnakeCase](#SnakeCase) - [SnakeCase](#SnakeCase)
- [UpperSnakeCase](#UpperSnakeCase)
- [SplitEx](#SplitEx) - [SplitEx](#SplitEx)
- [Wrap](#Wrap) - [Wrap](#Wrap)
- [Unwrap](#Unwrap) - [Unwrap](#Unwrap)
@@ -169,7 +171,7 @@ func main() {
### <span id="CamelCase">CamelCase</span> ### <span id="CamelCase">CamelCase</span>
<p>将字符串转换为驼峰式字符串</p> <p>将字符串转换为驼峰式字符串, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -196,6 +198,9 @@ func main() {
s4 := strutil.CamelCase("foo bar") s4 := strutil.CamelCase("foo bar")
fmt.Println(s4) //fooBar fmt.Println(s4) //fooBar
s4 := strutil.CamelCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s4) //foo11Bar
} }
``` ```
@@ -261,7 +266,7 @@ func main() {
### <span id="KebabCase">KebabCase</span> ### <span id="KebabCase">KebabCase</span>
<p>将字符串转换为kebab-case</p> <p>将字符串转换为kebab-case, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -287,7 +292,40 @@ func main() {
fmt.Println(s3) //foo-bar fmt.Println(s3) //foo-bar
s4 := strutil.KebabCase("__FOO_BAR__") s4 := strutil.KebabCase("__FOO_BAR__")
fmt.Println(s4) //f-o-o-b-a-r fmt.Println(s4) //foo-bar
}
```
### <span id="UpperKebabCase">UpperKebabCase</span>
<p>将字符串转换为大写KEBAB-CASE, 非字母和数字会被忽略</p>
<b>函数签名:</b>
```go
func KebabCase(s string) string
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperKebabCase("Foo Bar-")
fmt.Println(s1) //FOO-BAR
s2 := strutil.UpperKebabCase("foo_Bar")
fmt.Println(s2) //FOO-BAR
s3 := strutil.UpperKebabCase("fooBar")
fmt.Println(s3) //FOO-BAR
s4 := strutil.UpperKebabCase("__FOO_BAR__")
fmt.Println(s4) //FOO-BAR
} }
``` ```
@@ -458,7 +496,7 @@ func main() {
### <span id="SnakeCase">SnakeCase</span> ### <span id="SnakeCase">SnakeCase</span>
<p>将字符串转换为snake_case形式</p> <p>将字符串转换为snake_case形式, 非字母和数字会被忽略</p>
<b>函数签名:</b> <b>函数签名:</b>
@@ -484,10 +522,45 @@ func main() {
fmt.Println(s3) //foo_bar fmt.Println(s3) //foo_bar
s4 := strutil.SnakeCase("__FOO_BAR__") s4 := strutil.SnakeCase("__FOO_BAR__")
fmt.Println(s4) //f_o_o_b_a_r fmt.Println(s4) //foo_bar
s5 := strutil.SnakeCase("aBbc-s$@a&%_B.B^C") s5 := strutil.SnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //a_bbc_s_a_b_b_c fmt.Println(s5) //foo_1_1_bar
}
```
### <span id="UpperSnakeCase">UpperSnakeCase</span>
<p>将字符串转换为大写SNAKE_CASE形式, 非字母和数字会被忽略</p>
<b>函数签名:</b>
```go
func SnakeCase(s string) string
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
s1 := strutil.UpperSnakeCase("Foo Bar-")
fmt.Println(s1) //FOO_BAR
s2 := strutil.UpperSnakeCase("foo_Bar")
fmt.Println(s2) //FOO_BAR
s3 := strutil.UpperSnakeCase("fooBar")
fmt.Println(s3) //FOO_BAR
s4 := strutil.UpperSnakeCase("__FOO_BAR__")
fmt.Println(s4) //FOO_BAR
s5 := strutil.UpperSnakeCase("Foo-#1😄$_%^&*(1bar")
fmt.Println(s5) //FOO_1_1_BAR
} }
``` ```

View File

@@ -211,7 +211,7 @@ func main() {
### <span id="ExecCommand">CompareOsEnv</span> ### <span id="ExecCommand">CompareOsEnv</span>
<p>Use shell /bin/bash -c(linux) or cmd (windows) to execute command.</p> <p>Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.</p>
<b>Signature:</b> <b>Signature:</b>
@@ -227,10 +227,24 @@ import (
) )
func main() { func main() {
out, errout, err := system.ExecCommand("ls") // linux or mac
fmt.Println(out) stdout, stderr, err := system.ExecCommand("ls")
fmt.Println(errout) fmt.Println("std out: ", stdout)
fmt.Println(err) fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
// windows
stdout, stderr, err = system.ExecCommand("dir")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
// error command
stdout, stderr, err = system.ExecCommand("abc")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
if err != nil {
fmt.Println(err.Error())
}
} }
``` ```

View File

@@ -212,7 +212,7 @@ func main() {
### <span id="ExecCommand">ExecCommand</span> ### <span id="ExecCommand">ExecCommand</span>
<p>使用shell /bin/bash -c(linux) 或 cmd (windows) 执行shell命令</p> <p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令</p>
<b>Signature:</b> <b>Signature:</b>
@@ -228,10 +228,24 @@ import (
) )
func main() { func main() {
out, errout, err := system.ExecCommand("ls") // linux or mac
fmt.Println(out) stdout, stderr, err := system.ExecCommand("ls")
fmt.Println(errout) fmt.Println("std out: ", stdout)
fmt.Println(err) fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
// windows
stdout, stderr, err = system.ExecCommand("dir")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
// error command
stdout, stderr, err = system.ExecCommand("abc")
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
if err != nil {
fmt.Println(err.Error())
}
} }
``` ```

View File

@@ -47,6 +47,7 @@ import (
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
- [IsZeroValue](#IsZeroValue) - [IsZeroValue](#IsZeroValue)
- [IsGBK](#IsGBK)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -821,7 +822,34 @@ func main() {
``` ```
### <span id="IsGBK">IsGBK</span>
<p>Checks if data encoding is gbk(Chinese character internal code extension specification). this function is implemented by whether double bytes fall within the encoding range of gbk,while each byte of utf-8 encoding format falls within the encoding range of gbk.Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding and then call IsGBK() to check gbk encoding. like the example.</p>
<b>Signature:</b>
```go
func IsGBK(data []byte) bool
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
data := []byte("你好")
// check utf8 first
if utf8.Valid(data) {
fmt.Println("data encoding is utf-8")
}else if(validator.IsGBK(data)) {
fmt.Println("data encoding is GBK")
}
fmt.Println("data encoding is unknown")
}
```

View File

@@ -47,6 +47,7 @@ import (
- [IsUrl](#IsUrl) - [IsUrl](#IsUrl)
- [IsWeakPassword](#IsWeakPassword) - [IsWeakPassword](#IsWeakPassword)
- [IsZeroValue](#IsZeroValue) - [IsZeroValue](#IsZeroValue)
- [IsGBK](#IsGBK)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -820,3 +821,31 @@ func main() {
} }
``` ```
### <span id="IsGBK">IsGBK</span>
<p>检查数据编码是否为gbk汉字内部代码扩展规范。该函数的实现取决于双字节是否在gbk的编码范围内而utf-8编码格式的每个字节都在gbk编码范围内。因此应该首先调用utf8.valid检查它是否是utf-8编码然后调用IsGBK检查gbk编码。如示例所示。</p>
<b>函数签名:</b>
```go
func IsGBK(data []byte) bool
```
<b>例子:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/validator"
)
func main() {
data := []byte("你好")
// 先检查utf8编码
if utf8.Valid(data) {
fmt.Println("data encoding is utf-8")
}else if(validator.IsGBK(data)) {
fmt.Println("data encoding is GBK")
}
fmt.Println("data encoding is unknown")
}
```

View File

@@ -11,7 +11,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path"
@@ -78,13 +77,16 @@ func CopyFile(srcFilePath string, dstFilePath string) error {
var tmp = make([]byte, 1024*4) var tmp = make([]byte, 1024*4)
for { for {
n, err := srcFile.Read(tmp) n, err := srcFile.Read(tmp)
distFile.Write(tmp[:n])
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
return err return err
} }
_, err = distFile.Write(tmp[:n])
if err != nil {
return err
}
} }
} }
@@ -102,7 +104,7 @@ func ClearFile(path string) error {
//ReadFileToString return string of file content //ReadFileToString return string of file content
func ReadFileToString(path string) (string, error) { func ReadFileToString(path string) (string, error) {
bytes, err := ioutil.ReadFile(path) bytes, err := os.ReadFile(path)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -141,7 +143,7 @@ func ListFileNames(path string) ([]string, error) {
return []string{}, nil return []string{}, nil
} }
fs, err := ioutil.ReadDir(path) fs, err := os.ReadDir(path)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@@ -172,7 +174,7 @@ func Zip(fpath string, destPath string) error {
archive := zip.NewWriter(zipFile) archive := zip.NewWriter(zipFile)
defer archive.Close() defer archive.Close()
filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
@@ -209,6 +211,10 @@ func Zip(fpath string, destPath string) error {
return nil return nil
}) })
if err != nil {
return err
}
return nil return nil
} }
@@ -229,9 +235,13 @@ func UnZip(zipFile string, destPath string) error {
} }
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm) err = os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
} else { } else {
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { err = os.MkdirAll(filepath.Dir(path), os.ModePerm)
if err != nil {
return err return err
} }

View File

@@ -25,9 +25,10 @@ func TestCreateFile(t *testing.T) {
f := "./text.txt" f := "./text.txt"
if CreateFile(f) { if CreateFile(f) {
file, err := os.Open(f) file, err := os.Open(f)
defer file.Close()
assert.IsNil(err) assert.IsNil(err)
assert.Equal(f, file.Name()) assert.Equal(f, file.Name())
defer file.Close()
} else { } else {
t.FailNow() t.FailNow()
} }
@@ -113,7 +114,11 @@ func TestReadFileToString(t *testing.T) {
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() defer f.Close()
f.WriteString("hello world")
_, err := f.WriteString("hello world")
if err != nil {
t.Log(err)
}
content, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
assert.Equal("hello world", content) assert.Equal("hello world", content)
@@ -130,9 +135,12 @@ func TestClearFile(t *testing.T) {
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() defer f.Close()
f.WriteString("hello world") _, err := f.WriteString("hello world")
if err != nil {
t.Log(err)
}
err := ClearFile(path) err = ClearFile(path)
assert.IsNil(err) assert.IsNil(err)
content, _ := ReadFileToString(path) content, _ := ReadFileToString(path)
@@ -148,8 +156,13 @@ func TestReadFileByLine(t *testing.T) {
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() defer f.Close()
f.WriteString("hello\nworld")
_, err := f.WriteString("hello\nworld")
if err != nil {
t.Log(err)
}
expected := []string{"hello", "world"} expected := []string{"hello", "world"}
actual, _ := ReadFileByLine(path) actual, _ := ReadFileByLine(path)
@@ -166,10 +179,14 @@ func TestZipAndUnZip(t *testing.T) {
file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777) file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777)
defer file.Close() defer file.Close()
file.WriteString("hello\nworld")
_, err := file.WriteString("hello\nworld")
if err != nil {
t.Fail()
}
zipFile := "./text.zip" zipFile := "./text.zip"
err := Zip(srcFile, zipFile) err = Zip(srcFile, zipFile)
assert.IsNil(err) assert.IsNil(err)
unZipPath := "./unzip" unZipPath := "./unzip"

View File

@@ -4,14 +4,25 @@
// Package formatter implements some functions to format string, struct. // Package formatter implements some functions to format string, struct.
package formatter package formatter
import "strings" import (
"strings"
"golang.org/x/exp/constraints"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// if value is invalid number string eg "aa", return empty string
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
s, err := numberToString(value)
if err != nil {
return ""
}
// Comma add comma to number by every 3 numbers from right. ahead by symbol char
func Comma(v any, symbol string) string {
s := numString(v)
dotIndex := strings.Index(s, ".") dotIndex := strings.Index(s, ".")
if dotIndex != -1 { if dotIndex != -1 {
return symbol + commaString(s[:dotIndex]) + s[dotIndex:] return symbol + commaString(s[:dotIndex]) + s[dotIndex:]
} }
return symbol + commaString(s) return symbol + commaString(s)
} }

View File

@@ -14,27 +14,34 @@ func commaString(s string) string {
return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:]) return commaString(s[:len(s)-3]) + "," + commaString(s[len(s)-3:])
} }
func numString(value any) string { func numberToString(value any) (string, error) {
switch reflect.TypeOf(value).Kind() { switch reflect.TypeOf(value).Kind() {
case reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
return fmt.Sprintf("%v", value) reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return fmt.Sprintf("%v", value), nil
// todo: need to handle 12345678.9 => 1.23456789e+07
case reflect.Float32, reflect.Float64:
return fmt.Sprintf("%v", value), nil
case reflect.String: case reflect.String:
{ {
sv := fmt.Sprintf("%v", value) sv := fmt.Sprintf("%v", value)
if strings.Contains(sv, ".") { if strings.Contains(sv, ".") {
_, err := strconv.ParseFloat(sv, 64) _, err := strconv.ParseFloat(sv, 64)
if err == nil { if err != nil {
return sv return "", err
} }
return sv, nil
} else { } else {
_, err := strconv.ParseInt(sv, 10, 64) _, err := strconv.ParseInt(sv, 10, 64)
if err == nil { if err != nil {
return sv return "", nil
} }
return sv, nil
} }
} }
default: default:
return "" return "", nil
} }
return ""
} }

View File

@@ -12,12 +12,16 @@ func TestComma(t *testing.T) {
assert.Equal("", Comma("", "")) assert.Equal("", Comma("", ""))
assert.Equal("", Comma("aa", "")) assert.Equal("", Comma("aa", ""))
assert.Equal("", Comma("aa.a", "")) assert.Equal("", Comma("aa.a", ""))
assert.Equal("", Comma([]int{1}, ""))
assert.Equal("123", Comma("123", "")) assert.Equal("123", Comma("123", ""))
assert.Equal("12,345", Comma("12345", "")) assert.Equal("12,345", Comma("12345", ""))
assert.Equal("12,345.6789", Comma("12345.6789", ""))
assert.Equal("123,456,789,000", Comma("123456789000", ""))
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", Comma(12345, "¥")) assert.Equal("¥12,345", Comma(12345, "¥"))
assert.Equal("12,345.6789", Comma(12345.6789, "")) assert.Equal("12,345.6789", Comma(12345.6789, ""))
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
// assert.Equal("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
} }

View File

@@ -84,11 +84,9 @@ func Debounced(fn func(), duration time.Duration) func() {
go func() { go func() {
for { for {
select { <-timer.C
case <-timer.C:
go fn() go fn()
} }
}
}() }()
return func() { timer.Reset(duration) } return func() { timer.Reset(duration) }

5
go.mod
View File

@@ -1,3 +1,8 @@
module github.com/duke-git/lancet/v2 module github.com/duke-git/lancet/v2
go 1.18 go 1.18
require (
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
golang.org/x/text v0.5.0
)

4
go.sum Normal file
View File

@@ -0,0 +1,4 @@
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=

View File

@@ -3,14 +3,16 @@
// Package iterator provides a way to iterate over values stored in containers. // Package iterator provides a way to iterate over values stored in containers.
// note: // note:
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go. // 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go.
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs. // 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
// 3. It is currently under development, unstable, and will not be completed for some time in the future. // 3. It is currently under development, unstable, and will not be completed for some time in the future.
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it. // So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413 // Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
package iterator package iterator
import "github.com/duke-git/lancet/v2/lancetconstraints" import (
"golang.org/x/exp/constraints"
)
// Iterator supports iterating over a sequence of values of type `E`. // Iterator supports iterating over a sequence of values of type `E`.
type Iterator[T any] interface { type Iterator[T any] interface {
@@ -142,7 +144,7 @@ func (iter *sliceIterator[T]) Set(value T) {
// FromRange creates a iterator which returns the numeric range between start inclusive and end // FromRange creates a iterator which returns the numeric range between start inclusive and end
// exclusive by the step size. start should be less than end, step shoud be positive. // exclusive by the step size. start should be less than end, step shoud be positive.
func FromRange[T lancetconstraints.Number](start, end, step T) Iterator[T] { func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Iterator[T] {
if end < start { if end < start {
panic("RangeIterator: start should be before end") panic("RangeIterator: start should be before end")
} else if step <= 0 { } else if step <= 0 {
@@ -152,15 +154,12 @@ func FromRange[T lancetconstraints.Number](start, end, step T) Iterator[T] {
return &rangeIterator[T]{start: start, end: end, step: step} return &rangeIterator[T]{start: start, end: end, step: step}
} }
type rangeIterator[T lancetconstraints.Number] struct { type rangeIterator[T constraints.Integer | constraints.Float] struct {
start, end, step T start, end, step T
} }
func (iter *rangeIterator[T]) HasNext() bool { func (iter *rangeIterator[T]) HasNext() bool {
if iter.start >= iter.end { return iter.start < iter.end
return false
}
return true
} }
func (iter *rangeIterator[T]) Next() (T, bool) { func (iter *rangeIterator[T]) Next() (T, bool) {

View File

@@ -3,7 +3,7 @@
// Package iterator provides a way to iterate over values stored in containers. // Package iterator provides a way to iterate over values stored in containers.
// note: // note:
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go. // 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go.
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs. // 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
// 3. It is currently under development, unstable, and will not be completed for some time in the future. // 3. It is currently under development, unstable, and will not be completed for some time in the future.
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it. // So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.

View File

@@ -11,13 +11,3 @@ type Comparator interface {
// Descending order: should return 1 -> v1 < v2, 0 -> v1 = v2, -1 -> v1 > v2 // Descending order: should return 1 -> v1 < v2, 0 -> v1 = v2, -1 -> v1 > v2
Compare(v1, v2 any) int Compare(v1, v2 any) int
} }
// Number contains all types of number and uintptr, used for generics constraint
type Number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
}
// Ordered is a constraint that permits any ordered type: any type that supports the operators < <= >= >
type Ordered interface {
Number | ~string
}

View File

@@ -85,7 +85,7 @@ func Intersect[K comparable, V any](maps ...map[K]V) map[K]V {
return m return m
} }
reduceMaps := make([]map[K]V, 2, 2) reduceMaps := make([]map[K]V, 2)
result = reducer(maps[0], maps[1]) result = reducer(maps[0], maps[1])
for i := 2; i < len(maps); i++ { for i := 2; i < len(maps); i++ {

View File

@@ -10,7 +10,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/duke-git/lancet/v2/lancetconstraints" "golang.org/x/exp/constraints"
) )
// Exponent calculate x^n // Exponent calculate x^n
@@ -94,7 +94,7 @@ func TruncRound(x float64, n int) float64 {
} }
// Max return max value of params // Max return max value of params
func Max[T lancetconstraints.Number](numbers ...T) T { func Max[T constraints.Integer | constraints.Float](numbers ...T) T {
max := numbers[0] max := numbers[0]
for _, v := range numbers { for _, v := range numbers {
@@ -128,7 +128,7 @@ func MaxBy[T any](slice []T, comparator func(T, T) bool) T {
} }
// Min return min value of params // Min return min value of params
func Min[T lancetconstraints.Number](numbers ...T) T { func Min[T constraints.Integer | constraints.Float](numbers ...T) T {
min := numbers[0] min := numbers[0]
for _, v := range numbers { for _, v := range numbers {
@@ -161,8 +161,8 @@ func MinBy[T any](slice []T, comparator func(T, T) bool) T {
return min return min
} }
// Average return average value of params // Average return average value of numbers
func Average[T lancetconstraints.Number](numbers ...T) T { func Average[T constraints.Integer | constraints.Float](numbers ...T) T {
var sum T var sum T
n := T(len(numbers)) n := T(len(numbers))

View File

@@ -6,7 +6,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@@ -108,7 +108,11 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
client.setTLS(rawUrl) client.setTLS(rawUrl)
client.setHeader(req, request.Headers) client.setHeader(req, request.Headers)
client.setQueryParam(req, rawUrl, request.QueryParams)
err = client.setQueryParam(req, rawUrl, request.QueryParams)
if err != nil {
return nil, err
}
if request.FormData != nil { if request.FormData != nil {
client.setFormData(req, request.FormData) client.setFormData(req, request.FormData)
@@ -177,7 +181,7 @@ func (client *HttpClient) setQueryParam(req *http.Request, reqUrl string, queryP
func (client *HttpClient) setFormData(req *http.Request, values url.Values) { func (client *HttpClient) setFormData(req *http.Request, values url.Values) {
formData := []byte(values.Encode()) formData := []byte(values.Encode())
req.Body = ioutil.NopCloser(bytes.NewReader(formData)) req.Body = io.NopCloser(bytes.NewReader(formData))
req.ContentLength = int64(len(formData)) req.ContentLength = int64(len(formData))
} }

View File

@@ -1,7 +1,7 @@
package netutil package netutil
import ( import (
"io/ioutil" "io"
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
@@ -32,7 +32,10 @@ func TestHttpClient_Get(t *testing.T) {
} }
var todo Todo var todo Todo
httpClient.DecodeResponse(resp, &todo) err = httpClient.DecodeResponse(resp, &todo)
if err != nil {
t.Log(err)
}
assert.Equal(1, todo.Id) assert.Equal(1, todo.Id)
} }
@@ -58,7 +61,7 @@ func TestHttpClent_Post(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -90,6 +93,6 @@ func TestStructToUrlValues(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", string(body)) t.Log("response: ", string(body))
} }

View File

@@ -2,8 +2,9 @@ package netutil
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"log" "log"
"net/url"
"testing" "testing"
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
@@ -20,7 +21,7 @@ func TestHttpGet(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -40,7 +41,7 @@ func TestHttpPost(t *testing.T) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -50,23 +51,20 @@ func TestHttpPostFormData(t *testing.T) {
// "Content-Type": "application/x-www-form-urlencoded", // "Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "multipart/form-data", "Content-Type": "multipart/form-data",
} }
type Todo struct {
UserId int `json:"userId"`
Title string `json:"title"`
}
// postData := url.Values{}
// postData.Add("userId", "1")
// postData.Add("title", "TestAddToDo")
postData := make(map[string]string) postData := url.Values{}
postData["userId"] = "1" postData.Add("userId", "1")
postData["title"] = "title" postData.Add("title", "TestToDo")
// postData := make(map[string]string)
// postData["userId"] = "1"
// postData["title"] = "title"
resp, err := HttpPost(apiUrl, header, postData, nil) resp, err := HttpPost(apiUrl, header, postData, nil)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -87,7 +85,7 @@ func TestHttpPut(t *testing.T) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -108,7 +106,7 @@ func TestHttpPatch(t *testing.T) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
@@ -118,7 +116,7 @@ func TestHttpDelete(t *testing.T) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }

View File

@@ -2,7 +2,7 @@ package netutil
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -55,7 +55,7 @@ func GetPublicIpInfo() (*PublicIpInfo, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -3,7 +3,7 @@ package netutil
import ( import (
"bytes" "bytes"
"errors" "errors"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@@ -173,7 +173,7 @@ func setBodyByte(req *http.Request, body any) error {
if body != nil { if body != nil {
switch b := body.(type) { switch b := body.(type) {
case []byte: case []byte:
req.Body = ioutil.NopCloser(bytes.NewReader(b)) req.Body = io.NopCloser(bytes.NewReader(b))
req.ContentLength = int64(len(b)) req.ContentLength = int64(len(b))
default: default:
return errors.New("body type should be []byte") return errors.New("body type should be []byte")

View File

@@ -11,7 +11,14 @@ import (
"reflect" "reflect"
"sort" "sort"
"github.com/duke-git/lancet/v2/lancetconstraints" "golang.org/x/exp/constraints"
)
// Create a static variable to store the hash table.
// This variable has the same lifetime as the entire program and can be shared by functions that are called more than once.
var (
memoryHashMap = make(map[string]map[any]int)
memoryHashCounter = make(map[string]int)
) )
// Contain check if the target value is in the slice or not // Contain check if the target value is in the slice or not
@@ -729,7 +736,7 @@ func Shuffle[T any](slice []T) []T {
// Sort sorts a slice of any ordered type(number or string), use quick sort algrithm. // Sort sorts a slice of any ordered type(number or string), use quick sort algrithm.
// default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc` // default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`
func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string) { func Sort[T constraints.Ordered](slice []T, sortOrder ...string) {
if len(sortOrder) > 0 && sortOrder[0] == "desc" { if len(sortOrder) > 0 && sortOrder[0] == "desc" {
quickSort(slice, 0, len(slice)-1, "desc") quickSort(slice, 0, len(slice)-1, "desc")
} else { } else {
@@ -832,14 +839,44 @@ func Without[T comparable](slice []T, items ...T) []T {
return result return result
} }
// IndexOf returns the index at which the first occurrence of a item is found in a slice or return -1 if the item cannot be found. // IndexOf returns the index at which the first occurrence of an item is found in a slice or return -1 if the item cannot be found.
func IndexOf[T comparable](slice []T, item T) int { func IndexOf[T comparable](arr []T, val T) int {
for i, v := range slice { limit := 10
if v == item { // gets the hash value of the array as the key of the hash table.
return i key := fmt.Sprintf("%p", arr)
// determines whether the hash table is empty. If so, the hash table is created.
if memoryHashMap[key] == nil {
memoryHashMap[key] = make(map[any]int)
// iterate through the array, adding the value and index of each element to the hash table.
for i := len(arr) - 1; i >= 0; i-- {
memoryHashMap[key][arr[i]] = i
} }
} }
// update the hash table counter.
memoryHashCounter[key]++
// use the hash table to find the specified value. If found, the index is returned.
if index, ok := memoryHashMap[key][val]; ok {
// calculate the memory usage of the hash table.
size := len(memoryHashMap)
// If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared.
if size > limit {
var minKey string
var minVal int
for k, v := range memoryHashCounter {
if k == key {
continue
}
if minVal == 0 || v < minVal {
minKey = k
minVal = v
}
}
delete(memoryHashMap, minKey)
delete(memoryHashCounter, minKey)
}
return index
}
return -1 return -1
} }

View File

@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/duke-git/lancet/v2/lancetconstraints" "golang.org/x/exp/constraints"
) )
// sliceValue return the reflect value of a slice // sliceValue return the reflect value of a slice
@@ -27,7 +27,7 @@ func sliceElemType(reflectType reflect.Type) reflect.Type {
} }
} }
func quickSort[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) { func quickSort[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) {
if lowIndex < highIndex { if lowIndex < highIndex {
p := partitionOrderedSlice(slice, lowIndex, highIndex, order) p := partitionOrderedSlice(slice, lowIndex, highIndex, order)
quickSort(slice, lowIndex, p-1, order) quickSort(slice, lowIndex, p-1, order)
@@ -36,7 +36,7 @@ func quickSort[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int,
} }
// partitionOrderedSlice split ordered slice into two parts for quick sort // partitionOrderedSlice split ordered slice into two parts for quick sort
func partitionOrderedSlice[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) int { func partitionOrderedSlice[T constraints.Ordered](slice []T, lowIndex, highIndex int, order string) int {
p := slice[highIndex] p := slice[highIndex]
i := lowIndex i := lowIndex

View File

@@ -1,6 +1,7 @@
package slice package slice
import ( import (
"fmt"
"math" "math"
"testing" "testing"
@@ -669,8 +670,39 @@ func TestIndexOf(t *testing.T) {
assert := internal.NewAssert(t, "TestIndexOf") assert := internal.NewAssert(t, "TestIndexOf")
arr := []string{"a", "a", "b", "c"} arr := []string{"a", "a", "b", "c"}
key := fmt.Sprintf("%p", arr)
assert.Equal(0, IndexOf(arr, "a")) assert.Equal(0, IndexOf(arr, "a"))
assert.Equal(-1, IndexOf(arr, "d")) assert.Equal(-1, IndexOf(arr, "d"))
assert.Equal(2, memoryHashCounter[key])
arr1 := []int{1, 2, 3, 4, 5}
key1 := fmt.Sprintf("%p", arr1)
assert.Equal(3, IndexOf(arr1, 4))
assert.Equal(-1, IndexOf(arr1, 6))
assert.Equal(2, memoryHashCounter[key1])
arr2 := []float64{1.1, 2.2, 3.3, 4.4, 5.5}
key2 := fmt.Sprintf("%p", arr2)
assert.Equal(2, IndexOf(arr2, 3.3))
assert.Equal(3, IndexOf(arr2, 4.4))
assert.Equal(-1, IndexOf(arr2, 6.6))
assert.Equal(3, memoryHashCounter[key2])
for i := 0; i < 6; i++ {
a := []string{"a", "b", "c"}
IndexOf(a, "a")
IndexOf(a, "b")
}
minArr := []string{"c", "b", "a"}
minKey := fmt.Sprintf("%p", minArr)
assert.Equal(0, IndexOf(minArr, "c"))
arr3 := []string{"q", "w", "e"}
key3 := fmt.Sprintf("%p", arr3)
assert.Equal(1, IndexOf(arr3, "w"))
assert.Equal(-1, IndexOf(arr3, "r"))
assert.Equal(2, memoryHashCounter[key3])
assert.Equal(0, memoryHashCounter[minKey])
} }
func TestLastIndexOf(t *testing.T) { func TestLastIndexOf(t *testing.T) {

View File

@@ -4,53 +4,41 @@
package strutil package strutil
import ( import (
"regexp"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
) )
// CamelCase covert string to camelCase string. // CamelCase covert string to camelCase string.
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "foo11Bar"
func CamelCase(s string) string { func CamelCase(s string) string {
if len(s) == 0 { var builder strings.Builder
return ""
}
result := "" strs := splitIntoStrings(s, false)
blankSpace := " " for i, str := range strs {
regex, _ := regexp.Compile("[-_&]+")
ss := regex.ReplaceAllString(s, blankSpace)
for i, v := range strings.Split(ss, blankSpace) {
vv := []rune(v)
if i == 0 { if i == 0 {
if vv[i] >= 65 && vv[i] <= 96 { builder.WriteString(strings.ToLower(str))
vv[0] += 32
}
result += string(vv)
} else { } else {
result += Capitalize(v) builder.WriteString(Capitalize(str))
} }
} }
return result return builder.String()
} }
// Capitalize converts the first character of a string to upper case and the remaining to lower case. // Capitalize converts the first character of a string to upper case and the remaining to lower case.
func Capitalize(s string) string { func Capitalize(s string) string {
if len(s) == 0 { result := make([]rune, len(s))
return ""
}
out := make([]rune, len(s))
for i, v := range s { for i, v := range s {
if i == 0 { if i == 0 {
out[i] = unicode.ToUpper(v) result[i] = unicode.ToUpper(v)
} else { } else {
out[i] = unicode.ToLower(v) result[i] = unicode.ToLower(v)
} }
} }
return string(out) return string(result)
} }
// UpperFirst converts the first character of string to upper case. // UpperFirst converts the first character of string to upper case.
@@ -116,46 +104,34 @@ func PadStart(source string, size int, padStr string) string {
} }
// KebabCase covert string to kebab-case // KebabCase covert string to kebab-case
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "foo-1-1-bar"
func KebabCase(s string) string { func KebabCase(s string) string {
if len(s) == 0 { result := splitIntoStrings(s, false)
return "" return strings.Join(result, "-")
}
regex := regexp.MustCompile(`[\W|_]+`)
blankSpace := " "
match := regex.ReplaceAllString(s, blankSpace)
rs := strings.Split(match, blankSpace)
var result []string
for _, v := range rs {
splitWords := splitWordsToLower(v)
if len(splitWords) > 0 {
result = append(result, splitWords...)
}
} }
// UpperKebabCase covert string to upper KEBAB-CASE
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO-1-1-BAR"
func UpperKebabCase(s string) string {
result := splitIntoStrings(s, true)
return strings.Join(result, "-") return strings.Join(result, "-")
} }
// SnakeCase covert string to snake_case // SnakeCase covert string to snake_case
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "foo_1_1_bar"
func SnakeCase(s string) string { func SnakeCase(s string) string {
if len(s) == 0 { result := splitIntoStrings(s, false)
return "" return strings.Join(result, "_")
}
regex := regexp.MustCompile(`[\W|_]+`)
blankSpace := " "
match := regex.ReplaceAllString(s, blankSpace)
rs := strings.Split(match, blankSpace)
var result []string
for _, v := range rs {
splitWords := splitWordsToLower(v)
if len(splitWords) > 0 {
result = append(result, splitWords...)
}
} }
// UpperSnakeCase covert string to upper SNAKE_CASE
// non letters and numbers will be ignored
// eg. "Foo-#1😄$_%^&*(1bar" => "FOO_1_1_BAR"
func UpperSnakeCase(s string) string {
result := splitIntoStrings(s, true)
return strings.Join(result, "_") return strings.Join(result, "_")
} }

View File

@@ -1,40 +1,100 @@
package strutil package strutil
import "strings" import (
"unicode"
)
// splitWordsToLower split a string into worlds by uppercase char func splitIntoStrings(s string, upperCase bool) []string {
func splitWordsToLower(s string) []string { var runes [][]rune
var result []string lastCharType := 0
charType := 0
upperIndexes := upperIndex(s) // split into fields based on type of unicode character
l := len(upperIndexes) for _, r := range s {
if upperIndexes == nil || l == 0 { switch true {
if s != "" { case isLower(r):
result = append(result, s) charType = 1
case isUpper(r):
charType = 2
case isDigit(r):
charType = 3
default:
charType = 4
} }
return result
} if charType == lastCharType {
for i := 0; i < l; i++ { runes[len(runes)-1] = append(runes[len(runes)-1], r)
if i < l-1 {
result = append(result, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]]))
} else { } else {
result = append(result, strings.ToLower(s[upperIndexes[i]:])) runes = append(runes, []rune{r})
} }
} lastCharType = charType
return result
} }
// upperIndex get a int slice which elements are all the uppercase char index of a string for i := 0; i < len(runes)-1; i++ {
func upperIndex(s string) []int { if isUpper(runes[i][0]) && isLower(runes[i+1][0]) {
var result []int runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
for i := 0; i < len(s); i++ { runes[i] = runes[i][:len(runes[i])-1]
if 64 < s[i] && s[i] < 91 { }
result = append(result, i) }
// filter all none letters and none digit
var result []string
for _, rs := range runes {
if len(rs) > 0 && (unicode.IsLetter(rs[0]) || isDigit(rs[0])) {
if upperCase {
result = append(result, string(toUpperAll(rs)))
} else {
result = append(result, string(toLowerAll(rs)))
} }
} }
if len(s) > 0 && result != nil && result[0] != 0 {
result = append([]int{0}, result...)
} }
return result return result
} }
// isDigit checks if a character is digit ('0' to '9')
func isDigit(r rune) bool {
return r >= '0' && r <= '9'
}
// isLower checks if a character is lower case ('a' to 'z')
func isLower(r rune) bool {
return r >= 'a' && r <= 'z'
}
// isUpper checks if a character is upper case ('A' to 'Z')
func isUpper(r rune) bool {
return r >= 'A' && r <= 'Z'
}
// toLower converts a character 'A' to 'Z' to its lower case
func toLower(r rune) rune {
if r >= 'A' && r <= 'Z' {
return r + 32
}
return r
}
// toLowerAll converts a character 'A' to 'Z' to its lower case
func toLowerAll(rs []rune) []rune {
for i := range rs {
rs[i] = toLower(rs[i])
}
return rs
}
// toUpper converts a character 'a' to 'z' to its upper case
func toUpper(r rune) rune {
if r >= 'a' && r <= 'z' {
return r - 32
}
return r
}
// toUpperAll converts a character 'a' to 'z' to its upper case
func toUpperAll(rs []rune) []rune {
for i := range rs {
rs[i] = toUpper(rs[i])
}
return rs
}

View File

@@ -9,67 +9,163 @@ import (
func TestCamelCase(t *testing.T) { func TestCamelCase(t *testing.T) {
assert := internal.NewAssert(t, "TestCamelCase") assert := internal.NewAssert(t, "TestCamelCase")
assert.Equal("fooBar", CamelCase("foo_bar")) cases := map[string]string{
assert.Equal("fooBar", CamelCase("Foo-Bar")) "": "",
assert.Equal("fooBar", CamelCase("Foo&bar")) "foobar": "foobar",
assert.Equal("fooBar", CamelCase("foo bar")) "&FOO:BAR$BAZ": "fooBarBaz",
"fooBar": "fooBar",
"FOObar": "foObar",
"$foo%": "foo",
" $#$Foo 22 bar ": "foo22Bar",
"Foo-#1😄$_%^&*(1bar": "foo11Bar",
}
assert.NotEqual("FooBar", CamelCase("foo_bar")) for k, v := range cases {
assert.Equal(v, CamelCase(k))
}
} }
func TestCapitalize(t *testing.T) { func TestCapitalize(t *testing.T) {
assert := internal.NewAssert(t, "TestCapitalize") assert := internal.NewAssert(t, "TestCapitalize")
assert.Equal("Foo", Capitalize("foo")) cases := map[string]string{
assert.Equal("Foo", Capitalize("Foo")) "": "",
assert.Equal("Foo", Capitalize("Foo")) "Foo": "Foo",
"_foo": "_foo",
"foobar": "Foobar",
"fooBar": "Foobar",
"foo Bar": "Foo bar",
"foo-bar": "Foo-bar",
"$foo%": "$foo%",
}
assert.NotEqual("foo", Capitalize("Foo")) for k, v := range cases {
assert.Equal(v, Capitalize(k))
}
} }
func TestKebabCase(t *testing.T) { func TestKebabCase(t *testing.T) {
assert := internal.NewAssert(t, "TestKebabCase") assert := internal.NewAssert(t, "TestKebabCase")
assert.Equal("foo-bar", KebabCase("Foo Bar-")) cases := map[string]string{
assert.Equal("foo-bar", KebabCase("foo_Bar")) "": "",
assert.Equal("foo-bar", KebabCase("fooBar")) "foo-bar": "foo-bar",
assert.Equal("f-o-o-b-a-r", KebabCase("__FOO_BAR__")) "--Foo---Bar-": "foo-bar",
"Foo Bar-": "foo-bar",
"foo_Bar": "foo-bar",
"fooBar": "foo-bar",
"FOOBAR": "foobar",
"FOO_BAR": "foo-bar",
"__FOO_BAR__": "foo-bar",
"$foo@Bar": "foo-bar",
" $#$Foo 22 bar ": "foo-22-bar",
"Foo-#1😄$_%^&*(1bar": "foo-1-1-bar",
}
assert.NotEqual("foo_bar", KebabCase("fooBar")) for k, v := range cases {
assert.Equal(v, KebabCase(k))
}
}
func TestUpperKebabCase(t *testing.T) {
assert := internal.NewAssert(t, "TestUpperKebabCase")
cases := map[string]string{
"": "",
"foo-bar": "FOO-BAR",
"--Foo---Bar-": "FOO-BAR",
"Foo Bar-": "FOO-BAR",
"foo_Bar": "FOO-BAR",
"fooBar": "FOO-BAR",
"FOOBAR": "FOOBAR",
"FOO_BAR": "FOO-BAR",
"__FOO_BAR__": "FOO-BAR",
"$foo@Bar": "FOO-BAR",
" $#$Foo 22 bar ": "FOO-22-BAR",
"Foo-#1😄$_%^&*(1bar": "FOO-1-1-BAR",
}
for k, v := range cases {
assert.Equal(v, UpperKebabCase(k))
}
} }
func TestSnakeCase(t *testing.T) { func TestSnakeCase(t *testing.T) {
assert := internal.NewAssert(t, "TestSnakeCase") assert := internal.NewAssert(t, "TestSnakeCase")
assert.Equal("foo_bar", SnakeCase("Foo Bar-")) cases := map[string]string{
assert.Equal("foo_bar", SnakeCase("foo_Bar")) "": "",
assert.Equal("foo_bar", SnakeCase("fooBar")) "foo-bar": "foo_bar",
assert.Equal("f_o_o_b_a_r", SnakeCase("__FOO_BAR__")) "--Foo---Bar-": "foo_bar",
assert.Equal("a_bbc_s_a_b_b_c", SnakeCase("aBbc-s$@a&%_B.B^C")) "Foo Bar-": "foo_bar",
"foo_Bar": "foo_bar",
"fooBar": "foo_bar",
"FOOBAR": "foobar",
"FOO_BAR": "foo_bar",
"__FOO_BAR__": "foo_bar",
"$foo@Bar": "foo_bar",
" $#$Foo 22 bar ": "foo_22_bar",
"Foo-#1😄$_%^&*(1bar": "foo_1_1_bar",
}
assert.NotEqual("foo-bar", SnakeCase("foo_Bar")) for k, v := range cases {
assert.Equal(v, SnakeCase(k))
}
}
func TestUpperSnakeCase(t *testing.T) {
assert := internal.NewAssert(t, "TestUpperSnakeCase")
cases := map[string]string{
"": "",
"foo-bar": "FOO_BAR",
"--Foo---Bar-": "FOO_BAR",
"Foo Bar-": "FOO_BAR",
"foo_Bar": "FOO_BAR",
"fooBar": "FOO_BAR",
"FOOBAR": "FOOBAR",
"FOO_BAR": "FOO_BAR",
"__FOO_BAR__": "FOO_BAR",
"$foo@Bar": "FOO_BAR",
" $#$Foo 22 bar ": "FOO_22_BAR",
"Foo-#1😄$_%^&*(1bar": "FOO_1_1_BAR",
}
for k, v := range cases {
assert.Equal(v, UpperSnakeCase(k))
}
} }
func TestUpperFirst(t *testing.T) { func TestUpperFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst") assert := internal.NewAssert(t, "TestLowerFirst")
assert.Equal("Foo", UpperFirst("foo")) cases := map[string]string{
assert.Equal("BAR", UpperFirst("bAR")) "": "",
assert.Equal("FOo", UpperFirst("FOo")) "foo": "Foo",
assert.Equal("FOo大", UpperFirst("fOo大")) "bAR": "BAR",
"FOo": "FOo",
"fOo大": "FOo大",
}
assert.NotEqual("Bar", UpperFirst("BAR")) for k, v := range cases {
assert.Equal(v, UpperFirst(k))
}
} }
func TestLowerFirst(t *testing.T) { func TestLowerFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestLowerFirst") assert := internal.NewAssert(t, "TestLowerFirst")
assert.Equal("foo", LowerFirst("foo")) cases := map[string]string{
assert.Equal("bAR", LowerFirst("BAR")) "": "",
assert.Equal("fOo", LowerFirst("FOo")) "foo": "foo",
assert.Equal("fOo大", LowerFirst("FOo大")) "bAR": "bAR",
"FOo": "fOo",
"fOo大": "fOo大",
}
assert.NotEqual("Bar", LowerFirst("BAR")) for k, v := range cases {
assert.Equal(v, LowerFirst(k))
}
} }
func TestPadEnd(t *testing.T) { func TestPadEnd(t *testing.T) {

View File

@@ -9,6 +9,10 @@ import (
"os" "os"
"os/exec" "os/exec"
"runtime" "runtime"
"unicode/utf8"
"github.com/duke-git/lancet/v2/validator"
"golang.org/x/text/encoding/simplifiedchinese"
) )
// IsWindows check if current os is windows // IsWindows check if current os is windows
@@ -50,27 +54,61 @@ func CompareOsEnv(key, comparedEnv string) bool {
return env == comparedEnv return env == comparedEnv
} }
// ExecCommand use shell /bin/bash -c to execute command // ExecCommand execute command, return the stdout and stderr string of command, and error if error occur
// param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1
// in linux, use /bin/bash -c to execute command
// in windows, use powershell.exe to execute command
func ExecCommand(command string) (stdout, stderr string, err error) { func ExecCommand(command string) (stdout, stderr string, err error) {
var out bytes.Buffer var out bytes.Buffer
var errout bytes.Buffer var errOut bytes.Buffer
cmd := exec.Command("/bin/bash", "-c", command) cmd := exec.Command("/bin/bash", "-c", command)
if IsWindows() { if IsWindows() {
cmd = exec.Command("cmd") cmd = exec.Command("powershell.exe", command)
} }
cmd.Stdout = &out cmd.Stdout = &out
cmd.Stderr = &errout cmd.Stderr = &errOut
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
stderr = string(errout.Bytes()) if utf8.Valid(errOut.Bytes()) {
stderr = byteToString(errOut.Bytes(), "UTF8")
} else if validator.IsGBK(errOut.Bytes()) {
stderr = byteToString(errOut.Bytes(), "GBK")
}
return
}
data := out.Bytes()
if utf8.Valid(data) {
stdout = byteToString(data, "UTF8")
} else if validator.IsGBK(data) {
stdout = byteToString(data, "GBK")
} }
stdout = string(out.Bytes())
return return
} }
func byteToString(data []byte, charset string) string {
var result string
switch charset {
case "GBK":
decodeBytes, _ := simplifiedchinese.GBK.NewDecoder().Bytes(data)
result = string(decodeBytes)
case "GB18030":
decodeBytes, _ := simplifiedchinese.GB18030.NewDecoder().Bytes(data)
result = string(decodeBytes)
case "UTF8":
fallthrough
default:
result = string(data)
}
return result
}
// GetOsBits get this system bits 32bit or 64bit // GetOsBits get this system bits 32bit or 64bit
// return bit int (32/64) // return bit int (32/64)
func GetOsBits() int { func GetOsBits() int {

View File

@@ -11,10 +11,10 @@ func TestOsDetection(t *testing.T) {
assert := internal.NewAssert(t, "TestOsJudgment") assert := internal.NewAssert(t, "TestOsJudgment")
osType, _, _ := ExecCommand("echo $OSTYPE") osType, _, _ := ExecCommand("echo $OSTYPE")
if strings.Index(osType, "linux") != -1 { if strings.Contains(osType, "linux") {
assert.Equal(true, IsLinux()) assert.Equal(true, IsLinux())
} }
if strings.Index(osType, "darwin") != -1 { if strings.Contains(osType, "darwin") {
assert.Equal(true, IsMac()) assert.Equal(true, IsMac())
} }
} }
@@ -25,7 +25,9 @@ func TestOsEnvOperation(t *testing.T) {
envNotExist := GetOsEnv("foo") envNotExist := GetOsEnv("foo")
assert.Equal("", envNotExist) assert.Equal("", envNotExist)
SetOsEnv("foo", "foo_value") err := SetOsEnv("foo", "foo_value")
assert.IsNil(err)
envExist := GetOsEnv("foo") envExist := GetOsEnv("foo")
assert.Equal("foo_value", envExist) assert.Equal("foo_value", envExist)
@@ -34,7 +36,7 @@ func TestOsEnvOperation(t *testing.T) {
assert.Equal(false, CompareOsEnv("abc", "abc")) assert.Equal(false, CompareOsEnv("abc", "abc"))
assert.Equal(false, CompareOsEnv("abc", "abc")) assert.Equal(false, CompareOsEnv("abc", "abc"))
err := RemoveOsEnv("foo") err = RemoveOsEnv("foo")
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
@@ -44,22 +46,27 @@ func TestOsEnvOperation(t *testing.T) {
func TestExecCommand(t *testing.T) { func TestExecCommand(t *testing.T) {
assert := internal.NewAssert(t, "TestExecCommand") assert := internal.NewAssert(t, "TestExecCommand")
out, errout, err := ExecCommand("ls") // linux or mac
t.Log("std out: ", out) stdout, stderr, err := ExecCommand("ls")
t.Log("std err: ", errout) t.Log("std out: ", stdout)
t.Log("std err: ", stderr)
assert.Equal("", stderr)
assert.IsNil(err) assert.IsNil(err)
out, errout, err = ExecCommand("abc") // windows
t.Log("std out: ", out) stdout, stderr, err = ExecCommand("dir")
t.Log("std err: ", errout) t.Log("std out: ", stdout)
if err != nil { t.Log("std err: ", stderr)
t.Logf("error: %v\n", err) if IsWindows() {
assert.IsNil(err)
} }
if !IsWindows() { // error command
stdout, stderr, err = ExecCommand("abc")
t.Log("std out: ", stdout)
t.Log("std err: ", stderr)
assert.IsNotNil(err) assert.IsNotNil(err)
} }
}
func TestGetOsBits(t *testing.T) { func TestGetOsBits(t *testing.T) {
osBits := GetOsBits() osBits := GetOsBits()

View File

@@ -15,11 +15,24 @@ import (
"unicode" "unicode"
) )
var isAlphaRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`) var (
alphaMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]+$`)
letterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *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]*)?$`)
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`)
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
chinesePhoneMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
creditCardMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
base64Matcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
)
// IsAlpha checks if the string contains only letters (a-zA-Z) // IsAlpha checks if the string contains only letters (a-zA-Z)
func IsAlpha(str string) bool { func IsAlpha(str string) bool {
return isAlphaRegexMatcher.MatchString(str) return alphaMatcher.MatchString(str)
} }
// IsAllUpper check if the string is all upper case letters A-Z // IsAllUpper check if the string is all upper case letters A-Z
@@ -62,11 +75,9 @@ func ContainLower(str string) bool {
return false return false
} }
var containLetterRegexMatcher *regexp.Regexp = regexp.MustCompile(`[a-zA-Z]`)
// ContainLetter check if the string contain at least one letter // ContainLetter check if the string contain at least one letter
func ContainLetter(str string) bool { func ContainLetter(str string) bool {
return containLetterRegexMatcher.MatchString(str) return letterRegexMatcher.MatchString(str)
} }
// IsJSON checks if the string is valid JSON // IsJSON checks if the string is valid JSON
@@ -86,11 +97,9 @@ func IsFloatStr(str string) bool {
return e == nil return e == nil
} }
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(str string) bool { func IsIntStr(str string) bool {
return isIntStrRegexMatcher.MatchString(str) return intStrMatcher.MatchString(str)
} }
// IsIp check if the string is a ip address. // IsIp check if the string is a ip address.
@@ -125,8 +134,6 @@ func IsPort(str string) bool {
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. // IsUrl check if the string is url.
func IsUrl(str string) bool { func IsUrl(str string) bool {
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
@@ -143,64 +150,48 @@ func IsUrl(str string) bool {
return false return false
} }
return isUrlRegexMatcher.MatchString(str) return urlMatcher.MatchString(str)
} }
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.
func IsDns(dns string) bool { func IsDns(dns string) bool {
return isDnsRegexMatcher.MatchString(dns) return dnsMatcher.MatchString(dns)
} }
var isEmailRegexMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
// IsEmail check if the string is a email address. // IsEmail check if the string is a email address.
func IsEmail(email string) bool { func IsEmail(email string) bool {
return isEmailRegexMatcher.MatchString(email) return emailMatcher.MatchString(email)
} }
var isChineseMobileRegexMatcher *regexp.Regexp = regexp.MustCompile("^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$")
// IsChineseMobile check if the string is chinese mobile number. // IsChineseMobile check if the string is chinese mobile number.
func IsChineseMobile(mobileNum string) bool { func IsChineseMobile(mobileNum string) bool {
return isChineseMobileRegexMatcher.MatchString(mobileNum) return chineseMobileMatcher.MatchString(mobileNum)
} }
var isChineseIdNumRegexMatcher *regexp.Regexp = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`)
// IsChineseIdNum check if the string is chinese id number. // IsChineseIdNum check if the string is chinese id number.
func IsChineseIdNum(id string) bool { func IsChineseIdNum(id string) bool {
return isChineseIdNumRegexMatcher.MatchString(id) return chineseIdMatcher.MatchString(id)
} }
var containChineseRegexMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
// ContainChinese check if the string contain mandarin chinese. // ContainChinese check if the string contain mandarin chinese.
func ContainChinese(s string) bool { func ContainChinese(s string) bool {
return containChineseRegexMatcher.MatchString(s) return chineseMatcher.MatchString(s)
} }
var isChinesePhoneRegexMatcher *regexp.Regexp = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}`)
// IsChinesePhone check if the string is chinese phone number. // IsChinesePhone check if the string is chinese phone number.
// Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx // Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx
func IsChinesePhone(phone string) bool { func IsChinesePhone(phone string) bool {
return isChinesePhoneRegexMatcher.MatchString(phone) return chinesePhoneMatcher.MatchString(phone)
} }
var isCreditCardRegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`)
// IsCreditCard check if the string is credit card. // IsCreditCard check if the string is credit card.
func IsCreditCard(creditCart string) bool { func IsCreditCard(creditCart string) bool {
return isCreditCardRegexMatcher.MatchString(creditCart) return creditCardMatcher.MatchString(creditCart)
} }
var isBase64RegexMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`)
// IsBase64 check if the string is base64 string. // IsBase64 check if the string is base64 string.
func IsBase64(base64 string) bool { func IsBase64(base64 string) bool {
return isBase64RegexMatcher.MatchString(base64) return base64Matcher.MatchString(base64)
} }
// IsEmptyString check if the string is empty. // IsEmptyString check if the string is empty.
@@ -283,3 +274,40 @@ func IsZeroValue(value any) bool {
return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface()) return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface())
} }
// IsGBK check if data encoding is gbk
// Note: this function is implemented by whether double bytes fall within the encoding range of gbk,
// while each byte of utf-8 encoding format falls within the encoding range of gbk.
// Therefore, utf8.valid() should be called first to check whether it is not utf-8 encoding,
// and then call IsGBK() to check gbk encoding. like below
/**
data := []byte("你好")
if utf8.Valid(data) {
fmt.Println("data encoding is utf-8")
}else if(IsGBK(data)) {
fmt.Println("data encoding is GBK")
}
fmt.Println("data encoding is unknown")
**/
func IsGBK(data []byte) bool {
i := 0
for i < len(data) {
if data[i] <= 0xff {
i++
continue
} else {
if data[i] >= 0x81 &&
data[i] <= 0xfe &&
data[i+1] >= 0x40 &&
data[i+1] <= 0xfe &&
data[i+1] != 0xf7 {
i += 2
continue
} else {
return false
}
}
}
return true
}

View File

@@ -4,8 +4,10 @@ import (
"fmt" "fmt"
"testing" "testing"
"time" "time"
"unicode/utf8"
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
"golang.org/x/text/encoding/simplifiedchinese"
) )
func TestIsAllUpper(t *testing.T) { func TestIsAllUpper(t *testing.T) {
@@ -388,3 +390,13 @@ func TestIsZeroValue(t *testing.T) {
assert.Equal(false, IsZeroValue(value)) assert.Equal(false, IsZeroValue(value))
} }
} }
func TestIsGBK(t *testing.T) {
assert := internal.NewAssert(t, "TestIsGBK")
str := "你好"
gbkData, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str))
assert.Equal(true, IsGBK(gbkData))
assert.Equal(false, utf8.Valid(gbkData))
}