diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..c767534 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,43 @@ +name: Test + +on: + push: + branches: [ "master" ] + tags: [ "v*" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.18' + + - name: Format + run: gofmt -l . && test -z "$(gofmt -l .)" + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -race -coverprofile=coverage.out -covermode=atomic + + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Vet + run: go vet -v ./... + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9468788..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - 1.18.x - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - - go vet -v ./... - - gofmt -l . && test -z $(gofmt -l .) - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index d5c7ef5..74b0f69 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # pt -[![Build Status](https://travis-ci.com/gochore/pt.svg?branch=master)](https://travis-ci.com/gochore/pt) -[![codecov](https://codecov.io/gh/gochore/pt/branch/master/graph/badge.svg)](https://codecov.io/gh/gochore/pt) +[![Go Reference](https://pkg.go.dev/badge/github.com/gochore/pt.svg)](https://pkg.go.dev/github.com/gochore/pt) +[![Actions](https://github.com/gochore/pt/actions/workflows/test.yaml/badge.svg)](https://github.com/gochore/pt/actions) +[![Codecov](https://codecov.io/gh/gochore/pt/branch/master/graph/badge.svg)](https://codecov.io/gh/gochore/pt) [![Go Report Card](https://goreportcard.com/badge/github.com/gochore/pt)](https://goreportcard.com/report/github.com/gochore/pt) [![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gochore/pt)](https://github.com/gochore/pt/blob/master/go.mod) [![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/gochore/pt)](https://github.com/gochore/pt/releases) @@ -16,181 +17,205 @@ package main import "github.com/gochore/pt" func main() { - // wrong - f(&100) // can not compile + // 💀 It cannot work because Go does not allow taking the address of a constant or literal. + f(&100) - // bad + // 😕 It works, but it requires two lines and declares a variable that could pollute the namespace. v := 100 f(&v) - // good + // 😊 It works. Only one line and no new variables. + // But you have to use different functions for different types. + // It's the only way to do it before Go1.18. f(pt.Int(100)) - // good, with generics, need go1.18+ + // 🤩 It works. Only one line and no new variables, and a single function for all types. + // It's based on generics, so it requires Go1.18 and above. f(pt.P(100)) } -func f(*int) { +func f(p *int) { + // 💀 It could panic if p is nil. + println(*p) + // 😕 It's safe, but it requires multiple lines and declares a variable that could pollute the namespace. + v := 0 + if p != nil { + v = *p + } + println(v) + + // 🤩 It's safe. Only one line and no new variables. + // It's based on generics, so it requires Go1.18 and above. + println(pt.V(p)) } ``` ## Document -### go1.18 and above +### Go1.18 and later #### func P ```go -func P[V any](v V) *V +func P[T any](v T) *T +``` +P returns pointer of v. +It's a short form of "Pointer" or "GetPointer". + +#### func V + +```go +func V[T any](p *T) T ``` -P return pointer of v +V returns value of p. If p is nil, return zero value of T. +It's a short form of "Value" or "GetValue". -### before go1.18 +### Before Go1.18 (deprecated) -#### func Bool +#### func Bool ```go func Bool(v bool) *bool ``` -Bool return pointer of bool +Bool returns pointer of bool -#### func Byte +#### func Byte ```go func Byte(v byte) *byte ``` -Byte return pointer of byte +Byte returns pointer of byte -#### func Complex128 +#### func Complex128 ```go func Complex128(v complex128) *complex128 ``` -Complex128 return pointer of complex128 +Complex128 returns pointer of complex128 -#### func Complex64 +#### func Complex64 ```go func Complex64(v complex64) *complex64 ``` -Complex64 return pointer of complex64 +Complex64 returns pointer of complex64 -#### func Duration +#### func Duration ```go func Duration(v time.Duration) *time.Duration ``` -Duration return pointer of time.Duration +Duration returns pointer of time.Duration -#### func Float32 +#### func Float32 ```go func Float32(v float32) *float32 ``` -Float32 return pointer of float32 +Float32 returns pointer of float32 -#### func Float64 +#### func Float64 ```go func Float64(v float64) *float64 ``` -Float64 return pointer of float64 +Float64 returns pointer of float64 -#### func Int +#### func Int ```go func Int(v int) *int ``` -Int return pointer of int +Int returns pointer of int -#### func Int16 +#### func Int16 ```go func Int16(v int16) *int16 ``` -Int16 return pointer of int16 +Int16 returns pointer of int16 -#### func Int32 +#### func Int32 ```go func Int32(v int32) *int32 ``` -Int32 return pointer of int32 +Int32 returns pointer of int32 -#### func Int64 +#### func Int64 ```go func Int64(v int64) *int64 ``` -Int64 return pointer of int64 +Int64 returns pointer of int64 -#### func Int8 +#### func Int8 ```go func Int8(v int8) *int8 ``` -Int8 return pointer of int8 +Int8 returns pointer of int8 -#### func Rune +#### func Rune ```go func Rune(v rune) *rune ``` -Rune return pointer of rune +Rune returns pointer of rune -#### func String +#### func String ```go func String(v string) *string ``` -String return pointer of string +String returns pointer of string -#### func Time +#### func Time ```go func Time(v time.Time) *time.Time ``` -Time return pointer of time.Time +Time returns pointer of time.Time -#### func Uint +#### func Uint ```go func Uint(v uint) *uint ``` -Uint return pointer of uint +Uint returns pointer of uint -#### func Uint16 +#### func Uint16 ```go func Uint16(v uint16) *uint16 ``` -Uint16 return pointer of uint16 +Uint16 returns pointer of uint16 -#### func Uint32 +#### func Uint32 ```go func Uint32(v uint32) *uint32 ``` -Uint32 return pointer of uint32 +Uint32 returns pointer of uint32 -#### func Uint64 +#### func Uint64 ```go func Uint64(v uint64) *uint64 ``` -Uint64 return pointer of uint64 +Uint64 returns pointer of uint64 -#### func Uint8 +#### func Uint8 ```go func Uint8(v uint8) *uint8 ``` -Uint8 return pointer of uint8 +Uint8 returns pointer of uint8 -#### func Uintptr +#### func Uintptr ```go func Uintptr(v uintptr) *uintptr ``` -Uintptr return pointer of uintptr +Uintptr returns pointer of uintptr diff --git a/_example/main.go b/_example/main.go deleted file mode 100644 index d6217b8..0000000 --- a/_example/main.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import "github.com/gochore/pt" - -func main() { - // wrong - f(&100) // can not compile - - // bad - v := 100 - f(&v) - - // good - f(pt.Int(100)) - - // good, with generics, need go1.18+ - f(pt.P(100)) -} - -func f(*int) { - -} diff --git a/builtin.go b/builtin.go index 44bbc09..46418ad 100644 --- a/builtin.go +++ b/builtin.go @@ -1,115 +1,115 @@ package pt // Deprecated: use P with generics in go1.18+ -// Bool return pointer of bool +// Bool returns pointer of bool func Bool(v bool) *bool { return &v } // Deprecated: use P with generics in go1.18+ -// Uint8 return pointer of uint8 +// Uint8 returns pointer of uint8 func Uint8(v uint8) *uint8 { return &v } // Deprecated: use P with generics in go1.18+ -// Uint16 return pointer of uint16 +// Uint16 returns pointer of uint16 func Uint16(v uint16) *uint16 { return &v } // Deprecated: use P with generics in go1.18+ -// Uint32 return pointer of uint32 +// Uint32 returns pointer of uint32 func Uint32(v uint32) *uint32 { return &v } // Deprecated: use P with generics in go1.18+ -// Uint64 return pointer of uint64 +// Uint64 returns pointer of uint64 func Uint64(v uint64) *uint64 { return &v } // Deprecated: use P with generics in go1.18+ -// Int8 return pointer of int8 +// Int8 returns pointer of int8 func Int8(v int8) *int8 { return &v } // Deprecated: use P with generics in go1.18+ -// Int16 return pointer of int16 +// Int16 returns pointer of int16 func Int16(v int16) *int16 { return &v } // Deprecated: use P with generics in go1.18+ -// Int32 return pointer of int32 +// Int32 returns pointer of int32 func Int32(v int32) *int32 { return &v } // Deprecated: use P with generics in go1.18+ -// Int64 return pointer of int64 +// Int64 returns pointer of int64 func Int64(v int64) *int64 { return &v } // Deprecated: use P with generics in go1.18+ -// Float32 return pointer of float32 +// Float32 returns pointer of float32 func Float32(v float32) *float32 { return &v } // Deprecated: use P with generics in go1.18+ -// Float64 return pointer of float64 +// Float64 returns pointer of float64 func Float64(v float64) *float64 { return &v } // Deprecated: use P with generics in go1.18+ -// Complex64 return pointer of complex64 +// Complex64 returns pointer of complex64 func Complex64(v complex64) *complex64 { return &v } // Deprecated: use P with generics in go1.18+ -// Complex128 return pointer of complex128 +// Complex128 returns pointer of complex128 func Complex128(v complex128) *complex128 { return &v } // Deprecated: use P with generics in go1.18+ -// String return pointer of string +// String returns pointer of string func String(v string) *string { return &v } // Deprecated: use P with generics in go1.18+ -// Int return pointer of int +// Int returns pointer of int func Int(v int) *int { return &v } // Deprecated: use P with generics in go1.18+ -// Uint return pointer of uint +// Uint returns pointer of uint func Uint(v uint) *uint { return &v } // Deprecated: use P with generics in go1.18+ -// Uintptr return pointer of uintptr +// Uintptr returns pointer of uintptr func Uintptr(v uintptr) *uintptr { return &v } // Deprecated: use P with generics in go1.18+ -// Byte return pointer of byte +// Byte returns pointer of byte func Byte(v byte) *byte { return &v } // Deprecated: use P with generics in go1.18+ -// Rune return pointer of rune +// Rune returns pointer of rune func Rune(v rune) *rune { return &v } diff --git a/generic.go b/generic.go index fc369fa..b86467c 100644 --- a/generic.go +++ b/generic.go @@ -2,7 +2,17 @@ package pt -// P return pointer of v -func P[V any](v V) *V { +// P returns pointer of v. +// It's a short form of "Pointer" or "GetPointer". +func P[T any](v T) *T { return &v } + +// V returns value of p. If p is nil, return zero value of T. +// It's a short form of "Value" or "GetValue". +func V[T any](p *T) T { + if p == nil { + return *new(T) + } + return *p +} diff --git a/generic_test.go b/generic_test.go index e56e25b..c756d3a 100644 --- a/generic_test.go +++ b/generic_test.go @@ -10,37 +10,51 @@ import ( ) func TestP(t *testing.T) { - type args struct { - v any - } - tests := []struct { - name string - args args - }{ - { - name: "int", - args: args{ - v: 1, - }, - }, - { - name: "float64", - args: args{ - v: 1.1, - }, - }, - { - name: "time", - args: args{ - v: time.Now(), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := pt.P(tt.args.v); *got != tt.args.v { - t.Errorf("P() = %v, want %v", got, tt.args.v) - } - }) - } + t.Run("int", func(t *testing.T) { + if got := pt.P(1); *got != 1 { + t.Errorf("P() = %v, want %v", *got, 1) + } + }) + t.Run("float64", func(t *testing.T) { + if got := pt.P(1.1); *got != 1.1 { + t.Errorf("P() = %v, want %v", *got, 1) + } + }) + t.Run("time", func(t *testing.T) { + now := time.Now() + if got := pt.P(now); *got != now { + t.Errorf("P() = %v, want %v", *got, 1) + } + }) +} + +func TestV(t *testing.T) { + t.Run("int", func(t *testing.T) { + if got := pt.V(pt.P(1)); got != 1 { + t.Errorf("V() = %v, want %v", got, 1) + } + }) + t.Run("float64", func(t *testing.T) { + if got := pt.V(pt.P(1.1)); got != 1.1 { + t.Errorf("V() = %v, want %v", got, 1) + } + }) + t.Run("time", func(t *testing.T) { + now := time.Now() + if got := pt.V(pt.P(now)); got != now { + t.Errorf("V() = %v, want %v", got, 1) + } + }) + t.Run("nil int", func(t *testing.T) { + var i *int + if got := pt.V(i); got != 0 { + t.Errorf("V() = %v, want %v", got, 0) + } + }) + t.Run("nil string", func(t *testing.T) { + var s *string + if got := pt.V(s); got != "" { + t.Errorf("V() = %v, want %v", got, "") + } + }) } diff --git a/time.go b/time.go index 8870403..b030675 100644 --- a/time.go +++ b/time.go @@ -3,13 +3,13 @@ package pt import "time" // Deprecated: use P with generics in go1.18+ -// Time return pointer of time.Time +// Time returns pointer of time.Time func Time(v time.Time) *time.Time { return &v } // Deprecated: use P with generics in go1.18+ -// Duration return pointer of time.Duration +// Duration returns pointer of time.Duration func Duration(v time.Duration) *time.Duration { return &v }