Skip to content

Commit 52bdd50

Browse files
authored
Add pointer module (#24)
1 parent b918cae commit 52bdd50

File tree

9 files changed

+360
-0
lines changed

9 files changed

+360
-0
lines changed

.github/dependabot.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ updates:
2929
patterns:
3030
- "*"
3131

32+
- package-ecosystem: "gomod"
33+
directory: "/pointer"
34+
schedule:
35+
interval: "monthly"
36+
groups:
37+
dependencies:
38+
patterns:
39+
- "*"
40+
3241
- package-ecosystem: "gomod"
3342
directory: "/probe"
3443
schedule:

.github/workflows/pointer.yml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: check-pointer
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
paths:
8+
- "pointer/**"
9+
- ".github/workflows/pointer.yml"
10+
pull_request:
11+
branches:
12+
- "*"
13+
paths:
14+
- "pointer/**"
15+
- ".github/workflows/pointer.yml"
16+
17+
jobs:
18+
19+
clean:
20+
name: clean
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 2
23+
strategy:
24+
matrix:
25+
go: [stable]
26+
fail-fast: true
27+
steps:
28+
- name: Checkout repo
29+
uses: actions/checkout@v4
30+
- name: Set up Go
31+
uses: actions/setup-go@v5
32+
with:
33+
go-version: ${{ matrix.go }}
34+
cache: true
35+
- name: Run go mod tidy
36+
working-directory: pointer
37+
run: go mod tidy && git diff --exit-code
38+
- name: Run go mod verify
39+
working-directory: pointer
40+
run: go mod verify
41+
- name: Run formatting
42+
working-directory: pointer
43+
run: go run golang.org/x/tools/cmd/goimports@latest -w . && git diff --exit-code
44+
45+
lint:
46+
name: lint
47+
runs-on: ubuntu-latest
48+
timeout-minutes: 4
49+
strategy:
50+
matrix:
51+
go: [stable]
52+
fail-fast: true
53+
steps:
54+
- name: Checkout repo
55+
uses: actions/checkout@v4
56+
- name: Set up Go
57+
uses: actions/setup-go@v5
58+
with:
59+
go-version: ${{ matrix.go }}
60+
cache: true
61+
- name: Run go linting
62+
uses: golangci/golangci-lint-action@v4
63+
with:
64+
version: latest
65+
args: --timeout=4m
66+
working-directory: pointer
67+
68+
test:
69+
name: test
70+
runs-on: ubuntu-latest
71+
timeout-minutes: 2
72+
strategy:
73+
matrix:
74+
go: [stable]
75+
fail-fast: true
76+
steps:
77+
- name: Checkout repo
78+
uses: actions/checkout@v4
79+
- name: Set up Go
80+
uses: actions/setup-go@v5
81+
with:
82+
go-version: ${{ matrix.go }}
83+
cache: true
84+
- name: Run tests
85+
working-directory: pointer
86+
run: go test -shuffle=on -v -count=1 -race -failfast -timeout=30s -covermode=atomic -coverprofile=coverage.out ./...
87+
88+
benchmark:
89+
name: benchmark
90+
runs-on: ubuntu-latest
91+
timeout-minutes: 2
92+
strategy:
93+
matrix:
94+
go: [stable]
95+
fail-fast: true
96+
steps:
97+
- name: Checkout repo
98+
uses: actions/checkout@v4
99+
- name: Set up Go
100+
uses: actions/setup-go@v5
101+
with:
102+
go-version: ${{ matrix.go }}
103+
cache: true
104+
- name: Run benchmarks
105+
working-directory: pointer
106+
run: go test -v -shuffle=on -run=- -bench=. -benchtime=1x -timeout=10s ./...
107+
108+
build:
109+
name: build
110+
runs-on: ubuntu-latest
111+
timeout-minutes: 4
112+
strategy:
113+
matrix:
114+
go: [stable]
115+
fail-fast: true
116+
steps:
117+
- name: Checkout repo
118+
uses: actions/checkout@v4
119+
- name: Set up Go
120+
uses: actions/setup-go@v5
121+
with:
122+
go-version: ${{ matrix.go }}
123+
cache: true
124+
- name: Run go generate
125+
working-directory: pointer
126+
run: go generate ./... && git diff --exit-code
127+
- name: Run go build
128+
working-directory: pointer
129+
run: go build -o /dev/null ./...

pointer/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Ben Wakeford
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

pointer/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# pointer
2+
[![go reference](https://pkg.go.dev/badge/github.com/wafer-bw/go-toolbox/pointer.svg)](https://pkg.go.dev/github.com/wafer-bw/go-toolbox/pointer)
3+
[![Go Report Card](https://goreportcard.com/badge/github.com/wafer-bw/go-toolbox/pointer)](https://goreportcard.com/report/github.com/wafer-bw/go-toolbox/pointer)

pointer/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/wafer-bw/go-toolbox/pointer
2+
3+
go 1.24.2

pointer/go.sum

Whitespace-only changes.

pointer/pointer.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package pointer
2+
3+
type IsZeroer interface {
4+
IsZero() bool
5+
}
6+
7+
// To returns a pointer to v.
8+
func To[T any](v T) *T {
9+
return &v
10+
}
11+
12+
// ToOrNil returns a pointer to v if it is not zero or nil otherwise.
13+
//
14+
// If v implements the [IsZeroer] interface it will use that to determine if it
15+
// is zero.
16+
func ToOrNil[T comparable](v T) *T {
17+
if z, ok := any(v).(IsZeroer); ok {
18+
if z.IsZero() {
19+
return nil
20+
}
21+
return &v
22+
}
23+
24+
var z T
25+
if v == z {
26+
return nil
27+
}
28+
29+
return &v
30+
}
31+
32+
// From returns the value pointed at by p or the zero value of p's type if it is
33+
// nil.
34+
func From[T any](p *T) T {
35+
if p == nil {
36+
var z T
37+
return z
38+
}
39+
40+
return *p
41+
}

pointer/pointer_benchmark_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package pointer_test
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/wafer-bw/go-toolbox/pointer"
8+
)
9+
10+
func BenchmarkTo(b *testing.B) {
11+
v := true
12+
for b.Loop() {
13+
pv := pointer.To(v)
14+
_ = pv
15+
}
16+
}
17+
18+
func BenchmarkToOrNil(b *testing.B) {
19+
b.Run("zero value to nil", func(b *testing.B) {
20+
var v string
21+
for b.Loop() {
22+
pv := pointer.ToOrNil(v)
23+
_ = pv
24+
}
25+
})
26+
27+
b.Run("non-zero value to pointer", func(b *testing.B) {
28+
v := "non-zero"
29+
for b.Loop() {
30+
pv := pointer.ToOrNil(v)
31+
_ = pv
32+
}
33+
})
34+
35+
b.Run("non-zero value to pointer with IsZero", func(b *testing.B) {
36+
v := time.Date(2014, 6, 25, 12, 24, 40, 0, time.UTC)
37+
for b.Loop() {
38+
pv := pointer.ToOrNil(v)
39+
_ = pv
40+
}
41+
})
42+
43+
b.Run("zero value to pointer with IsZero", func(b *testing.B) {
44+
v := time.Time{}
45+
for b.Loop() {
46+
pv := pointer.ToOrNil(v)
47+
_ = pv
48+
}
49+
})
50+
}
51+
52+
func BenchmarkFrom(b *testing.B) {
53+
b.Run("not nil", func(b *testing.B) {
54+
var pv *bool = new(bool)
55+
for b.Loop() {
56+
v := pointer.From(pv)
57+
_ = v
58+
}
59+
})
60+
61+
b.Run("nil", func(b *testing.B) {
62+
var pv *bool
63+
for b.Loop() {
64+
v := pointer.From(pv)
65+
_ = v
66+
}
67+
})
68+
}

pointer/pointer_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package pointer_test
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/wafer-bw/go-toolbox/pointer"
8+
)
9+
10+
func TestTo(t *testing.T) {
11+
t.Parallel()
12+
v := true
13+
pv := pointer.To(v)
14+
if pv == nil {
15+
t.Fatal("expected non-nil pointer")
16+
} else if v != *pv {
17+
t.Fatalf("expected %v, got %v", v, *pv)
18+
}
19+
}
20+
21+
func TestFrom(t *testing.T) {
22+
t.Parallel()
23+
24+
t.Run("not nil", func(t *testing.T) {
25+
t.Parallel()
26+
var pv *bool = new(bool)
27+
v := pointer.From(pv)
28+
if v != *pv {
29+
t.Fatalf("expected %v, got %v", *pv, v)
30+
}
31+
})
32+
33+
t.Run("nil", func(t *testing.T) {
34+
t.Parallel()
35+
var e bool
36+
var pv *bool
37+
v := pointer.From(pv)
38+
if v != e {
39+
t.Fatalf("expected %v, got %v", e, v)
40+
}
41+
})
42+
}
43+
44+
func TestToOrNil(t *testing.T) {
45+
t.Parallel()
46+
47+
t.Run("zero value to nil", func(t *testing.T) {
48+
t.Parallel()
49+
var v string
50+
pv := pointer.ToOrNil(v)
51+
if pv != nil {
52+
t.Fatalf("expected nil pointer, got %v", pv)
53+
}
54+
})
55+
56+
t.Run("non-zero value to pointer", func(t *testing.T) {
57+
t.Parallel()
58+
v := "non-zero"
59+
pv := pointer.ToOrNil(v)
60+
if pv == nil {
61+
t.Fatal("expected non-nil pointer")
62+
} else if v != *pv {
63+
t.Fatalf("expected %v, got %v", v, *pv)
64+
}
65+
})
66+
67+
t.Run("non-zero value to pointer with IsZero", func(t *testing.T) {
68+
t.Parallel()
69+
v := time.Date(2014, 6, 25, 12, 24, 40, 0, time.UTC)
70+
pv := pointer.ToOrNil(v)
71+
if pv == nil {
72+
t.Fatal("expected non-nil pointer")
73+
} else if v != *pv {
74+
t.Fatalf("expected %v, got %v", v, *pv)
75+
}
76+
})
77+
78+
t.Run("zero value to pointer with IsZero", func(t *testing.T) {
79+
t.Parallel()
80+
v := time.Time{}
81+
pv := pointer.ToOrNil(v)
82+
if pv != nil {
83+
t.Fatal("expected nil pointer")
84+
}
85+
})
86+
}

0 commit comments

Comments
 (0)