diff --git a/core/internal/testpb/modules.go b/core/internal/testpb/modules.go index 944a1ba75f55..358f4b90e5ed 100644 --- a/core/internal/testpb/modules.go +++ b/core/internal/testpb/modules.go @@ -95,4 +95,8 @@ type keeperB struct { a KeeperA } -type KeeperB interface{} +type KeeperB interface { + isKeeperB() +} + +func (k keeperB) isKeeperB() {} diff --git a/depinject/binding_test.go b/depinject/binding_test.go new file mode 100644 index 000000000000..4608dc36d267 --- /dev/null +++ b/depinject/binding_test.go @@ -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) +} diff --git a/depinject/config.go b/depinject/config.go index 1043f63adb69..664ad0e1c460 100644 --- a/depinject/config.go +++ b/depinject/config.go @@ -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 { diff --git a/depinject/container.go b/depinject/container.go index 37580c051b07..91a95224e2d4 100644 --- a/depinject/container.go +++ b/depinject/container.go @@ -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}) + } + + 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 c.resolvers[typ], nil + 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") diff --git a/depinject/container_test.go b/depinject/container_test.go index 25af05504010..c1db90d77945 100644 --- a/depinject/container_test.go +++ b/depinject/container_test.go @@ -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) -} diff --git a/depinject/errors.go b/depinject/errors.go index 7e70cf70482b..145ce548a863 100644 --- a/depinject/errors.go +++ b/depinject/errors.go @@ -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) diff --git a/depinject/features/bindings.feature b/depinject/features/bindings.feature new file mode 100644 index 000000000000..fafa61de2b91 --- /dev/null +++ b/depinject/features/bindings.feature @@ -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 diff --git a/depinject/go.mod b/depinject/go.mod index 6d012b53b845..7e738836e6c5 100644 --- a/depinject/go.mod +++ b/depinject/go.mod @@ -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 ) diff --git a/depinject/go.sum b/depinject/go.sum index ca119182a835..57c347b5a126 100644 --- a/depinject/go.sum +++ b/depinject/go.sum @@ -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= diff --git a/depinject/group.go b/depinject/group.go index f966a739b5bb..4656b1c29ba6 100644 --- a/depinject/group.go +++ b/depinject/group.go @@ -38,6 +38,10 @@ type groupResolver struct { graphNode *graphviz.Node } +func (g *groupResolver) getType() reflect.Type { + return g.sliceType +} + type sliceGroupResolver struct { *groupResolver } diff --git a/depinject/module_dep.go b/depinject/module_dep.go index 0a9d8b820a7d..643cb5563063 100644 --- a/depinject/module_dep.go +++ b/depinject/module_dep.go @@ -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() } diff --git a/depinject/one_per_module.go b/depinject/one_per_module.go index 498ec1a6021b..26ce1f0c3974 100644 --- a/depinject/one_per_module.go +++ b/depinject/one_per_module.go @@ -37,6 +37,10 @@ type onePerModuleResolver struct { graphNode *graphviz.Node } +func (o *onePerModuleResolver) getType() reflect.Type { + return o.mapType +} + type mapOfOnePerModuleResolver struct { *onePerModuleResolver } diff --git a/depinject/provider_desc.go b/depinject/provider_desc.go index 84e8cffff3d6..bf39e2a31873 100644 --- a/depinject/provider_desc.go +++ b/depinject/provider_desc.go @@ -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) { diff --git a/depinject/provider_desc_test.go b/depinject/provider_desc_test.go index 014bd82991ec..26a478e70762 100644 --- a/depinject/provider_desc_test.go +++ b/depinject/provider_desc_test.go @@ -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) { diff --git a/depinject/resolver.go b/depinject/resolver.go index a38098e986c3..9355df3c1733 100644 --- a/depinject/resolver.go +++ b/depinject/resolver.go @@ -11,4 +11,5 @@ type resolver interface { resolve(*container, *moduleKey, Location) (reflect.Value, error) describeLocation() string typeGraphNode() *graphviz.Node + getType() reflect.Type } diff --git a/depinject/simple.go b/depinject/simple.go index 5857b555e925..73c29e0bb746 100644 --- a/depinject/simple.go +++ b/depinject/simple.go @@ -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() } diff --git a/depinject/struct_args.go b/depinject/struct_args.go index 8310704eb341..5de9eea7280f 100644 --- a/depinject/struct_args.go +++ b/depinject/struct_args.go @@ -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 diff --git a/depinject/supply.go b/depinject/supply.go index fac6156444e1..0be0caef1920 100644 --- a/depinject/supply.go +++ b/depinject/supply.go @@ -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() } diff --git a/x/auth/module.go b/x/auth/module.go index feec8ccaccea..e15370ee1163 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -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 } diff --git a/x/bank/module.go b/x/bank/module.go index 687f1a39d55f..55e106c4976e 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -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 } diff --git a/x/feegrant/module/module.go b/x/feegrant/module/module.go index d712e102c84b..2788ed204b0c 100644 --- a/x/feegrant/module/module.go +++ b/x/feegrant/module/module.go @@ -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 } diff --git a/x/staking/module.go b/x/staking/module.go index 8e35603cc486..0eed87d059e0 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -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 }