Skip to content

Commit e107827

Browse files
committed
Add dedicated DefaultSlice function given comparable constraint. Add benchmark for slices
1 parent 450a971 commit e107827

File tree

2 files changed

+84
-12
lines changed

2 files changed

+84
-12
lines changed

setter/setter.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,23 @@ func SetDefault(dest interface{}, defaultValue ...interface{}) {
5353
}
5454
}
5555

56-
func SetDefaultNew[T comparable](dest *T, defaultValues ...T) {
56+
// Default assigns the first non-zero default value to `dest`
57+
// if `dest`, itself, is the zero value of type T.
58+
// To work with slices: use DefaultSlice
59+
//
60+
// var config struct {
61+
// Verbose *bool
62+
// Foo string
63+
// Bar int
64+
// }
65+
// holster.Default(&config.Foo, "default")
66+
// holster.Default(&config.Bar, 200)
67+
//
68+
// Supply additional default values and SetDefault will
69+
// choose the first default that is not of zero value
70+
//
71+
// holster.SetDefault(&config.Foo, os.Getenv("FOO"), "default")
72+
func Default[T comparable](dest *T, defaultValues ...T) {
5773
if IsZeroNew(*dest) {
5874
for _, value := range defaultValues {
5975
if !IsZeroNew(value) {
@@ -64,6 +80,22 @@ func SetDefaultNew[T comparable](dest *T, defaultValues ...T) {
6480
}
6581
}
6682

83+
// DefaultSlice assigns the first non-empty default value to `dest` if
84+
// `dest`, itself, is an empty slice.
85+
func DefaultSlice[T comparable](dest *[]T, defaultValues ...[]T) {
86+
if len(*dest) == 0 {
87+
for _, value := range defaultValues {
88+
if len(value) != 0 {
89+
*dest = make([]T, len(value))
90+
copy(*dest, value)
91+
return
92+
}
93+
}
94+
}
95+
}
96+
97+
// IsZeroNew compares the given value to its Golang-specified zero value.
98+
// It works for any type T that satisfies comparable.
6799
func IsZeroNew[T comparable](value T) bool {
68100
var zero T
69101
return value == zero

setter/setter_test.go

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/mailgun/holster/v4/setter"
2222
"github.com/stretchr/testify/assert"
23+
"github.com/stretchr/testify/require"
2324
)
2425

2526
func TestIfEmpty(t *testing.T) {
@@ -148,24 +149,46 @@ func TestIsNil(t *testing.T) {
148149

149150
// ---------------------------------------------------------
150151

151-
var newRes string
152+
var newStrRes string
152153

153154
func BenchmarkSetterNew(b *testing.B) {
154155
var r string
155156
for i := 0; i < b.N; i++ {
156-
setter.SetDefaultNew(&r, "", "", "42")
157+
setter.Default(&r, "", "", "42")
157158
}
158-
newRes = r
159+
newStrRes = r
159160
}
160161

161-
var oldRes string
162+
var oldStrRes string
162163

163164
func BenchmarkSetter(b *testing.B) {
164165
var r string
165166
for i := 0; i < b.N; i++ {
166167
setter.SetDefault(&r, "", "", "42")
167168
}
168-
oldRes = r
169+
oldStrRes = r
170+
}
171+
172+
var newSliceRs []string
173+
174+
func BenchmarkSetterNew_Slice(b *testing.B) {
175+
r := make([]string, 0, 3)
176+
b.ResetTimer()
177+
for i := 0; i < b.N; i++ {
178+
setter.DefaultSlice(&r, []string{}, []string{"welcome all", "to a benchmark", "of SILLY proportions"})
179+
}
180+
newSliceRs = r
181+
}
182+
183+
var oldSliceRs []string
184+
185+
func BenchmarkSetter_Slice(b *testing.B) {
186+
r := make([]string, 0, 3)
187+
b.ResetTimer()
188+
for i := 0; i < b.N; i++ {
189+
setter.SetDefault(&r, []string{""}, []string{"welcome all", "to a benchmark", "of SILLY proportions"})
190+
}
191+
oldSliceRs = r
169192
}
170193

171194
func TestSetterNew_IfEmpty(t *testing.T) {
@@ -177,8 +200,8 @@ func TestSetterNew_IfEmpty(t *testing.T) {
177200
assert.Equal(t, 0, conf.Bar)
178201

179202
// Should apply the default values
180-
setter.SetDefaultNew(&conf.Foo, "default")
181-
setter.SetDefaultNew(&conf.Bar, 200)
203+
setter.Default(&conf.Foo, "default")
204+
setter.Default(&conf.Bar, 200)
182205

183206
assert.Equal(t, "default", conf.Foo)
184207
assert.Equal(t, 200, conf.Bar)
@@ -187,13 +210,30 @@ func TestSetterNew_IfEmpty(t *testing.T) {
187210
conf.Bar = 500
188211

189212
// Should NOT apply the default values
190-
setter.SetDefaultNew(&conf.Foo, "default")
191-
setter.SetDefaultNew(&conf.Bar, 200)
213+
setter.Default(&conf.Foo, "default")
214+
setter.Default(&conf.Bar, 200)
192215

193216
assert.Equal(t, "thrawn", conf.Foo)
194217
assert.Equal(t, 500, conf.Bar)
195218
}
196219

220+
func TestSetterNew_Slices(t *testing.T) {
221+
var foo []string
222+
require.Len(t, foo, 0)
223+
224+
// Should apply the default values
225+
setter.DefaultSlice(&foo, []string{"default"})
226+
require.Len(t, foo, 1)
227+
assert.Equal(t, "default", foo[0])
228+
229+
foo = []string{"thrawn"}
230+
231+
// Should NOT apply the default values
232+
setter.DefaultSlice(&foo, []string{"default"})
233+
require.Len(t, foo, 1)
234+
assert.Equal(t, "thrawn", foo[0])
235+
}
236+
197237
func TestSetterNew_IfDefaultPrecedence(t *testing.T) {
198238
var conf struct {
199239
Foo string
@@ -204,12 +244,12 @@ func TestSetterNew_IfDefaultPrecedence(t *testing.T) {
204244

205245
// Should use the final default value
206246
envValue := ""
207-
setter.SetDefaultNew(&conf.Foo, envValue, "default")
247+
setter.Default(&conf.Foo, envValue, "default")
208248
assert.Equal(t, "default", conf.Foo)
209249

210250
// Should use envValue
211251
envValue = "bar"
212-
setter.SetDefaultNew(&conf.Bar, envValue, "default")
252+
setter.Default(&conf.Bar, envValue, "default")
213253
assert.Equal(t, "bar", conf.Bar)
214254
}
215255

0 commit comments

Comments
 (0)