Skip to content

Commit 1c951f5

Browse files
Implement Nullable Type Functions and DefaultIfNil Utility (#37)
* added pointer utils * added test cases * organized related functions in same files --------- Co-authored-by: Kashif Khan <[email protected]>
1 parent 62e0ef4 commit 1c951f5

File tree

6 files changed

+917
-0
lines changed

6 files changed

+917
-0
lines changed

pointers/common.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package pointers
2+
3+
import "time"
4+
5+
// DefaultIfNil returns the value of the pointer if it is not nil,
6+
// or the default value if the pointer is nil
7+
func DefaultIfNil[T any](ptr *T, defaultVal T) T {
8+
if ptr == nil {
9+
return defaultVal
10+
}
11+
return *ptr
12+
}
13+
14+
// NullableBool returns the value of the bool pointer
15+
// or false if the pointer is nil
16+
func NullableBool(b *bool) bool {
17+
if b == nil {
18+
return false
19+
}
20+
return *b
21+
}
22+
23+
// NullableTime returns the dereferenced value of *time.Time if not nil,
24+
// or a zero time.Time otherwise.
25+
func NullableTime(t *time.Time) time.Time {
26+
if t == nil {
27+
return time.Time{}
28+
}
29+
return *t
30+
}

pointers/common_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package pointers
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestDefaultIfNil(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
ptr interface{}
12+
defaultVal interface{}
13+
want interface{}
14+
}{
15+
{
16+
name: "success - ptr is nil with int",
17+
ptr: (*int)(nil), // explicitly passing a nil *int pointer
18+
defaultVal: 42,
19+
want: 42,
20+
},
21+
{
22+
name: "success - ptr is not nil with int",
23+
ptr: new(int),
24+
defaultVal: 42,
25+
want: 0, // default int value
26+
},
27+
{
28+
name: "success - ptr is not nil with int and custom value",
29+
ptr: func() *int { x := 100; return &x }(),
30+
defaultVal: 42,
31+
want: 100,
32+
},
33+
{
34+
name: "success - ptr is nil with string",
35+
ptr: (*string)(nil), // explicitly passing a nil *string pointer
36+
defaultVal: "default value",
37+
want: "default value",
38+
},
39+
{
40+
name: "success - ptr is not nil with string",
41+
ptr: new(string),
42+
defaultVal: "default value",
43+
want: "",
44+
},
45+
{
46+
name: "success - ptr is not nil with string and custom value",
47+
ptr: func() *string { s := "hello"; return &s }(),
48+
defaultVal: "default value",
49+
want: "hello",
50+
},
51+
{
52+
name: "success - ptr is nil with bool",
53+
ptr: (*bool)(nil), // explicitly passing a nil *bool pointer
54+
defaultVal: false,
55+
want: false,
56+
},
57+
{
58+
name: "success - ptr is not nil with bool",
59+
ptr: func() *bool { b := true; return &b }(),
60+
defaultVal: false,
61+
want: true,
62+
},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
// Use type assertion to call DefaultIfNil correctly
68+
switch v := tt.ptr.(type) {
69+
case *int:
70+
got := DefaultIfNil(v, tt.defaultVal.(int))
71+
if got != tt.want.(int) {
72+
t.Errorf("DefaultIfNil() = %v, want %v", got, tt.want)
73+
}
74+
case *string:
75+
got := DefaultIfNil(v, tt.defaultVal.(string))
76+
if got != tt.want.(string) {
77+
t.Errorf("DefaultIfNil() = %v, want %v", got, tt.want)
78+
}
79+
case *bool:
80+
got := DefaultIfNil(v, tt.defaultVal.(bool))
81+
if got != tt.want.(bool) {
82+
t.Errorf("DefaultIfNil() = %v, want %v", got, tt.want)
83+
}
84+
default:
85+
t.Errorf("Unsupported type %T", v)
86+
}
87+
})
88+
}
89+
}
90+
91+
func TestNullableBool(t *testing.T) {
92+
type args struct {
93+
b *bool
94+
}
95+
tests := []struct {
96+
name string
97+
args args
98+
want bool
99+
}{
100+
{
101+
name: "success - b is nil",
102+
args: args{
103+
b: nil,
104+
},
105+
want: false,
106+
},
107+
{
108+
name: "success - b is not nil and is false",
109+
args: args{
110+
b: new(bool), // new(bool) initializes to false
111+
},
112+
want: false,
113+
},
114+
{
115+
name: "success - b is not nil and is true",
116+
args: args{
117+
b: func() *bool { v := true; return &v }(),
118+
},
119+
want: true,
120+
},
121+
}
122+
for _, tt := range tests {
123+
t.Run(tt.name, func(t *testing.T) {
124+
if got := NullableBool(tt.args.b); got != tt.want {
125+
t.Errorf("NullableBool() = %v, want %v", got, tt.want)
126+
}
127+
})
128+
}
129+
}
130+
131+
func TestNullableTime(t *testing.T) {
132+
now := time.Now()
133+
type args struct {
134+
t *time.Time
135+
}
136+
tests := []struct {
137+
name string
138+
args args
139+
want time.Time
140+
}{
141+
{
142+
name: "success - t is nil",
143+
args: args{
144+
t: nil,
145+
},
146+
want: time.Time{},
147+
},
148+
{
149+
name: "success - t has a value",
150+
args: args{
151+
t: func() *time.Time {
152+
return &now
153+
}(),
154+
},
155+
want: now,
156+
},
157+
}
158+
for _, tt := range tests {
159+
t.Run(tt.name, func(t *testing.T) {
160+
if got := NullableTime(tt.args.t); got != tt.want {
161+
t.Errorf("NullableTime() = %v, want %v", got, tt.want)
162+
}
163+
})
164+
}
165+
}

pointers/numeric.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package pointers
2+
3+
// NullableInt returns the dereferenced value of the int pointer
4+
// or 0 if the pointer is nil
5+
func NullableInt(i *int) int {
6+
if i == nil {
7+
return 0
8+
}
9+
return *i
10+
}
11+
12+
// NullableInt8 returns the dereferenced value of *int8 if not nil,
13+
// or 0 otherwise.
14+
func NullableInt8(i *int8) int8 {
15+
if i == nil {
16+
return 0
17+
}
18+
return *i
19+
}
20+
21+
// NullableInt16 returns the dereferenced value of *int16 if not nil,
22+
// or 0 otherwise.
23+
func NullableInt16(i *int16) int16 {
24+
if i == nil {
25+
return 0
26+
}
27+
return *i
28+
}
29+
30+
// NullableInt32 returns the dereferenced value of *int32 if not nil,
31+
// or 0 otherwise.
32+
func NullableInt32(i *int32) int32 {
33+
if i == nil {
34+
return 0
35+
}
36+
return *i
37+
}
38+
39+
// NullableInt64 returns the dereferenced value of *int64 if not nil,
40+
// or 0 otherwise.
41+
func NullableInt64(i *int64) int64 {
42+
if i == nil {
43+
return 0
44+
}
45+
return *i
46+
}
47+
48+
// NullableUint returns the dereferenced value of *uint if not nil,
49+
// or 0 otherwise.
50+
func NullableUint(i *uint) uint {
51+
if i == nil {
52+
return 0
53+
}
54+
return *i
55+
}
56+
57+
// NullableUint8 returns the dereferenced value of *uint8 if not nil,
58+
// or 0 otherwise.
59+
func NullableUint8(i *uint8) uint8 {
60+
if i == nil {
61+
return 0
62+
}
63+
return *i
64+
}
65+
66+
// NullableUint16 returns the dereferenced value of *uint16 if not nil,
67+
// or 0 otherwise.
68+
func NullableUint16(i *uint16) uint16 {
69+
if i == nil {
70+
return 0
71+
}
72+
return *i
73+
}
74+
75+
// NullableUint32 returns the dereferenced value of *uint32 if not nil,
76+
// or 0 otherwise.
77+
func NullableUint32(i *uint32) uint32 {
78+
if i == nil {
79+
return 0
80+
}
81+
return *i
82+
}
83+
84+
// NullableUint64 returns the dereferenced value of *uint64 if not nil,
85+
// or 0 otherwise.
86+
func NullableUint64(i *uint64) uint64 {
87+
if i == nil {
88+
return 0
89+
}
90+
return *i
91+
}
92+
93+
// NullableFloat32 returns the dereferenced value of *float32 if not nil,
94+
// or 0.0 otherwise.
95+
func NullableFloat32(f *float32) float32 {
96+
if f == nil {
97+
return 0.0
98+
}
99+
return *f
100+
}
101+
102+
// NullableFloat64 returns the dereferenced value of *float64 if not nil,
103+
// or 0.0 otherwise.
104+
func NullableFloat64(f *float64) float64 {
105+
if f == nil {
106+
return 0.0
107+
}
108+
return *f
109+
}
110+
111+
// NullableComplex64 returns the dereferenced value of *complex64 if not nil,
112+
// or 0+0i otherwise.
113+
func NullableComplex64(c *complex64) complex64 {
114+
if c == nil {
115+
return 0 + 0i
116+
}
117+
return *c
118+
}
119+
120+
// NullableComplex128 returns the dereferenced value of *complex128 if not nil,
121+
// or 0+0i otherwise.
122+
func NullableComplex128(c *complex128) complex128 {
123+
if c == nil {
124+
return 0 + 0i
125+
}
126+
return *c
127+
}

0 commit comments

Comments
 (0)