Skip to content

Commit 790c8f9

Browse files
committed
feat: add pointer conversion helpers to convert
1 parent e7c6da7 commit 790c8f9

2 files changed

Lines changed: 265 additions & 28 deletions

File tree

convert/conv.go

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,138 @@
11
package convert
22

33
import (
4-
"strconv"
5-
"strings"
6-
"time"
4+
"strconv"
5+
"strings"
6+
"time"
77
)
88

99
// ToFloat converts string to float64. It returns 0 if conversion is impossible.
1010
func ToFloat(s string) float32 {
11-
f, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", -1), 32)
11+
f, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", -1), 32)
1212

13-
return float32(f)
13+
return float32(f)
1414
}
1515

1616
// ToFloat64 converts string to float64. It returns 0 if conversion is impossible.
1717
func ToFloat64(s string) float64 {
18-
f, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", -1), 64)
18+
f, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", -1), 64)
1919

20-
return f
20+
return f
2121
}
2222

2323
// ToInt converts string to int. It returns 0 if conversion is impossible.
2424
func ToInt(s string) int {
25-
i, _ := strconv.Atoi(s)
25+
i, _ := strconv.Atoi(s)
2626

27-
return i
27+
return i
2828
}
2929

3030
// ToInt64 converts string to int64. It returns 0 if conversion is impossible.
3131
func ToInt64(s string) int64 {
32-
i, _ := strconv.ParseInt(s, 0, 64)
32+
i, _ := strconv.ParseInt(s, 0, 64)
3333

34-
return i
34+
return i
3535
}
3636

3737
func PointerTo[T any](v T) *T {
38-
return &v
38+
return &v
39+
}
40+
41+
// PointerToOrNil returns a pointer to v if condition is true, otherwise returns nil.
42+
// This is useful for conditionally setting optional fields in API requests.
43+
//
44+
// Example:
45+
//
46+
// latitude := convert.PointerToOrNil(geo.Latitude, geo != nil && geo.Latitude != 0)
47+
func PointerToOrNil[T any](v T, condition bool) *T {
48+
if condition {
49+
return &v
50+
}
51+
return nil
52+
}
53+
54+
// PointerToIf returns a pointer to v if the predicate function returns true, otherwise returns nil.
55+
// This is useful for conditional pointer creation with complex validation logic.
56+
//
57+
// Example:
58+
//
59+
// isValid := func(lat float64) bool { return lat >= -90 && lat <= 90 }
60+
// latitude := convert.PointerToIf(geo.Latitude, isValid)
61+
func PointerToIf[T any](v T, predicate func(T) bool) *T {
62+
if predicate(v) {
63+
return &v
64+
}
65+
return nil
66+
}
67+
68+
// PointerToIfNonZero returns a pointer to v if v is not the zero value for its type, otherwise returns nil.
69+
// This is a convenience wrapper for the common case of checking non-zero values.
70+
//
71+
// Example:
72+
//
73+
// name := convert.PointerToIfNonZero("John") // returns &"John"
74+
// empty := convert.PointerToIfNonZero("") // returns nil
75+
func PointerToIfNonZero[T comparable](v T) *T {
76+
var zero T
77+
if v != zero {
78+
return &v
79+
}
80+
return nil
3981
}
4082

4183
// Deprecated: use PointerTo instead.
4284
func BoolP(v bool) *bool {
43-
return &v
85+
return &v
4486
}
4587

4688
// Deprecated: use PointerTo instead.
4789
func Float64P(v float64) *float64 {
48-
return &v
90+
return &v
4991
}
5092

5193
// Deprecated: use PointerTo instead.
5294
func FloatP(v float32) *float32 {
53-
return &v
95+
return &v
5496
}
5597

5698
// Deprecated: use PointerTo instead.
5799
func Int64P(v int64) *int64 {
58-
return &v
100+
return &v
59101
}
60102

61103
// Deprecated: use PointerTo instead.
62104
func IntP(v int) *int {
63-
return &v
105+
return &v
64106
}
65107

66108
// Deprecated: use PointerTo instead.
67109
func StrP(s string) *string {
68-
return &s
110+
return &s
69111
}
70112

71113
// Deprecated: use PointerTo instead.
72114
func TimeP(t time.Time) *time.Time {
73-
return &t
115+
return &t
74116
}
75117

76118
func EqualInt64P(a, b *int64) bool {
77-
if a == b {
78-
return true
79-
}
119+
if a == b {
120+
return true
121+
}
80122

81-
if a == nil || b == nil {
82-
return false
83-
}
123+
if a == nil || b == nil {
124+
return false
125+
}
84126

85-
return *a == *b
127+
return *a == *b
86128
}
87129

88130
// Float32ToString converts a float32 to a string.
89131
func Float32ToString(f float32) string {
90-
return strconv.FormatFloat(float64(f), 'f', -1, 32)
132+
return strconv.FormatFloat(float64(f), 'f', -1, 32)
91133
}
92134

93135
// Float64ToString converts a float64 to a string.
94136
func Float64ToString(f float64) string {
95-
return strconv.FormatFloat(f, 'f', -1, 64)
137+
return strconv.FormatFloat(f, 'f', -1, 64)
96138
}

convert/conv_test.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package convert
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestPointerToOrNil(t *testing.T) {
10+
t.Run("int with true condition", func(t *testing.T) {
11+
result := PointerToOrNil(42, true)
12+
assert.NotNil(t, result)
13+
assert.Equal(t, 42, *result)
14+
})
15+
16+
t.Run("int with false condition", func(t *testing.T) {
17+
result := PointerToOrNil(42, false)
18+
assert.Nil(t, result)
19+
})
20+
21+
t.Run("zero value with true condition", func(t *testing.T) {
22+
result := PointerToOrNil(0, true)
23+
assert.NotNil(t, result)
24+
assert.Equal(t, 0, *result)
25+
})
26+
27+
t.Run("string with true condition", func(t *testing.T) {
28+
result := PointerToOrNil("hello", true)
29+
assert.NotNil(t, result)
30+
assert.Equal(t, "hello", *result)
31+
})
32+
33+
t.Run("empty string with false condition", func(t *testing.T) {
34+
result := PointerToOrNil("", false)
35+
assert.Nil(t, result)
36+
})
37+
38+
t.Run("float64 with condition", func(t *testing.T) {
39+
result := PointerToOrNil(3.14, true)
40+
assert.NotNil(t, result)
41+
assert.Equal(t, 3.14, *result)
42+
43+
resultNil := PointerToOrNil(3.14, false)
44+
assert.Nil(t, resultNil)
45+
})
46+
47+
t.Run("struct with true condition", func(t *testing.T) {
48+
type testStruct struct {
49+
x int
50+
y string
51+
}
52+
val := testStruct{x: 42, y: "test"}
53+
result := PointerToOrNil(val, true)
54+
assert.NotNil(t, result)
55+
assert.Equal(t, val, *result)
56+
})
57+
}
58+
59+
func TestPointerToIf(t *testing.T) {
60+
t.Run("predicate returns true", func(t *testing.T) {
61+
result := PointerToIf(42, func(v int) bool { return v > 0 })
62+
assert.NotNil(t, result)
63+
assert.Equal(t, 42, *result)
64+
})
65+
66+
t.Run("predicate returns false", func(t *testing.T) {
67+
result := PointerToIf(-5, func(v int) bool { return v > 0 })
68+
assert.Nil(t, result)
69+
})
70+
71+
t.Run("complex predicate - latitude range check valid", func(t *testing.T) {
72+
isValidLatitude := func(v float64) bool {
73+
return v >= -90 && v <= 90
74+
}
75+
result := PointerToIf(45.5, isValidLatitude)
76+
assert.NotNil(t, result)
77+
assert.Equal(t, 45.5, *result)
78+
})
79+
80+
t.Run("complex predicate - latitude out of range", func(t *testing.T) {
81+
isValidLatitude := func(v float64) bool {
82+
return v >= -90 && v <= 90
83+
}
84+
result := PointerToIf(100.0, isValidLatitude)
85+
assert.Nil(t, result)
86+
})
87+
88+
t.Run("string length predicate", func(t *testing.T) {
89+
isNotEmpty := func(s string) bool { return len(s) > 0 }
90+
result := PointerToIf("hello", isNotEmpty)
91+
assert.NotNil(t, result)
92+
assert.Equal(t, "hello", *result)
93+
94+
resultEmpty := PointerToIf("", isNotEmpty)
95+
assert.Nil(t, resultEmpty)
96+
})
97+
98+
t.Run("zero value with predicate that accepts it", func(t *testing.T) {
99+
alwaysTrue := func(v int) bool { return true }
100+
result := PointerToIf(0, alwaysTrue)
101+
assert.NotNil(t, result)
102+
assert.Equal(t, 0, *result)
103+
})
104+
}
105+
106+
func TestPointerToIfNonZero(t *testing.T) {
107+
t.Run("non-zero int", func(t *testing.T) {
108+
result := PointerToIfNonZero(42)
109+
assert.NotNil(t, result)
110+
assert.Equal(t, 42, *result)
111+
})
112+
113+
t.Run("zero int", func(t *testing.T) {
114+
result := PointerToIfNonZero(0)
115+
assert.Nil(t, result)
116+
})
117+
118+
t.Run("non-empty string", func(t *testing.T) {
119+
result := PointerToIfNonZero("hello")
120+
assert.NotNil(t, result)
121+
assert.Equal(t, "hello", *result)
122+
})
123+
124+
t.Run("empty string", func(t *testing.T) {
125+
result := PointerToIfNonZero("")
126+
assert.Nil(t, result)
127+
})
128+
129+
t.Run("non-zero float64", func(t *testing.T) {
130+
result := PointerToIfNonZero(3.14)
131+
assert.NotNil(t, result)
132+
assert.Equal(t, 3.14, *result)
133+
})
134+
135+
t.Run("zero float64", func(t *testing.T) {
136+
result := PointerToIfNonZero(0.0)
137+
assert.Nil(t, result)
138+
})
139+
140+
t.Run("negative int is non-zero", func(t *testing.T) {
141+
result := PointerToIfNonZero(-5)
142+
assert.NotNil(t, result)
143+
assert.Equal(t, -5, *result)
144+
})
145+
146+
t.Run("negative float is non-zero", func(t *testing.T) {
147+
result := PointerToIfNonZero(-3.14)
148+
assert.NotNil(t, result)
149+
assert.Equal(t, -3.14, *result)
150+
})
151+
}
152+
153+
// Example tests for documentation
154+
155+
func ExamplePointerToOrNil() {
156+
type GeoLocation struct {
157+
Latitude float64
158+
Longitude float64
159+
}
160+
161+
geo := &GeoLocation{Latitude: 48.8566, Longitude: 2.3522}
162+
163+
// Using PointerToOrNil for conditional pointer creation
164+
latPtr := PointerToOrNil(geo.Latitude, geo != nil && geo.Latitude != 0)
165+
166+
if latPtr != nil {
167+
println(*latPtr) // 48.8566
168+
}
169+
}
170+
171+
func ExamplePointerToIf() {
172+
// Validate latitude is in valid range
173+
isValidLatitude := func(lat float64) bool {
174+
return lat >= -90 && lat <= 90
175+
}
176+
177+
validLat := PointerToIf(48.8566, isValidLatitude) // returns pointer
178+
invalidLat := PointerToIf(200.0, isValidLatitude) // returns nil
179+
180+
println(validLat != nil) // true
181+
println(invalidLat != nil) // false
182+
}
183+
184+
func ExamplePointerToIfNonZero() {
185+
// Simple non-zero check
186+
name := PointerToIfNonZero("John") // returns pointer to "John"
187+
empty := PointerToIfNonZero("") // returns nil
188+
count := PointerToIfNonZero(5) // returns pointer to 5
189+
zero := PointerToIfNonZero(0) // returns nil
190+
191+
println(name != nil) // true
192+
println(empty != nil) // false
193+
println(count != nil) // true
194+
println(zero != nil) // false
195+
}

0 commit comments

Comments
 (0)