feat(depinject): resolve interface types (#12169)

This commit is contained in:
Matt Kocubinski 2022-06-09 18:02:54 -05:00 committed by GitHub
parent 907df327ed
commit dd2e432937
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 539 additions and 133 deletions

View File

@ -95,4 +95,8 @@ type keeperB struct {
a KeeperA
}
type KeeperB interface{}
type KeeperB interface {
isKeeperB()
}
func (k keeperB) isKeeperB() {}

148
depinject/binding_test.go Normal file
View File

@ -0,0 +1,148 @@
package depinject_test
import (
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/require"
"github.com/regen-network/gocuke"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/depinject"
)
func TestBindInterface(t *testing.T) {
gocuke.NewRunner(t, &bindingSuite{}).
Path("features/bindings.feature").
Step(`we try to resolve a "Duck" in global scope`, (*bindingSuite).WeTryToResolveADuckInGlobalScope).
Step(`module "(\w+)" wants a "Duck"`, (*bindingSuite).ModuleWantsADuck).
Run()
}
type Duck interface {
quack()
}
type Mallard struct{}
type Canvasback struct{}
type Marbled struct{}
func (duck Mallard) quack() {}
func (duck Canvasback) quack() {}
func (duck Marbled) quack() {}
type DuckWrapper struct {
Module string
Duck Duck
}
func (d DuckWrapper) IsManyPerContainerType() {}
type Pond struct {
Ducks []DuckWrapper
}
type bindingSuite struct {
gocuke.TestingT // this gets injected by gocuke
configs []depinject.Config
pond *Pond
err error
}
func (s bindingSuite) AnInterfaceDuck() {
// we don't need to do anything because this is defined at the type level
}
func (s bindingSuite) TwoImplementationsMallardAndCanvasback() {
// we don't need to do anything because this is defined at the type level
}
func (s *bindingSuite) IsProvided(a string) {
switch a {
case "Mallard":
s.addConfig(depinject.Provide(func() Mallard { return Mallard{} }))
case "Canvasback":
s.addConfig(depinject.Provide(func() Canvasback { return Canvasback{} }))
case "Marbled":
s.addConfig(depinject.Provide(func() Marbled { return Marbled{} }))
default:
s.Fatalf("unexpected duck type %s", a)
}
}
func (s *bindingSuite) addConfig(config depinject.Config) {
s.configs = append(s.configs, config)
}
func (s *bindingSuite) WeTryToResolveADuckInGlobalScope() {
s.addConfig(depinject.Provide(func(duck Duck) DuckWrapper {
return DuckWrapper{Module: "", Duck: duck}
}))
}
func (s *bindingSuite) resolvePond() *Pond {
if s.pond != nil {
return s.pond
}
s.addConfig(depinject.Provide(func(ducks []DuckWrapper) Pond { return Pond{Ducks: ducks} }))
var pond Pond
s.err = depinject.Inject(depinject.Configs(s.configs...), &pond)
s.pond = &pond
return s.pond
}
func (s *bindingSuite) IsResolvedInGlobalScope(typeName string) {
pond := s.resolvePond()
found := false
for _, dw := range pond.Ducks {
if dw.Module == "" {
require.Contains(s, reflect.TypeOf(dw.Duck).Name(), typeName)
found = true
}
}
assert.True(s, found)
}
func (s *bindingSuite) ThereIsAError(expectedErrorMsg string) {
s.resolvePond()
assert.ErrorContains(s, s.err, expectedErrorMsg)
}
func (s *bindingSuite) ThereIsNoError() {
s.resolvePond()
assert.NoError(s, s.err)
}
func fullTypeName(typeName string) string {
return fmt.Sprintf("github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.%s", typeName)
}
func (s *bindingSuite) ThereIsAGlobalBindingForA(preferredType string, interfaceType string) {
s.addConfig(depinject.BindInterface(fullTypeName(interfaceType), fullTypeName(preferredType)))
}
func (s *bindingSuite) ThereIsABindingForAInModule(preferredType string, interfaceType string, moduleName string) {
s.addConfig(depinject.BindInterfaceInModule(moduleName, fullTypeName(interfaceType), fullTypeName(preferredType)))
}
func (s *bindingSuite) ModuleWantsADuck(module string) {
s.addConfig(depinject.ProvideInModule(module, func(duck Duck) DuckWrapper {
return DuckWrapper{Module: module, Duck: duck}
}))
}
func (s *bindingSuite) ModuleResolvesA(module string, duckType string) {
pond := s.resolvePond()
moduleFound := false
for _, dw := range pond.Ducks {
if dw.Module == module {
assert.Contains(s, reflect.TypeOf(dw.Duck).Name(), duckType)
moduleFound = true
}
}
assert.True(s, moduleFound)
}

View File

@ -48,6 +48,48 @@ func provide(ctr *container, key *moduleKey, providers []interface{}) error {
return nil
}
// BindInterface defines a container configuration for an explicit interface binding of inTypeName to outTypeName
// in global scope. The example below demonstrates a configuration where the container always provides a Canvasback
// instance when an interface of type Duck is requested as an input.
//
// BindInterface(
// "github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.Duck",
// "github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.Canvasback")
func BindInterface(inTypeName string, outTypeName string) Config {
return containerConfig(func(ctr *container) error {
return bindInterface(ctr, inTypeName, outTypeName, "")
})
}
// BindInterfaceInModule defines a container configuration for an explicit interface binding of inTypeName to outTypeName
// in the scope of the module with name moduleName. The example below demonstrates a configuration where the container
// provides a Canvasback instance when an interface of type Duck is requested as an input, but only in the scope of
// "moduleFoo".
//
// BindInterfaceInModule(
// "moduleFoo",
// "github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.Duck",
// "github.com/cosmos/cosmos-sdk/depinject_test/depinject_test.Canvasback")
func BindInterfaceInModule(moduleName string, inTypeName string, outTypeName string) Config {
return containerConfig(func(ctr *container) error {
return bindInterface(ctr, inTypeName, outTypeName, moduleName)
})
}
func bindInterface(ctr *container, inTypeName string, outTypeName string, moduleName string) error {
var mk *moduleKey
if moduleName != "" {
mk = &moduleKey{name: moduleName}
}
ctr.addBinding(interfaceBinding{
interfaceName: inTypeName,
implTypeName: outTypeName,
moduleKey: mk,
})
return nil
}
func Supply(values ...interface{}) Config {
loc := LocationFromCaller(1)
return containerConfig(func(ctr *container) error {

View File

@ -3,18 +3,16 @@ package depinject
import (
"bytes"
"fmt"
"reflect"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/depinject/internal/graphviz"
"github.com/pkg/errors"
"reflect"
)
type container struct {
*debugConfig
resolvers map[reflect.Type]resolver
keyedResolvers map[string]resolver
resolvers map[string]resolver
interfaceBindings map[string]interfaceBinding
moduleKeys map[string]*moduleKey
@ -28,14 +26,24 @@ type resolveFrame struct {
typ reflect.Type
}
// interfaceBinding defines a type binding for interfaceName to type implTypeName when being provided as a
// dependency to the module identified by moduleKey. If moduleKey is nil then the type binding is applied globally,
// not module-scoped.
type interfaceBinding struct {
interfaceName string
implTypeName string
moduleKey *moduleKey
resolver resolver
}
func newContainer(cfg *debugConfig) *container {
return &container{
debugConfig: cfg,
resolvers: map[reflect.Type]resolver{},
keyedResolvers: map[string]resolver{},
moduleKeys: map[string]*moduleKey{},
callerStack: nil,
callerMap: map[Location]bool{},
debugConfig: cfg,
resolvers: map[string]resolver{},
moduleKeys: map[string]*moduleKey{},
interfaceBindings: map[string]interfaceBinding{},
callerStack: nil,
callerMap: map[Location]bool{},
}
}
@ -78,14 +86,18 @@ func (c *container) call(provider *ProviderDescriptor, moduleKey *moduleKey) ([]
return out, nil
}
func (c *container) getResolver(typ reflect.Type, key string) (resolver, error) {
if key != "" {
if vr, ok := c.keyedResolvers[key]; ok {
return vr, nil
}
func (c *container) getResolver(typ reflect.Type, key *moduleKey) (resolver, error) {
c.logf("Resolving %v", typ)
pr, err := c.getExplicitResolver(typ, key)
if err != nil {
return nil, err
}
if pr != nil {
return pr, nil
}
if vr, ok := c.resolvers[typ]; ok {
if vr, ok := c.resolverByType(typ); ok {
return vr, nil
}
@ -109,8 +121,8 @@ func (c *container) getResolver(typ reflect.Type, key string) (resolver, error)
graphNode: typeGraphNode,
}
c.resolvers[elemType] = r
c.resolvers[sliceType] = &sliceGroupResolver{r}
c.addResolver(elemType, r)
c.addResolver(sliceType, &sliceGroupResolver{r})
} else if isOnePerModuleType(elemType) {
c.logf("Registering resolver for one-per-module type %v", elemType)
mapType := reflect.MapOf(stringType, elemType)
@ -126,11 +138,63 @@ func (c *container) getResolver(typ reflect.Type, key string) (resolver, error)
graphNode: typeGraphNode,
}
c.resolvers[elemType] = r
c.resolvers[mapType] = &mapOfOnePerModuleResolver{r}
c.addResolver(elemType, r)
c.addResolver(mapType, &mapOfOnePerModuleResolver{r})
}
return c.resolvers[typ], nil
res, found := c.resolverByType(typ)
if !found && typ.Kind() == reflect.Interface {
matches := map[reflect.Type]reflect.Type{}
var resolverType reflect.Type
for _, r := range c.resolvers {
if r.getType().Kind() != reflect.Interface && r.getType().Implements(typ) {
resolverType = r.getType()
matches[resolverType] = resolverType
}
}
if len(matches) == 1 {
res, _ = c.resolverByType(resolverType)
c.logf("Implicitly registering resolver %v for interface type %v", resolverType, typ)
c.addResolver(typ, res)
} else if len(matches) > 1 {
return nil, newErrMultipleImplicitInterfaceBindings(typ, matches)
}
}
return res, nil
}
func (c *container) getExplicitResolver(typ reflect.Type, key *moduleKey) (resolver, error) {
var pref interfaceBinding
var found bool
// module scoped binding takes precedence
pref, found = c.interfaceBindings[bindingKeyFromType(typ, key)]
// fallback to global scope binding
if !found {
pref, found = c.interfaceBindings[bindingKeyFromType(typ, nil)]
}
if !found {
return nil, nil
}
if pref.resolver != nil {
return pref.resolver, nil
}
res, ok := c.resolverByTypeName(pref.implTypeName)
if ok {
c.logf("Registering resolver %v for interface type %v by explicit binding", res.getType(), typ)
pref.resolver = res
return res, nil
}
return nil, newErrNoTypeForExplicitBindingFound(pref)
}
var stringType = reflect.TypeOf("")
@ -155,7 +219,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
return nil, fmt.Errorf("one-per-module type %v can't be used as an input parameter", typ)
}
vr, err := c.getResolver(typ, in.Key)
vr, err := c.getResolver(typ, key)
if err != nil {
return nil, err
}
@ -197,7 +261,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
typ = typ.Elem()
}
vr, err := c.getResolver(typ, out.Key)
vr, err := c.getResolver(typ, key)
if err != nil {
return nil, err
}
@ -218,11 +282,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
graphNode: typeGraphNode,
idxInValues: i,
}
c.resolvers[typ] = vr
if out.Key != "" {
c.keyedResolvers[out.Key] = vr
}
c.addResolver(typ, vr)
}
c.addGraphEdge(providerGraphNode, vr.typeGraphNode())
@ -250,25 +310,20 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
c.logf("Registering resolver for module-scoped type %v", typ)
existing, ok := c.resolvers[typ]
existing, ok := c.resolverByType(typ)
if ok {
return nil, errors.Errorf("duplicate provision of type %v by module-scoped provider %s\n\talready provided by %s",
typ, provider.Location, existing.describeLocation())
}
typeGraphNode := c.typeGraphNode(typ)
mdr := &moduleDepResolver{
c.addResolver(typ, &moduleDepResolver{
typ: typ,
idxInValues: i,
node: node,
valueMap: map[*moduleKey]reflect.Value{},
graphNode: typeGraphNode,
}
c.resolvers[typ] = mdr
if out.Key != "" {
c.keyedResolvers[out.Key] = mdr
}
})
c.addGraphEdge(providerGraphNode, typeGraphNode)
}
@ -284,16 +339,16 @@ func (c *container) supply(value reflect.Value, location Location) error {
typeGraphNode := c.typeGraphNode(typ)
c.addGraphEdge(locGrapNode, typeGraphNode)
if existing, ok := c.resolvers[typ]; ok {
if existing, ok := c.resolverByType(typ); ok {
return duplicateDefinitionError(typ, location, existing.describeLocation())
}
c.resolvers[typ] = &supplyResolver{
c.addResolver(typ, &supplyResolver{
typ: typ,
value: value,
loc: location,
graphNode: typeGraphNode,
}
})
return nil
}
@ -321,7 +376,7 @@ func (c *container) resolve(in ProviderInput, moduleKey *moduleKey, caller Locat
return reflect.ValueOf(OwnModuleKey{moduleKey}), nil
}
vr, err := c.getResolver(in.Type, in.Key)
vr, err := c.getResolver(in.Type, moduleKey)
if err != nil {
return reflect.Value{}, err
}
@ -431,6 +486,42 @@ func (c container) formatResolveStack() string {
return buf.String()
}
func fullyQualifiedTypeName(typ reflect.Type) string {
pkgType := typ
if typ.Kind() == reflect.Pointer || typ.Kind() == reflect.Slice || typ.Kind() == reflect.Map || typ.Kind() == reflect.Array {
pkgType = typ.Elem()
}
return fmt.Sprintf("%s/%v", pkgType.PkgPath(), typ)
}
func bindingKeyFromTypeName(typeName string, key *moduleKey) string {
if key == nil {
return fmt.Sprintf("%s;", typeName)
}
return fmt.Sprintf("%s;%s", typeName, key.name)
}
func bindingKeyFromType(typ reflect.Type, key *moduleKey) string {
return bindingKeyFromTypeName(fullyQualifiedTypeName(typ), key)
}
func (c *container) addBinding(p interfaceBinding) {
c.interfaceBindings[bindingKeyFromTypeName(p.interfaceName, p.moduleKey)] = p
}
func (c *container) addResolver(typ reflect.Type, r resolver) {
c.resolvers[fullyQualifiedTypeName(typ)] = r
}
func (c *container) resolverByType(typ reflect.Type) (resolver, bool) {
return c.resolverByTypeName(fullyQualifiedTypeName(typ))
}
func (c *container) resolverByTypeName(typeName string) (resolver, bool) {
res, found := c.resolvers[typeName]
return res, found
}
func markGraphNodeAsUsed(node *graphviz.Node) {
node.SetColor("black")
node.SetPenWidth("1.5")

View File

@ -634,45 +634,3 @@ func TestConditionalDebugging(t *testing.T) {
require.Empty(t, logs)
require.True(t, success)
}
type Duck interface {
quack()
}
type AlsoDuck interface {
quack()
}
type Mallard struct{}
func (duck Mallard) quack() {}
type KeyedOutput struct {
depinject.Out
Duck Duck `key:"foo"`
}
type KeyedInput struct {
depinject.In
AlsoDuck AlsoDuck `key:"foo"`
}
type Pond struct {
Duck AlsoDuck
}
func TestKeyedInputOutput(t *testing.T) {
var pond Pond
require.NoError(t,
depinject.Inject(
depinject.Provide(
func() KeyedOutput { return KeyedOutput{Duck: Mallard{}} },
func(in KeyedInput) Pond {
require.NotNil(t, in.AlsoDuck)
return Pond{Duck: in.AlsoDuck}
}),
&pond))
require.NotNil(t, pond)
}

View File

@ -1,11 +1,70 @@
package depinject
import (
"fmt"
"reflect"
"github.com/pkg/errors"
)
// ErrMultipleImplicitInterfaceBindings defines an error condition where an attempt was made to implicitly bind
// Interface to a concrete type, but the container was unable to come to a resolution because multiple Matches
// were found.
type ErrMultipleImplicitInterfaceBindings struct {
error
Interface reflect.Type
Matches []reflect.Type
}
func newErrMultipleImplicitInterfaceBindings(i reflect.Type, matches map[reflect.Type]reflect.Type) ErrMultipleImplicitInterfaceBindings {
var ms []reflect.Type
for k := range matches {
ms = append(ms, k)
}
return ErrMultipleImplicitInterfaceBindings{Interface: i, Matches: ms}
}
func (err ErrMultipleImplicitInterfaceBindings) Error() string {
matchesStr := ""
for _, m := range err.Matches {
matchesStr = fmt.Sprintf("%s\n %s", matchesStr, fullyQualifiedTypeName(m))
}
return fmt.Sprintf("Multiple implementations found for interface %v: %s", err.Interface, matchesStr)
}
// ErrNoTypeForExplicitBindingFound defines an error condition where an explicit binding was specified from Interface
// to Implementation but no provider for the requested Implementation was found in the container.
type ErrNoTypeForExplicitBindingFound struct {
Implementation string
Interface string
ModuleName string
error
}
func newErrNoTypeForExplicitBindingFound(p interfaceBinding) ErrNoTypeForExplicitBindingFound {
var moduleName string
if p.moduleKey != nil {
moduleName = p.moduleKey.name
}
return ErrNoTypeForExplicitBindingFound{
Implementation: p.implTypeName,
Interface: p.interfaceName,
ModuleName: moduleName,
}
}
func (err ErrNoTypeForExplicitBindingFound) Error() string {
if err.ModuleName != "" {
return fmt.Sprintf("No type for explicit binding found. Given the explicit interface binding %s in module %s, a provider of type %s was not found.",
err.Interface, err.ModuleName, err.Implementation)
} else {
return fmt.Sprintf("No type for explicit binding found. Given the explicit interface binding %s, a provider of type %s was not found.",
err.Interface, err.Implementation)
}
}
func duplicateDefinitionError(typ reflect.Type, duplicateLoc Location, existingLoc string) error {
return errors.Errorf("duplicate provision of type %v by %s\n\talready provided by %s",
typ, duplicateLoc, existingLoc)

View File

@ -0,0 +1,94 @@
Feature: interface type resolution
Background:
Given an interface Duck
And two implementations Mallard and Canvasback
Rule: interface types resolve to a concrete type implicitly if there is only one matching implementation
Example: only one implementation
Given "Mallard" is provided
When we try to resolve a "Duck" in global scope
Then "Mallard" is resolved in global scope
Example: two implementations
Given "Mallard" is provided
* "Canvasback" is provided
When we try to resolve a "Duck" in global scope
Then there is a "Multiple implementations found" error
Rule: bindings must point to a real type
Example: a bound type is not provided
Given "Mallard" is provided
And there is a global binding for a "Marbled" "Duck"
When we try to resolve a "Duck" in global scope
Then there is a "No type for explicit binding" error
Rule: bindings supersede implicit type resolution
Example: global scope
Given "Canvasback" is provided
And there is a global binding for a "Mallard" "Duck"
When we try to resolve a "Duck" in global scope
Then there is a "No type for explicit binding" error
Example: module scope
Given "Canvasback" is provided
And there is a binding for a "Mallard" "Duck" in module "A"
When module "A" wants a "Duck"
Then there is a "No type for explicit binding" error
Rule: bindings in global scope apply to both global and module-scoped resolution (if there is no module-scoped binding)
Example: global resolution
Given "Mallard" is provided
And "Canvasback" is provided
And there is a global binding for a "Mallard" "Duck"
When we try to resolve a "Duck" in global scope
Then "Mallard" is resolved in global scope
Example: module-scoped resolution
Given "Mallard" is provided
And "Canvasback" is provided
And there is a global binding for a "Mallard" "Duck"
When module "A" wants a "Duck"
Then module "A" resolves a "Mallard"
Rule: module-scoped bindings only apply to module-scoped resolution
Example: a module-scoped binding doesn't work for global scope
Given "Mallard" is provided
* "Canvasback" is provided
* there is a binding for a "Canvasback" "Duck" in module "A"
When we try to resolve a "Duck" in global scope
Then there is a "Multiple implementations found" error
Example: a module-scoped binding works for that module
Given "Mallard" is provided
* "Canvasback" is provided
* there is a binding for a "Canvasback" "Duck" in module "A"
When module "A" wants a "Duck"
Then module "A" resolves a "Canvasback"
Example: a module-scoped binding doesn't work for another module
Given "Mallard" is provided
* "Canvasback" is provided
* there is a binding for a "Canvasback" "Duck" in module "A"
When module "B" wants a "Duck"
Then there is a "Multiple implementations found" error
# this case is called a "journey" scenario which tests a bunch of things together
# most tests should be short and to the point like the ones above but one or two long ones
# are good to test more things together &/or do integration tests
Example: two module-scoped binding and a global binding
Given "Mallard" is provided
* "Canvasback" is provided
* "Marbled" is provided
* there is a global binding for a "Marbled" "Duck"
* there is a binding for a "Canvasback" "Duck" in module "A"
* there is a binding for a "Mallard" "Duck" in module "B"
When module "A" wants a "Duck"
* module "B" wants a "Duck"
* module "C" wants a "Duck"
* we try to resolve a "Duck" in global scope
Then there is no error
* module "A" resolves a "Canvasback"
* module "B" resolves a "Mallard"
* module "C" resolves a "Marbled"
* "Marbled" is resolved in global scope

View File

@ -4,14 +4,21 @@ go 1.18
require (
github.com/pkg/errors v0.9.1
github.com/regen-network/gocuke v0.6.2
github.com/stretchr/testify v1.7.1
golang.org/x/exp v0.0.0-20220428152302-39d4317da171
gotest.tools/v3 v3.2.0
)
require (
github.com/alecthomas/participle/v2 v2.0.0-alpha7 // indirect
github.com/cockroachdb/apd/v3 v3.1.0 // indirect
github.com/cucumber/common/gherkin/go/v22 v22.0.0 // indirect
github.com/cucumber/common/messages/go/v17 v17.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
pgregory.net/rapid v0.4.7 // indirect
)

View File

@ -1,14 +1,33 @@
github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c=
github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA=
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w=
github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4=
github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0=
github.com/cucumber/common/gherkin/go/v22 v22.0.0/go.mod h1:3mJT10B2GGn3MvVPd3FwR7m2u4tLhSRhWUqJU4KN4Fg=
github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts=
github.com/cucumber/common/messages/go/v17 v17.1.1/go.mod h1:bpGxb57tDE385Rb2EohgUadLkAbhoC4IyCFi89u/JQI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M=
github.com/regen-network/gocuke v0.6.2/go.mod h1:zYaqIHZobHyd0xOrHGPQjbhGJsuZ1oElx150u2o1xuk=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -39,8 +58,11 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g=
pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=

View File

@ -38,6 +38,10 @@ type groupResolver struct {
graphNode *graphviz.Node
}
func (g *groupResolver) getType() reflect.Type {
return g.sliceType
}
type sliceGroupResolver struct {
*groupResolver
}

View File

@ -20,6 +20,10 @@ type moduleDepResolver struct {
graphNode *graphviz.Node
}
func (s moduleDepResolver) getType() reflect.Type {
return s.typ
}
func (s moduleDepResolver) describeLocation() string {
return s.node.provider.Location.String()
}

View File

@ -37,6 +37,10 @@ type onePerModuleResolver struct {
graphNode *graphviz.Node
}
func (o *onePerModuleResolver) getType() reflect.Type {
return o.mapType
}
type mapOfOnePerModuleResolver struct {
*onePerModuleResolver
}

View File

@ -28,12 +28,10 @@ type ProviderDescriptor struct {
type ProviderInput struct {
Type reflect.Type
Optional bool
Key string
}
type ProviderOutput struct {
Type reflect.Type
Key string
}
func ExtractProviderDescriptor(provider interface{}) (ProviderDescriptor, error) {

View File

@ -24,16 +24,6 @@ type StructOut struct {
Y []byte
}
type KeyedIn struct {
depinject.In
X string `key:"theKey"`
}
type KeyedOut struct {
depinject.Out
X string `key:"theKey"`
}
func TestExtractProviderDescriptor(t *testing.T) {
var (
intType = reflect.TypeOf(0)
@ -97,20 +87,6 @@ func TestExtractProviderDescriptor(t *testing.T) {
nil,
true,
},
{
name: "keyed input",
ctr: func(_ KeyedIn) int { return 0 },
wantIn: []depinject.ProviderInput{{Type: stringType, Key: "theKey"}},
wantOut: []depinject.ProviderOutput{{Type: intType}},
wantErr: false,
},
{
name: "keyed output",
ctr: func(s string) KeyedOut { return KeyedOut{X: "foo"} },
wantIn: []depinject.ProviderInput{{Type: stringType}},
wantOut: []depinject.ProviderOutput{{Type: stringType, Key: "theKey"}},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -11,4 +11,5 @@ type resolver interface {
resolve(*container, *moduleKey, Location) (reflect.Value, error)
describeLocation() string
typeGraphNode() *graphviz.Node
getType() reflect.Type
}

View File

@ -22,6 +22,10 @@ type simpleResolver struct {
graphNode *graphviz.Node
}
func (s *simpleResolver) getType() reflect.Type {
return s.typ
}
func (s *simpleResolver) describeLocation() string {
return s.node.provider.Location.String()
}

View File

@ -120,16 +120,9 @@ func structArgsInTypes(typ reflect.Type) ([]ProviderInput, error) {
}
}
var key string
keyTag, keyFound := f.Tag.Lookup("key")
if keyFound {
key = keyTag
}
res = append(res, ProviderInput{
Type: f.Type,
Optional: optional,
Key: key,
})
}
return res, nil
@ -158,15 +151,8 @@ func structArgsOutTypes(typ reflect.Type) []ProviderOutput {
continue
}
var key string
keyTag, keyFound := f.Tag.Lookup("key")
if keyFound {
key = keyTag
}
res = append(res, ProviderOutput{
Type: f.Type,
Key: key,
})
}
return res

View File

@ -13,6 +13,10 @@ type supplyResolver struct {
graphNode *graphviz.Node
}
func (s supplyResolver) getType() reflect.Type {
return s.typ
}
func (s supplyResolver) describeLocation() string {
return s.loc.String()
}

View File

@ -211,7 +211,7 @@ func provideModuleBasic() runtime.AppModuleBasicWrapper {
type authOutputs struct {
depinject.Out
AccountKeeper keeper.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
AccountKeeper keeper.AccountKeeper
Module runtime.AppModuleWrapper
}

View File

@ -221,7 +221,7 @@ type bankInputs struct {
depinject.In
Config *modulev1.Module
AccountKeeper types.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
AccountKeeper types.AccountKeeper
Cdc codec.Codec
Subspace paramtypes.Subspace
Key *store.KVStoreKey
@ -230,7 +230,7 @@ type bankInputs struct {
type bankOutputs struct {
depinject.Out
BankKeeper keeper.Keeper `key:"cosmos.bank.v1.Keeper"`
BankKeeper keeper.BaseKeeper
Module runtime.AppModuleWrapper
}

View File

@ -209,8 +209,8 @@ type feegrantInputs struct {
Key *store.KVStoreKey
Cdc codec.Codec
AccountKeeper feegrant.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
BankKeeper feegrant.BankKeeper `key:"cosmos.bank.v1.Keeper"`
AccountKeeper feegrant.AccountKeeper
BankKeeper feegrant.BankKeeper
Registry cdctypes.InterfaceRegistry
}

View File

@ -197,8 +197,8 @@ type stakingInputs struct {
depinject.In
Config *modulev1.Module
AccountKeeper types.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
BankKeeper types.BankKeeper `key:"cosmos.bank.v1.Keeper"`
AccountKeeper types.AccountKeeper
BankKeeper types.BankKeeper
Cdc codec.Codec
Subspace paramstypes.Subspace
Key *store.KVStoreKey
@ -208,7 +208,7 @@ type stakingInputs struct {
type stakingOutputs struct {
depinject.Out
StakingKeeper *keeper.Keeper `key:"cosmos.staking.v1.Keeper"`
StakingKeeper *keeper.Keeper
Module runtime.AppModuleWrapper
}