Skip to content

Commit ce90e20

Browse files
committed
feat(pkg): add ptr package handle pointers
1 parent 03086a2 commit ce90e20

File tree

5 files changed

+337
-0
lines changed

5 files changed

+337
-0
lines changed

.todo.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
[?] onex-apiserver 后端对接 ES
3636
[?] 诊断平台
3737
[?] GraphQL API
38+
[?] 添加状态机
3839
---
3940

4041
[?] 创建一个项目开发者社区网站 + 群???

pkg/ptr/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Pointer
2+
3+
This package provides some functions for pointer-based operations.

pkg/ptr/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package gptr provides generic operations for pointers.
2+
3+
package ptr

pkg/ptr/ptr.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ptr
18+
19+
import (
20+
"fmt"
21+
"reflect"
22+
)
23+
24+
// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when,
25+
// for example, an API struct is handled by plugins which need to distinguish
26+
// "no plugin accepted this spec" from "this spec is empty".
27+
//
28+
// This function is only valid for structs and pointers to structs. Any other
29+
// type will cause a panic. Passing a typed nil pointer will return true.
30+
func AllPtrFieldsNil(obj interface{}) bool {
31+
v := reflect.ValueOf(obj)
32+
if !v.IsValid() {
33+
panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
34+
}
35+
if v.Kind() == reflect.Ptr {
36+
if v.IsNil() {
37+
return true
38+
}
39+
v = v.Elem()
40+
}
41+
for i := 0; i < v.NumField(); i++ {
42+
if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
43+
return false
44+
}
45+
}
46+
return true
47+
}
48+
49+
// To returns a pointer to the given value.
50+
func To[T any](v T) *T {
51+
return &v
52+
}
53+
54+
// From returns the value pointed to by the pointer p.
55+
// If the pointer is nil, returns the zero value of T instead.
56+
func From[T any](v *T) T {
57+
var zero T
58+
if v != nil {
59+
return *v
60+
}
61+
62+
return zero
63+
}
64+
65+
// FromOr dereferences ptr and returns the value it points to if no nil, or else
66+
// returns def.
67+
func FromOr[T any](ptr *T, def T) T {
68+
if ptr != nil {
69+
return *ptr
70+
}
71+
return def
72+
}
73+
74+
// IsNil returns whether the given pointer v is nil.
75+
func IsNil[T any](p *T) bool {
76+
return p == nil
77+
}
78+
79+
// IsNotNil is negation of [IsNil].
80+
func IsNotNil[T any](p *T) bool {
81+
return p != nil
82+
}
83+
84+
// Clone returns a shallow copy of the slice.
85+
// If the given pointer is nil, nil is returned.
86+
//
87+
// HINT: The element is copied using assignment (=), so this is a shallow clone.
88+
// If you want to do a deep clone, use [CloneBy] with an appropriate element
89+
// clone function.
90+
//
91+
// AKA: Copy
92+
func Clone[T any](p *T) *T {
93+
if p == nil {
94+
return nil
95+
}
96+
clone := *p
97+
return &clone
98+
}
99+
100+
// CloneBy is variant of [Clone], it returns a copy of the map.
101+
// Element is copied using function f.
102+
// If the given pointer is nil, nil is returned.
103+
func CloneBy[T any](p *T, f func(T) T) *T {
104+
return Map(p, f)
105+
}
106+
107+
// Equal returns true if both arguments are nil or both arguments
108+
// dereference to the same value.
109+
func Equal[T comparable](a, b *T) bool {
110+
if (a == nil) != (b == nil) {
111+
return false
112+
}
113+
if a == nil {
114+
return true
115+
}
116+
return *a == *b
117+
}
118+
119+
// EqualTo returns whether the value of pointer p is equal to value v.
120+
// It a shortcut of "x != nil && *x == y".
121+
//
122+
// EXAMPLE:
123+
//
124+
// x, y := 1, 2
125+
// Equal(&x, 1) ⏩ true
126+
// Equal(&y, 1) ⏩n false
127+
// Equal(nil, 1) ⏩ false
128+
func EqualTo[T comparable](p *T, v T) bool {
129+
return p != nil && *p == v
130+
}
131+
132+
// Map applies function f to element of pointer p.
133+
// If p is nil, f will not be called and nil is returned, otherwise,
134+
// result of f are returned as a new pointer.
135+
//
136+
// EXAMPLE:
137+
//
138+
// i := 1
139+
// Map(&i, strconv.Itoa) ⏩ (*string)("1")
140+
// Map[int](nil, strconv.Itoa) ⏩ (*string)(nil)
141+
func Map[F, T any](p *F, f func(F) T) *T {
142+
if p == nil {
143+
return nil
144+
}
145+
return To(f(*p))
146+
}

pkg/ptr/ptr_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ptr
18+
19+
import (
20+
"fmt"
21+
"strconv"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
func TestAllPtrFieldsNil(t *testing.T) {
28+
testCases := []struct {
29+
obj interface{}
30+
expected bool
31+
}{
32+
{struct{}{}, true},
33+
{struct{ Foo int }{12345}, true},
34+
{&struct{ Foo int }{12345}, true},
35+
{struct{ Foo *int }{nil}, true},
36+
{&struct{ Foo *int }{nil}, true},
37+
{struct {
38+
Foo int
39+
Bar *int
40+
}{12345, nil}, true},
41+
{&struct {
42+
Foo int
43+
Bar *int
44+
}{12345, nil}, true},
45+
{struct {
46+
Foo *int
47+
Bar *int
48+
}{nil, nil}, true},
49+
{&struct {
50+
Foo *int
51+
Bar *int
52+
}{nil, nil}, true},
53+
{struct{ Foo *int }{new(int)}, false},
54+
{&struct{ Foo *int }{new(int)}, false},
55+
{struct {
56+
Foo *int
57+
Bar *int
58+
}{nil, new(int)}, false},
59+
{&struct {
60+
Foo *int
61+
Bar *int
62+
}{nil, new(int)}, false},
63+
{(*struct{})(nil), true},
64+
}
65+
for i, tc := range testCases {
66+
name := fmt.Sprintf("case[%d]", i)
67+
t.Run(name, func(t *testing.T) {
68+
if actual := AllPtrFieldsNil(tc.obj); actual != tc.expected {
69+
t.Errorf("%s: expected %t, got %t", name, tc.expected, actual)
70+
}
71+
})
72+
}
73+
}
74+
75+
func TestRef(t *testing.T) {
76+
type T int
77+
78+
val := T(0)
79+
pointer := To(val)
80+
if *pointer != val {
81+
t.Errorf("expected %d, got %d", val, *pointer)
82+
}
83+
84+
val = T(1)
85+
pointer = To(val)
86+
if *pointer != val {
87+
t.Errorf("expected %d, got %d", val, *pointer)
88+
}
89+
}
90+
91+
func TestFrom(t *testing.T) {
92+
assert.Equal(t, 543, From(To(543)))
93+
assert.Equal(t, "Alice", From(To("Alice")))
94+
assert.Zero(t, From[int](nil))
95+
assert.Nil(t, From[interface{}](nil))
96+
assert.Nil(t, From(To[fmt.Stringer](nil)))
97+
}
98+
99+
func TestFromOr(t *testing.T) {
100+
type T int
101+
102+
var val, def T = 1, 0
103+
104+
out := FromOr(&val, def)
105+
if out != val {
106+
t.Errorf("expected %d, got %d", val, out)
107+
}
108+
109+
out = FromOr(nil, def)
110+
if out != def {
111+
t.Errorf("expected %d, got %d", def, out)
112+
}
113+
}
114+
115+
func TestIsNil(t *testing.T) {
116+
assert.False(t, IsNil(To(1)))
117+
assert.True(t, IsNil[int](nil))
118+
}
119+
120+
func TestClone(t *testing.T) {
121+
assert.True(t, IsNil(Clone(((*int)(nil)))))
122+
123+
v := 1
124+
assert.True(t, Clone(&v) != &v)
125+
assert.True(t, Equal(Clone(&v), &v))
126+
127+
src := To(1)
128+
dst := Clone(&src)
129+
assert.Equal(t, &src, dst)
130+
assert.True(t, src == *dst)
131+
}
132+
133+
func TestCloneBy(t *testing.T) {
134+
assert.True(t, IsNil(CloneBy(((**int)(nil)), Clone[int])))
135+
136+
src := To(1)
137+
dst := CloneBy(&src, Clone[int])
138+
assert.Equal(t, &src, dst)
139+
assert.False(t, src == *dst)
140+
}
141+
142+
func TestEqual(t *testing.T) {
143+
type T int
144+
145+
if !Equal[T](nil, nil) {
146+
t.Errorf("expected true (nil == nil)")
147+
}
148+
if !Equal(To(T(123)), To(T(123))) {
149+
t.Errorf("expected true (val == val)")
150+
}
151+
if Equal(nil, To(T(123))) {
152+
t.Errorf("expected false (nil != val)")
153+
}
154+
if Equal(To(T(123)), nil) {
155+
t.Errorf("expected false (val != nil)")
156+
}
157+
if Equal(To(T(123)), To(T(456))) {
158+
t.Errorf("expected false (val != val)")
159+
}
160+
}
161+
162+
func TestEqualTo(t *testing.T) {
163+
assert.True(t, EqualTo(To(1), 1))
164+
assert.False(t, EqualTo(To(2), 1))
165+
assert.False(t, EqualTo(nil, 0))
166+
}
167+
168+
func TestMap(t *testing.T) {
169+
i := 1
170+
assert.Equal(t, To("1"), Map(&i, strconv.Itoa))
171+
assert.True(t, Map(nil, strconv.Itoa) == nil)
172+
173+
assert.NotPanics(t, func() {
174+
_ = Map(nil, func(int) string {
175+
panic("Q_Q")
176+
})
177+
})
178+
179+
assert.Panics(t, func() {
180+
_ = Map(&i, func(int) string {
181+
panic("Q_Q")
182+
})
183+
})
184+
}

0 commit comments

Comments
 (0)