Skip to content

Commit

Permalink
Adds Context.BindToProvider (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
stanistan authored Aug 30, 2021
1 parent d0c0180 commit 74cb513
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 20 deletions.
26 changes: 26 additions & 0 deletions callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"strings"

"github.com/pkg/errors"
)

type bindings map[reflect.Type]func() (reflect.Value, error)
Expand All @@ -24,6 +26,30 @@ func (b bindings) add(values ...interface{}) bindings {
return b
}

func (b bindings) addTo(impl, iface interface{}) {
valueOf := reflect.ValueOf(impl)
b[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
}

func (b bindings) addProvider(provider interface{}) error {
pv := reflect.ValueOf(provider)
t := pv.Type()
if t.Kind() != reflect.Func || t.NumIn() != 0 || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
return errors.Errorf("%T must be a function with the signature func()(T, error)", provider)
}
rt := pv.Type().Out(0)
b[rt] = func() (reflect.Value, error) {
out := pv.Call(nil)
errv := out[1]
var err error
if !errv.IsNil() {
err = errv.Interface().(error) // nolint
}
return out[0], err
}
return nil
}

// Clone and add values.
func (b bindings) clone() bindings {
out := make(bindings, len(b))
Expand Down
11 changes: 9 additions & 2 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,15 @@ func (c *Context) Bind(args ...interface{}) {
//
// BindTo(impl, (*MyInterface)(nil))
func (c *Context) BindTo(impl, iface interface{}) {
valueOf := reflect.ValueOf(impl)
c.bindings[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
c.bindings.addTo(impl, iface)
}

// BindToProvider allows binding of provider functions.
//
// This is useful when the Run() function of different commands require different values that may
// not all be initialisable from the main() function.
func (c *Context) BindToProvider(provider interface{}) error {
return c.bindings.addProvider(provider)
}

// Value returns the value for a particular path element.
Expand Down
20 changes: 2 additions & 18 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@ func Bind(args ...interface{}) Option {
// BindTo(impl, (*iface)(nil))
func BindTo(impl, iface interface{}) Option {
return OptionFunc(func(k *Kong) error {
valueOf := reflect.ValueOf(impl)
k.bindings[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
k.bindings.addTo(impl, iface)
return nil
})
}
Expand All @@ -192,22 +191,7 @@ func BindTo(impl, iface interface{}) Option {
// not all be initialisable from the main() function.
func BindToProvider(provider interface{}) Option {
return OptionFunc(func(k *Kong) error {
pv := reflect.ValueOf(provider)
t := pv.Type()
if t.Kind() != reflect.Func || t.NumIn() != 0 || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
return errors.Errorf("%T must be a function with the signature func()(T, error)", provider)
}
rt := pv.Type().Out(0)
k.bindings[rt] = func() (reflect.Value, error) {
out := pv.Call(nil)
errv := out[1]
var err error
if !errv.IsNil() {
err = errv.Interface().(error) // nolint
}
return out[0], err
}
return nil
return k.bindings.addProvider(provider)
})
}

Expand Down

0 comments on commit 74cb513

Please sign in to comment.