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

Compare commits

...

17 Commits

Author SHA1 Message Date
dudaodong
051f20caef release: v1.1.4, merge pr7 and pr8 2021-12-30 20:25:20 +08:00
donutloop
613785b07c function: catch earlier programming error (#8)
Place at first line of the function body a function type safe guard
check.
2021-12-30 20:22:00 +08:00
donutloop
0b0eb695e8 datetime: optimized func calls (#7)
Replace time parsing calls with less expensive operations
2021-12-30 10:29:10 +08:00
dudaodong
745082fff1 fix: update api url for http timeout issue in the task of github actions 2021-12-29 11:39:48 +08:00
dudaodong
24b8da360e release: v1.1.3, merge pr5 and pr6 2021-12-29 09:55:42 +08:00
donutloop
b106c428ae Every: use int counter (#6)
Use counter to verify all elements passed the perdicate func.
Replace slice of indexes with int counter.
2021-12-29 09:51:50 +08:00
dudaodong
8b1171d0cb fmt: go fmt for request_test.go 2021-12-28 19:28:55 +08:00
donutloop
ab012f2545 LowerFirst: use slicing and utf8 func tools (#5)
Replace looping with slicing and utf8 func tools operations.
2021-12-28 19:25:44 +08:00
dudaodong
a952cb208a release v1.1.2 2021-12-28 11:28:00 +08:00
dudaodong
f239a8ca8e fix: fix request timeout issue 2021-12-28 11:04:57 +08:00
dudaodong
5c6626b37e fmt: go fmt for function.go 2021-12-28 11:00:43 +08:00
dudaodong
16b5101600 fix: fix TestHttpPost timeout issue 2021-12-28 10:56:54 +08:00
dudaodong
ec983b7aa6 fix: fix
Intersection slice issue
2021-12-28 10:16:53 +08:00
dudaodong
56fc2aabd6 fmt: fix lint issue 2021-12-28 10:08:08 +08:00
dudaodong
0ddd52225b Merge branch 'main' of github.com:duke-git/lancet into main 2021-12-28 10:07:33 +08:00
donutloop
3919160e38 Optimized: Capitalize (#4)
* Use unicode.ToUpper func to convert first letter in string to upper case
letter.

* Preallocate memory with correct length and cap because the final
  string is not changing.
2021-12-28 10:04:15 +08:00
dudaodong
40ec5bc0f6 fmt: fix lint issue 2021-12-27 20:40:54 +08:00
10 changed files with 121 additions and 92 deletions

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;"> <div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.0-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-1.1.4-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)

View File

@@ -6,7 +6,7 @@
<div align="center" style="text-align: center;"> <div align="center" style="text-align: center;">
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-1.1.0-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-1.1.4-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet) [![GoDoc](https://godoc.org/github.com//duke-git/lancet?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet)](https://goreportcard.com/report/github.com/duke-git/lancet)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet) [![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)

View File

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

View File

@@ -135,7 +135,10 @@ func TestFormatStrToTime(t *testing.T) {
"2021/01"} "2021/01"}
for i := 0; i < len(cases); i++ { for i := 0; i < len(cases); i++ {
res := FormatStrToTime(datetimeStr[i], cases[i]) res, err := FormatStrToTime(datetimeStr[i], cases[i])
if err != nil {
t.Fatal(err)
}
expected, _ := time.Parse(formats[i], datetimeStr[i]) expected, _ := time.Parse(formats[i], datetimeStr[i])
if res != expected { if res != expected {
utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res) utils.LogFailedTestInfo(t, "FormatTimeToStr", cases[i], expected, res)

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by MIT license // Use of this source code is governed by MIT license
// Package function implements some functions for control the function execution and some is for functional programming. // Package function implements some functions for control the function execution and some is for functional programming.
package function package function
import ( import (
@@ -12,10 +11,12 @@ import (
// After creates a function that invokes func once it's called n or more times // After creates a function that invokes func once it's called n or more times
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
MustBeFunction(fn)
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
n-- n--
if n < 1 { if n < 1 {
return invokeFunc(fn, args...) return unsafeInvokeFunc(fn, args...)
} }
return nil return nil
} }
@@ -23,11 +24,12 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Before creates a function that invokes func once it's called less than n times // Before creates a function that invokes func once it's called less than n times
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
MustBeFunction(fn)
var res []reflect.Value var res []reflect.Value
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
if n > 0 { if n > 0 {
res = invokeFunc(fn, args...) res = unsafeInvokeFunc(fn, args...)
} }
if n <= 0 { if n <= 0 {
fn = nil fn = nil
@@ -37,9 +39,10 @@ func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
} }
} }
// Fn is for curry function which is func(...interface{}) interface{}
type Fn func(...interface{}) interface{} type Fn func(...interface{}) interface{}
// Curry make a curryed function // Curry make a curry function
func (f Fn) Curry(i interface{}) func(...interface{}) interface{} { func (f Fn) Curry(i interface{}) func(...interface{}) interface{} {
return func(values ...interface{}) interface{} { return func(values ...interface{}) interface{} {
v := append([]interface{}{i}, values...) v := append([]interface{}{i}, values...)
@@ -69,11 +72,12 @@ func Delay(delay time.Duration, fn interface{}, args ...interface{}) {
// Schedule invoke function every duration time, util close the returned bool chan // Schedule invoke function every duration time, util close the returned bool chan
func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool { func Schedule(d time.Duration, fn interface{}, args ...interface{}) chan bool {
// Catch programming error while constructing the closure
MustBeFunction(fn)
quit := make(chan bool) quit := make(chan bool)
go func() { go func() {
for { for {
invokeFunc(fn, args...) unsafeInvokeFunc(fn, args...)
select { select {
case <-time.After(d): case <-time.After(d):
case <-quit: case <-quit:

View File

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

View File

@@ -11,16 +11,12 @@ import (
) )
func TestHttpGet(t *testing.T) { func TestHttpGet(t *testing.T) {
_, e := HttpGet("", nil) url := "https://jsonplaceholder.typicode.com/todos/1"
if e == nil { header := map[string]string{
t.FailNow() "Content-Type": "application/json",
} }
url := "https://gutendex.com/books?" resp, err := HttpGet(url, header)
queryParams := make(map[string]interface{})
queryParams["ids"] = "1"
resp, err := HttpGet(url, nil, queryParams)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
@@ -28,23 +24,20 @@ func TestHttpGet(t *testing.T) {
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) fmt.Println("response: ", resp.StatusCode, string(body))
} }
func TestHttpPost(t *testing.T) { func TestHttpPost(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users" url := "https://jsonplaceholder.typicode.com/todos"
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{
"test",
"test@test.com",
}
bodyParams, _ := json.Marshal(user)
header := map[string]string{ header := map[string]string{
"Content-Type": "application/json", "Content-Type": "application/json",
} }
type Todo struct {
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, "TestAddToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPost(url, header, nil, bodyParams) resp, err := HttpPost(url, header, nil, bodyParams)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -55,19 +48,18 @@ func TestHttpPost(t *testing.T) {
} }
func TestHttpPut(t *testing.T) { func TestHttpPut(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users/10420" url := "https://jsonplaceholder.typicode.com/todos/1"
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{
"test",
"test@test.com",
}
bodyParams, _ := json.Marshal(user)
header := map[string]string{ header := map[string]string{
"Content-Type": "application/json", "Content-Type": "application/json",
} }
type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, 1, "TestPutToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPut(url, header, nil, bodyParams) resp, err := HttpPut(url, header, nil, bodyParams)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -77,8 +69,30 @@ func TestHttpPut(t *testing.T) {
fmt.Println("response: ", resp.StatusCode, string(body)) fmt.Println("response: ", resp.StatusCode, string(body))
} }
func TestHttpPatch(t *testing.T) {
url := "https://jsonplaceholder.typicode.com/todos/1"
header := map[string]string{
"Content-Type": "application/json",
}
type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
}
todo := Todo{1, 1, "TestPatchToDo"}
bodyParams, _ := json.Marshal(todo)
resp, err := HttpPatch(url, header, nil, bodyParams)
if err != nil {
log.Fatal(err)
t.FailNow()
}
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body))
}
func TestHttpDelete(t *testing.T) { func TestHttpDelete(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users/10420" url := "https://jsonplaceholder.typicode.com/todos/1"
resp, err := HttpDelete(url) resp, err := HttpDelete(url)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -104,25 +118,29 @@ func TestConvertMapToQueryString(t *testing.T) {
} }
func TestParseResponse(t *testing.T) { func TestParseResponse(t *testing.T) {
url := "http://public-api-v1.aspirantzhang.com/users" url := "https://jsonplaceholder.typicode.com/todos/1"
type User struct { header := map[string]string{
Id int `json:"id"` "Content-Type": "application/json",
Name string `json:"name"`
Email string `json:"email"`
} }
type UserResp struct {
Data []User `json:"data"` resp, err := HttpGet(url, header)
}
resp, err := HttpGet(url)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
} }
userResp := &UserResp{}
err = ParseHttpResponse(resp, userResp) type Todo struct {
Id int `json:"id"`
UserId int `json:"userId"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
toDoResp := &Todo{}
err = ParseHttpResponse(resp, toDoResp)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
t.FailNow() t.FailNow()
} }
fmt.Println(userResp.Data) fmt.Println("response: ", toDoResp)
} }

View File

@@ -100,15 +100,15 @@ func Every(slice, function interface{}) bool {
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String()) panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
} }
var indexes []int var currentLength int
for i := 0; i < sv.Len(); i++ { for i := 0; i < sv.Len(); i++ {
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0] flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
if flag.Bool() { if flag.Bool() {
indexes = append(indexes, i) currentLength++
} }
} }
return len(indexes) == sv.Len() return currentLength == sv.Len()
} }
// Some return true if any of the values in the list pass the predicate function. // Some return true if any of the values in the list pass the predicate function.
@@ -474,7 +474,7 @@ func Intersection(slices ...interface{}) interface{} {
} }
res := Reduce(slices, reduceFunc, nil) res := Reduce(slices, reduceFunc, nil)
return Union(res) return Unique(res)
} }
// ReverseSlice return slice of element order is reversed to the given slice // ReverseSlice return slice of element order is reversed to the given slice

View File

@@ -7,6 +7,8 @@ package strutil
import ( import (
"regexp" "regexp"
"strings" "strings"
"unicode"
"unicode/utf8"
) )
// CamelCase covert string to camelCase string. // CamelCase covert string to camelCase string.
@@ -40,18 +42,16 @@ func Capitalize(s string) string {
return "" return ""
} }
res := "" out := make([]rune, len(s))
for i, v := range []rune(s) { for i, v := range s {
if i == 0 { if i == 0 {
if v >= 97 && v <= 122 { out[i] = unicode.ToUpper(v)
v -= 32
}
res += string(v)
} else { } else {
res += strings.ToLower(string(v)) out[i] = unicode.ToLower(v)
} }
} }
return res
return string(out)
} }
// LowerFirst converts the first character of string to lower case. // LowerFirst converts the first character of string to lower case.
@@ -60,20 +60,10 @@ func LowerFirst(s string) string {
return "" return ""
} }
res := "" r, size := utf8.DecodeRuneInString(s)
for i, v := range []rune(s) { r = unicode.ToLower(r)
if i == 0 {
if v >= 65 && v <= 96 { return string(r) + s[size:]
v += 32
res += string(v)
} else {
return s
}
} else {
res += string(v)
}
}
return res
} }
// PadEnd pads string on the right side if it's shorter than size. // PadEnd pads string on the right side if it's shorter than size.

View File

@@ -70,6 +70,7 @@ func TestLowerFirst(t *testing.T) {
lowerFirst(t, "foo", "foo") lowerFirst(t, "foo", "foo")
lowerFirst(t, "BAR", "bAR") lowerFirst(t, "BAR", "bAR")
lowerFirst(t, "FOo", "fOo") lowerFirst(t, "FOo", "fOo")
lowerFirst(t, "FOo大", "fOo大")
} }
func lowerFirst(t *testing.T, test string, expected string) { func lowerFirst(t *testing.T, test string, expected string) {