Skip to content

Commit

Permalink
Add dedicated DefaultSlice function given comparable constraint. Add …
Browse files Browse the repository at this point in the history
…benchmark for slices
  • Loading branch information
medge-mailgun committed Sep 19, 2024
1 parent 26def09 commit cdff537
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 12 deletions.
34 changes: 33 additions & 1 deletion setter/setter.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,23 @@ func SetDefault(dest interface{}, defaultValue ...interface{}) {
}
}

func SetDefaultNew[T comparable](dest *T, defaultValues ...T) {
// Default assigns the first non-zero default value to `dest`
// if `dest`, itself, is the zero value of type T.
// To work with slices: use DefaultSlice
//
// var config struct {
// Verbose *bool
// Foo string
// Bar int
// }
// holster.Default(&config.Foo, "default")
// holster.Default(&config.Bar, 200)
//
// Supply additional default values and SetDefault will
// choose the first default that is not of zero value
//
// holster.SetDefault(&config.Foo, os.Getenv("FOO"), "default")
func Default[T comparable](dest *T, defaultValues ...T) {
if IsZeroNew(*dest) {
for _, value := range defaultValues {
if !IsZeroNew(value) {
Expand All @@ -64,6 +80,22 @@ func SetDefaultNew[T comparable](dest *T, defaultValues ...T) {
}
}

// DefaultSlice assigns the first non-empty default value to `dest` if
// `dest`, itself, is an empty slice.
func DefaultSlice[T comparable](dest *[]T, defaultValues ...[]T) {
if len(*dest) == 0 {
for _, value := range defaultValues {
if len(value) != 0 {
*dest = make([]T, len(value))
copy(*dest, value)
return
}
}
}
}

// IsZeroNew compares the given value to its Golang-specified zero value.
// It works for any type T that satisfies comparable.
func IsZeroNew[T comparable](value T) bool {
var zero T
return value == zero
Expand Down
62 changes: 51 additions & 11 deletions setter/setter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/mailgun/holster/v4/setter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIfEmpty(t *testing.T) {
Expand Down Expand Up @@ -148,24 +149,46 @@ func TestIsNil(t *testing.T) {

// ---------------------------------------------------------

var newRes string
var newStrRes string

func BenchmarkSetterNew(b *testing.B) {
var r string
for i := 0; i < b.N; i++ {
setter.SetDefaultNew(&r, "", "", "42")
setter.Default(&r, "", "", "42")
}
newRes = r
newStrRes = r
}

var oldRes string
var oldStrRes string

func BenchmarkSetter(b *testing.B) {
var r string
for i := 0; i < b.N; i++ {
setter.SetDefault(&r, "", "", "42")
}
oldRes = r
oldStrRes = r
}

var newSliceRs []string

func BenchmarkSetterNew_Slice(b *testing.B) {
r := make([]string, 0, 3)
b.ResetTimer()
for i := 0; i < b.N; i++ {
setter.DefaultSlice(&r, []string{}, []string{"welcome all", "to a benchmark", "of SILLY proportions"})
}
newSliceRs = r
}

var oldSliceRs []string

func BenchmarkSetter_Slice(b *testing.B) {
r := make([]string, 0, 3)
b.ResetTimer()
for i := 0; i < b.N; i++ {
setter.SetDefault(&r, []string{""}, []string{"welcome all", "to a benchmark", "of SILLY proportions"})
}
oldSliceRs = r
}

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

// Should apply the default values
setter.SetDefaultNew(&conf.Foo, "default")
setter.SetDefaultNew(&conf.Bar, 200)
setter.Default(&conf.Foo, "default")
setter.Default(&conf.Bar, 200)

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

// Should NOT apply the default values
setter.SetDefaultNew(&conf.Foo, "default")
setter.SetDefaultNew(&conf.Bar, 200)
setter.Default(&conf.Foo, "default")
setter.Default(&conf.Bar, 200)

assert.Equal(t, "thrawn", conf.Foo)
assert.Equal(t, 500, conf.Bar)
}

func TestSetterNew_Slices(t *testing.T) {
var foo []string
require.Len(t, foo, 0)

// Should apply the default values
setter.DefaultSlice(&foo, []string{"default"})
require.Len(t, foo, 1)
assert.Equal(t, "default", foo[0])

foo = []string{"thrawn"}

// Should NOT apply the default values
setter.DefaultSlice(&foo, []string{"default"})
require.Len(t, foo, 1)
assert.Equal(t, "thrawn", foo[0])
}

func TestSetterNew_IfDefaultPrecedence(t *testing.T) {
var conf struct {
Foo string
Expand All @@ -204,12 +244,12 @@ func TestSetterNew_IfDefaultPrecedence(t *testing.T) {

// Should use the final default value
envValue := ""
setter.SetDefaultNew(&conf.Foo, envValue, "default")
setter.Default(&conf.Foo, envValue, "default")
assert.Equal(t, "default", conf.Foo)

// Should use envValue
envValue = "bar"
setter.SetDefaultNew(&conf.Bar, envValue, "default")
setter.Default(&conf.Bar, envValue, "default")
assert.Equal(t, "bar", conf.Bar)
}

Expand Down

0 comments on commit cdff537

Please sign in to comment.