Skip to content

Commit

Permalink
Merge pull request #1 from xmidt-org/feature/simpler-option
Browse files Browse the repository at this point in the history
allow custom types for options
  • Loading branch information
johnabass authored Oct 25, 2023
2 parents aa78b1c + 671c0d7 commit f3315c5
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 7 deletions.
41 changes: 34 additions & 7 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,51 @@ package praetor

import (
"net/http"
"reflect"

"github.com/hashicorp/consul/api"
"go.uber.org/multierr"
)

// Option is a functional option for tailoring the consul client
// configuration prior to creating it. Each option can modify the
// *api.Config prior to it being passed to api.NewClient.
type Option func(*api.Config) error

// AsOption bundles one or more functions into a single Option.
func AsOption[O ~func(*api.Config) error](opts ...O) Option {
return func(cfg *api.Config) (err error) {
for _, o := range opts {
err = multierr.Append(err, o(cfg))
var (
optionType = reflect.TypeOf(Option(nil))
noErrorOptionType = reflect.TypeOf((func(*api.Config))(nil))
)

// OptionFunc represents the types of functions that can be coerced into Options.
type OptionFunc interface {
~func(*api.Config) error | ~func(*api.Config)
}

// AsOption coerces a function into an Option.
func AsOption[OF OptionFunc](of OF) Option {
// trivial conversions
switch oft := any(of).(type) {
case Option:
return oft

case func(*api.Config):
return func(cfg *api.Config) error {
oft(cfg)
return nil
}
}

// now we convert to the underlying type
ofv := reflect.ValueOf(of)
if ofv.CanConvert(optionType) {
return ofv.Convert(optionType).Interface().(Option)
}

return
// there are only (2) types, so the other type must be it
f := ofv.Convert(noErrorOptionType).Interface().(func(*api.Config))
return func(cfg *api.Config) error {
f(cfg)
return nil
}
}

Expand Down
113 changes: 113 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0

package praetor

import (
"errors"
"net/http"
"testing"

"github.com/hashicorp/consul/api"
"github.com/stretchr/testify/suite"
)

const testAddress = "localhost:1234"

type OptionSuite struct {
suite.Suite
}

func (suite *OptionSuite) testAsOptionWithOption() {
suite.Run("Success", func() {
opt := Option(func(cfg *api.Config) error {
cfg.Address = testAddress
return nil
})

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.NoError(err)
suite.Equal(testAddress, cfg.Address)
})

suite.Run("Fail", func() {
expectedErr := errors.New("expected")
opt := Option(func(cfg *api.Config) error {
return expectedErr
})

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.ErrorIs(err, expectedErr)
})
}

func (suite *OptionSuite) testAsOptionWithClosure() {
suite.Run("Success", func() {
opt := func(cfg *api.Config) error {
cfg.Address = testAddress
return nil
}

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.NoError(err)
suite.Equal(testAddress, cfg.Address)
})

suite.Run("Fail", func() {
expectedErr := errors.New("expected")
opt := func(cfg *api.Config) error {
return expectedErr
}

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.ErrorIs(err, expectedErr)
})
}

func (suite *OptionSuite) testAsOptionNoError() {
opt := func(cfg *api.Config) {
cfg.Address = testAddress
}

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.NoError(err)
suite.Equal(testAddress, cfg.Address)
}

func (suite *OptionSuite) testAsOptionCustomType() {
type TestFunc func(*api.Config)
var opt TestFunc = func(cfg *api.Config) {
cfg.Address = testAddress
}

var cfg api.Config
err := AsOption(opt)(&cfg)
suite.NoError(err)
suite.Equal(testAddress, cfg.Address)
}

func (suite *OptionSuite) TestAsOption() {
suite.Run("WithOption", suite.testAsOptionWithOption)
suite.Run("WithClosure", suite.testAsOptionWithClosure)
suite.Run("NoError", suite.testAsOptionNoError)
suite.Run("CustomType", suite.testAsOptionCustomType)
}

func (suite *OptionSuite) TestWithHTTPClient() {
c := new(http.Client)
var cfg api.Config
suite.NoError(
WithHTTPClient(c)(&cfg),
)

suite.Same(c, cfg.HttpClient)
}

func TestOption(t *testing.T) {
suite.Run(t, new(OptionSuite))
}

0 comments on commit f3315c5

Please sign in to comment.