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

Compare commits

...

4 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
7 changed files with 108 additions and 66 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.3-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.3-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

@@ -11,10 +11,12 @@ import (
// After creates a function that invokes func once it's called n or more times // After creates a function that invokes func once it's called n or more times
func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
MustBeFunction(fn)
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
n-- n--
if n < 1 { if n < 1 {
return invokeFunc(fn, args...) return unsafeInvokeFunc(fn, args...)
} }
return nil return nil
} }
@@ -22,11 +24,12 @@ func After(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Before creates a function that invokes func once it's called less than n times // Before creates a function that invokes func once it's called less than n times
func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value { func Before(n int, fn interface{}) func(args ...interface{}) []reflect.Value {
// Catch programming error while constructing the closure
MustBeFunction(fn)
var res []reflect.Value var res []reflect.Value
return func(args ...interface{}) []reflect.Value { return func(args ...interface{}) []reflect.Value {
if n > 0 { if n > 0 {
res = invokeFunc(fn, args...) res = unsafeInvokeFunc(fn, args...)
} }
if n <= 0 { if n <= 0 {
fn = nil fn = nil
@@ -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,36 +11,33 @@ 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 {
fmt.Println("error: ", err) log.Fatal(err)
//t.FailNow() t.FailNow()
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) fmt.Println("response: ", resp.StatusCode, string(body))
} }
func TestHttpPost(t *testing.T) { func TestHttpPost(t *testing.T) {
url := "http://api.postcodes.io/postcodes" url := "https://jsonplaceholder.typicode.com/todos"
type Postcode struct {
Postcodes []string `json:"postcodes"`
}
postcode := Postcode{[]string{"OX49 5NU"}}
bodyParams, _ := json.Marshal(postcode)
header := map[string]string{ header := map[string]string{
"Content-Type": "application/json", "Content-Type": "application/json",
} }
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)
@@ -51,34 +48,55 @@ 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 {
fmt.Println("error: ", err) log.Fatal(err)
//t.FailNow() t.FailNow()
}
body, _ := ioutil.ReadAll(resp.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) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(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 {
fmt.Println("error: ", err) log.Fatal(err)
//t.FailNow() t.FailNow()
} }
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response: ", resp.StatusCode, string(body)) fmt.Println("response: ", resp.StatusCode, string(body))
@@ -100,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)
} }