From 24c97d529f041358305300f04b67536eefcbcfec Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Mon, 31 Jan 2022 12:12:08 -0500 Subject: [PATCH] refactor(container)!: rename Scope -> ModuleKey (#11073) ## Description This renames `container.Scope` to `container.ModuleKey` to make it more consistent with the usage of scope for modules. It also renames all usages of `constructor` (in docs and variable name) to `provider` for consistency. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- container/container.go | 120 ++++++++++++++++---------------- container/container_test.go | 90 ++++++++++++------------ container/debug.go | 12 ++-- container/go.mod | 2 +- container/go.sum | 3 +- container/group.go | 6 +- container/module_dep.go | 57 +++++++++++++++ container/module_key.go | 31 +++++++++ container/one_per_module.go | 106 ++++++++++++++++++++++++++++ container/one_per_scope.go | 106 ---------------------------- container/option.go | 28 ++++---- container/provider_desc.go | 10 +-- container/provider_desc_test.go | 2 +- container/resolver.go | 2 +- container/scope.go | 41 ----------- container/scope_dep.go | 57 --------------- container/simple.go | 12 ++-- container/struct_args.go | 20 +++--- container/supply.go | 2 +- 19 files changed, 349 insertions(+), 358 deletions(-) create mode 100644 container/module_dep.go create mode 100644 container/module_key.go create mode 100644 container/one_per_module.go delete mode 100644 container/one_per_scope.go delete mode 100644 container/scope.go delete mode 100644 container/scope_dep.go diff --git a/container/container.go b/container/container.go index 8b4cfe0da9..700b6b88ce 100644 --- a/container/container.go +++ b/container/container.go @@ -14,7 +14,7 @@ type container struct { resolvers map[reflect.Type]resolver - scopes map[string]Scope + moduleKeys map[string]*moduleKey resolveStack []resolveFrame callerStack []Location @@ -30,15 +30,15 @@ func newContainer(cfg *debugConfig) *container { return &container{ debugConfig: cfg, resolvers: map[reflect.Type]resolver{}, - scopes: map[string]Scope{}, + moduleKeys: map[string]*moduleKey{}, callerStack: nil, callerMap: map[Location]bool{}, } } -func (c *container) call(constructor *ProviderDescriptor, scope Scope) ([]reflect.Value, error) { - loc := constructor.Location - graphNode, err := c.locationGraphNode(loc, scope) +func (c *container) call(provider *ProviderDescriptor, moduleKey *moduleKey) ([]reflect.Value, error) { + loc := provider.Location + graphNode, err := c.locationGraphNode(loc, moduleKey) if err != nil { return nil, err } @@ -53,9 +53,9 @@ func (c *container) call(constructor *ProviderDescriptor, scope Scope) ([]reflec c.logf("Resolving dependencies for %s", loc) c.indentLogger() - inVals := make([]reflect.Value, len(constructor.Inputs)) - for i, in := range constructor.Inputs { - val, err := c.resolve(in, scope, loc) + inVals := make([]reflect.Value, len(provider.Inputs)) + for i, in := range provider.Inputs { + val, err := c.resolve(in, moduleKey, loc) if err != nil { return nil, err } @@ -67,9 +67,9 @@ func (c *container) call(constructor *ProviderDescriptor, scope Scope) ([]reflec delete(c.callerMap, loc) c.callerStack = c.callerStack[0 : len(c.callerStack)-1] - out, err := constructor.Fn(inVals) + out, err := provider.Fn(inVals) if err != nil { - return nil, errors.Wrapf(err, "error calling constructor %s", loc) + return nil, errors.Wrapf(err, "error calling provider %s", loc) } markGraphNodeAsUsed(graphNode) @@ -83,7 +83,7 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { } elemType := typ - if isAutoGroupSliceType(elemType) || isOnePerScopeMapType(elemType) { + if isAutoGroupSliceType(elemType) || isOnePerModuleMapType(elemType) { elemType = elemType.Elem() } @@ -108,48 +108,48 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { c.resolvers[elemType] = r c.resolvers[sliceType] = &sliceGroupResolver{r} - } else if isOnePerScopeType(elemType) { - c.logf("Registering resolver for one-per-scope type %v", elemType) + } else if isOnePerModuleType(elemType) { + c.logf("Registering resolver for one-per-module type %v", elemType) mapType := reflect.MapOf(stringType, elemType) typeGraphNode, err = c.typeGraphNode(mapType) if err != nil { return nil, err } - typeGraphNode.SetComment("one-per-scope") + typeGraphNode.SetComment("one-per-module") - r := &onePerScopeResolver{ + r := &onePerModuleResolver{ typ: elemType, mapType: mapType, - providers: map[Scope]*simpleProvider{}, - idxMap: map[Scope]int{}, + providers: map[*moduleKey]*simpleProvider{}, + idxMap: map[*moduleKey]int{}, graphNode: typeGraphNode, } c.resolvers[elemType] = r - c.resolvers[mapType] = &mapOfOnePerScopeResolver{r} + c.resolvers[mapType] = &mapOfOnePerModuleResolver{r} } return c.resolvers[typ], nil } -func (c *container) addNode(constructor *ProviderDescriptor, scope Scope) (interface{}, error) { - constructorGraphNode, err := c.locationGraphNode(constructor.Location, scope) +func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (interface{}, error) { + providerGraphNode, err := c.locationGraphNode(provider.Location, key) if err != nil { return nil, err } - hasScopeParam := false - for _, in := range constructor.Inputs { + hasModuleKeyParam := false + for _, in := range provider.Inputs { typ := in.Type - if typ == scopeType { - hasScopeParam = true + if typ == moduleKeyType { + hasModuleKeyParam = true } if isAutoGroupType(typ) { return nil, fmt.Errorf("auto-group type %v can't be used as an input parameter", typ) - } else if isOnePerScopeType(typ) { - return nil, fmt.Errorf("one-per-scope type %v can't be used as an input parameter", typ) + } else if isOnePerModuleType(typ) { + return nil, fmt.Errorf("one-per-module type %v can't be used as an input parameter", typ) } vr, err := c.getResolver(typ) @@ -167,25 +167,25 @@ func (c *container) addNode(constructor *ProviderDescriptor, scope Scope) (inter } } - c.addGraphEdge(typeGraphNode, constructorGraphNode) + c.addGraphEdge(typeGraphNode, providerGraphNode) } - if scope != nil || !hasScopeParam { - c.logf("Registering %s", constructor.Location.String()) + if key != nil || !hasModuleKeyParam { + c.logf("Registering %s", provider.Location.String()) c.indentLogger() defer c.dedentLogger() sp := &simpleProvider{ - provider: constructor, - scope: scope, + provider: provider, + moduleKey: key, } - for i, out := range constructor.Outputs { + for i, out := range provider.Outputs { typ := out.Type - // one-per-scope maps can't be used as a return type - if isOnePerScopeMapType(typ) { - return nil, fmt.Errorf("%v cannot be used as a return type because %v is a one-per-scope type", + // one-per-module maps can't be used as a return type + if isOnePerModuleMapType(typ) { + return nil, fmt.Errorf("%v cannot be used as a return type because %v is a one-per-module type", typ, typ.Elem()) } @@ -221,30 +221,30 @@ func (c *container) addNode(constructor *ProviderDescriptor, scope Scope) (inter c.resolvers[typ] = vr } - c.addGraphEdge(constructorGraphNode, vr.typeGraphNode()) + c.addGraphEdge(providerGraphNode, vr.typeGraphNode()) } return sp, nil } else { - c.logf("Registering scope provider: %s", constructor.Location.String()) + c.logf("Registering module-scoped provider: %s", provider.Location.String()) c.indentLogger() defer c.dedentLogger() - node := &scopeDepProvider{ - provider: constructor, - calledForScope: map[Scope]bool{}, - valueMap: map[Scope][]reflect.Value{}, + node := &moduleDepProvider{ + provider: provider, + calledForModule: map[*moduleKey]bool{}, + valueMap: map[*moduleKey][]reflect.Value{}, } - for i, out := range constructor.Outputs { + for i, out := range provider.Outputs { typ := out.Type - c.logf("Registering resolver for scoped type %v", typ) + c.logf("Registering resolver for module-scoped type %v", typ) existing, ok := c.resolvers[typ] if ok { - return nil, errors.Errorf("duplicate provision of type %v by scoped provider %s\n\talready provided by %s", - typ, constructor.Location, existing.describeLocation()) + 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, err := c.typeGraphNode(typ) @@ -252,15 +252,15 @@ func (c *container) addNode(constructor *ProviderDescriptor, scope Scope) (inter return reflect.Value{}, err } - c.resolvers[typ] = &scopeDepResolver{ + c.resolvers[typ] = &moduleDepResolver{ typ: typ, idxInValues: i, node: node, - valueMap: map[Scope]reflect.Value{}, + valueMap: map[*moduleKey]reflect.Value{}, graphNode: typeGraphNode, } - c.addGraphEdge(constructorGraphNode, typeGraphNode) + c.addGraphEdge(providerGraphNode, typeGraphNode) } return node, nil @@ -296,7 +296,7 @@ func (c *container) supply(value reflect.Value, location Location) error { return nil } -func (c *container) resolve(in ProviderInput, scope Scope, caller Location) (reflect.Value, error) { +func (c *container) resolve(in ProviderInput, moduleKey *moduleKey, caller Location) (reflect.Value, error) { c.resolveStack = append(c.resolveStack, resolveFrame{loc: caller, typ: in.Type}) typeGraphNode, err := c.typeGraphNode(in.Type) @@ -304,13 +304,13 @@ func (c *container) resolve(in ProviderInput, scope Scope, caller Location) (ref return reflect.Value{}, err } - if in.Type == scopeType { - if scope == nil { - return reflect.Value{}, errors.Errorf("trying to resolve %T for %s but not inside of any scope", scope, caller) + if in.Type == moduleKeyType { + if moduleKey == nil { + return reflect.Value{}, errors.Errorf("trying to resolve %T for %s but not inside of any module's scope", moduleKey, caller) } - c.logf("Providing Scope %s", scope.Name()) + c.logf("Providing ModuleKey %s", moduleKey.name) markGraphNodeAsUsed(typeGraphNode) - return reflect.ValueOf(scope), nil + return reflect.ValueOf(ModuleKey{moduleKey}), nil } vr, err := c.getResolver(in.Type) @@ -329,7 +329,7 @@ func (c *container) resolve(in ProviderInput, scope Scope, caller Location) (ref in.Type, caller, c.formatResolveStack()) } - res, err := vr.resolve(c, scope, caller) + res, err := vr.resolve(c, moduleKey, caller) if err != nil { markGraphNodeAsFailed(typeGraphNode) return reflect.Value{}, err @@ -364,7 +364,7 @@ func (c *container) run(invoker interface{}) error { sn, ok := node.(*simpleProvider) if !ok { - return errors.Errorf("cannot run scoped provider as an invoker") + return errors.Errorf("cannot run module-scoped provider as an invoker") } c.logf("Building container") @@ -377,12 +377,12 @@ func (c *container) run(invoker interface{}) error { return nil } -func (c container) createOrGetScope(name string) Scope { - if s, ok := c.scopes[name]; ok { +func (c container) createOrGetModuleKey(name string) *moduleKey { + if s, ok := c.moduleKeys[name]; ok { return s } - s := newScope(name) - c.scopes[name] = s + s := &moduleKey{name} + c.moduleKeys[name] = s return s } diff --git a/container/container_test.go b/container/container_test.go index c9aa3a4d89..15d8d1a3b6 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -34,7 +34,7 @@ type Handler struct { Handle func() } -func (Handler) IsOnePerScopeType() {} +func (Handler) IsOnePerModuleType() {} type Command struct { Run func() @@ -42,15 +42,15 @@ type Command struct { func (Command) IsAutoGroupType() {} -func ProvideKVStoreKey(scope container.Scope) KVStoreKey { - return KVStoreKey{name: scope.Name()} +func ProvideKVStoreKey(moduleKey container.ModuleKey) KVStoreKey { + return KVStoreKey{name: moduleKey.Name()} } -func ProvideModuleKey(scope container.Scope) (ModuleKey, error) { - return ModuleKey(scope.Name()), nil +func ProvideModuleKey(moduleKey container.ModuleKey) (ModuleKey, error) { + return ModuleKey(moduleKey.Name()), nil } -func ProvideMsgClientA(_ container.Scope, key ModuleKey) MsgClientA { +func ProvideMsgClientA(_ container.ModuleKey, key ModuleKey) MsgClientA { return MsgClientA{key} } @@ -76,7 +76,7 @@ type BProvides struct { Commands []Command } -func (ModuleB) Provide(dependencies BDependencies, _ container.Scope) (BProvides, Handler, error) { +func (ModuleB) Provide(dependencies BDependencies, _ container.ModuleKey) (BProvides, Handler, error) { return BProvides{ KeeperB: KeeperB{ key: dependencies.Key, @@ -109,8 +109,8 @@ func TestScenario(t *testing.T) { ProvideModuleKey, ProvideMsgClientA, ), - container.ProvideWithScope("a", wrapMethod0(ModuleA{})), - container.ProvideWithScope("b", wrapMethod0(ModuleB{})), + container.ProvideInModule("a", wrapMethod0(ModuleA{})), + container.ProvideInModule("b", wrapMethod0(ModuleB{})), )) } @@ -215,11 +215,11 @@ func TestSimple(t *testing.T) { ) } -func TestScoped(t *testing.T) { +func TestModuleScoped(t *testing.T) { require.Error(t, container.Run(func(int) {}, container.Provide( - func(container.Scope) int { return 0 }, + func(container.ModuleKey) int { return 0 }, ), ), ) @@ -227,10 +227,10 @@ func TestScoped(t *testing.T) { require.Error(t, container.Run(func(float64) {}, container.Provide( - func(container.Scope) int { return 0 }, + func(container.ModuleKey) int { return 0 }, func() int { return 1 }, ), - container.ProvideWithScope("a", + container.ProvideInModule("a", func(x int) float64 { return float64(x) }, ), ), @@ -240,9 +240,9 @@ func TestScoped(t *testing.T) { container.Run(func(float64) {}, container.Provide( func() int { return 0 }, - func(container.Scope) int { return 1 }, + func(container.ModuleKey) int { return 1 }, ), - container.ProvideWithScope("a", + container.ProvideInModule("a", func(x int) float64 { return float64(x) }, ), ), @@ -251,10 +251,10 @@ func TestScoped(t *testing.T) { require.Error(t, container.Run(func(float64) {}, container.Provide( - func(container.Scope) int { return 0 }, - func(container.Scope) int { return 1 }, + func(container.ModuleKey) int { return 0 }, + func(container.ModuleKey) int { return 1 }, ), - container.ProvideWithScope("a", + container.ProvideInModule("a", func(x int) float64 { return float64(x) }, ), ), @@ -263,9 +263,9 @@ func TestScoped(t *testing.T) { require.NoError(t, container.Run(func(float64) {}, container.Provide( - func(container.Scope) int { return 0 }, + func(container.ModuleKey) int { return 0 }, ), - container.ProvideWithScope("a", + container.ProvideInModule("a", func(x int) float64 { return float64(x) }, ), ), @@ -274,9 +274,9 @@ func TestScoped(t *testing.T) { require.Error(t, container.Run(func(float64) {}, container.Provide( - func(container.Scope) int { return 0 }, + func(container.ModuleKey) int { return 0 }, ), - container.ProvideWithScope("", + container.ProvideInModule("", func(x int) float64 { return float64(x) }, ), ), @@ -285,45 +285,45 @@ func TestScoped(t *testing.T) { require.NoError(t, container.Run(func(float64, float32) {}, container.Provide( - func(container.Scope) int { return 0 }, + func(container.ModuleKey) int { return 0 }, ), - container.ProvideWithScope("a", + container.ProvideInModule("a", func(x int) float64 { return float64(x) }, func(x int) float32 { return float32(x) }, ), ), - "use scope dep twice", + "use module dep twice", ) } -type OnePerScopeInt int +type OnePerModuleInt int -func (OnePerScopeInt) IsOnePerScopeType() {} +func (OnePerModuleInt) IsOnePerModuleType() {} -func TestOnePerScope(t *testing.T) { +func TestOnePerModule(t *testing.T) { require.Error(t, container.Run( - func(OnePerScopeInt) {}, + func(OnePerModuleInt) {}, ), "bad input type", ) require.NoError(t, container.Run( - func(x map[string]OnePerScopeInt, y string) { - require.Equal(t, map[string]OnePerScopeInt{ + func(x map[string]OnePerModuleInt, y string) { + require.Equal(t, map[string]OnePerModuleInt{ "a": 3, "b": 4, }, x) require.Equal(t, "7", y) }, - container.ProvideWithScope("a", - func() OnePerScopeInt { return 3 }, + container.ProvideInModule("a", + func() OnePerModuleInt { return 3 }, ), - container.ProvideWithScope("b", - func() OnePerScopeInt { return 4 }, + container.ProvideInModule("b", + func() OnePerModuleInt { return 4 }, ), - container.Provide(func(x map[string]OnePerScopeInt) string { + container.Provide(func(x map[string]OnePerModuleInt) string { sum := 0 for _, v := range x { sum += int(v) @@ -335,10 +335,10 @@ func TestOnePerScope(t *testing.T) { require.Error(t, container.Run( - func(map[string]OnePerScopeInt) {}, - container.ProvideWithScope("a", - func() OnePerScopeInt { return 0 }, - func() OnePerScopeInt { return 0 }, + func(map[string]OnePerModuleInt) {}, + container.ProvideInModule("a", + func() OnePerModuleInt { return 0 }, + func() OnePerModuleInt { return 0 }, ), ), "duplicate", @@ -346,9 +346,9 @@ func TestOnePerScope(t *testing.T) { require.Error(t, container.Run( - func(map[string]OnePerScopeInt) {}, + func(map[string]OnePerModuleInt) {}, container.Provide( - func() OnePerScopeInt { return 0 }, + func() OnePerModuleInt { return 0 }, ), ), "out of scope", @@ -356,9 +356,9 @@ func TestOnePerScope(t *testing.T) { require.Error(t, container.Run( - func(map[string]OnePerScopeInt) {}, + func(map[string]OnePerModuleInt) {}, container.Provide( - func() map[string]OnePerScopeInt { return nil }, + func() map[string]OnePerModuleInt { return nil }, ), ), "bad return type", @@ -366,7 +366,7 @@ func TestOnePerScope(t *testing.T) { require.NoError(t, container.Run( - func(map[string]OnePerScopeInt) {}, + func(map[string]OnePerModuleInt) {}, ), "no providers", ) diff --git a/container/debug.go b/container/debug.go index a743b84cef..26862c08c5 100644 --- a/container/debug.go +++ b/container/debug.go @@ -193,8 +193,8 @@ func (c *debugConfig) addFileVisualizer(filename string, format string) { }) } -func (c *debugConfig) locationGraphNode(location Location, scope Scope) (*cgraph.Node, error) { - graph := c.scopeSubGraph(scope) +func (c *debugConfig) locationGraphNode(location Location, key *moduleKey) (*cgraph.Node, error) { + graph := c.moduleSubGraph(key) node, found, err := c.findOrCreateGraphNode(graph, location.Name()) if err != nil { return nil, err @@ -241,12 +241,12 @@ func (c *debugConfig) findOrCreateGraphNode(subGraph *cgraph.Graph, name string) return node, false, nil } -func (c *debugConfig) scopeSubGraph(scope Scope) *cgraph.Graph { +func (c *debugConfig) moduleSubGraph(key *moduleKey) *cgraph.Graph { graph := c.graph - if scope != nil { - gname := fmt.Sprintf("cluster_%s", scope.Name()) + if key != nil { + gname := fmt.Sprintf("cluster_%s", key.name) graph = c.graph.SubGraph(gname, 1) - graph.SetLabel(fmt.Sprintf("Scope: %s", scope.Name())) + graph.SetLabel(fmt.Sprintf("ModuleKey: %s", key.name)) } return graph } diff --git a/container/go.mod b/container/go.mod index d7128697a5..9d261255b3 100644 --- a/container/go.mod +++ b/container/go.mod @@ -9,7 +9,7 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fogleman/gg v1.3.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/container/go.sum b/container/go.sum index a208ec5ad3..368b9dc676 100644 --- a/container/go.sum +++ b/container/go.sum @@ -1,7 +1,8 @@ github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/goccy/go-graphviz v0.0.9 h1:s/FMMJ1Joj6La3S5ApO3Jk2cwM4LpXECC2muFx3IPQQ= diff --git a/container/group.go b/container/group.go index c5647f4253..ff374960e1 100644 --- a/container/group.go +++ b/container/group.go @@ -10,7 +10,7 @@ import ( ) // AutoGroupType marks a type which automatically gets grouped together. For an AutoGroupType T, -// T and []T can be declared as output parameters for constructors as many times within the container +// T and []T can be declared as output parameters for providers as many times within the container // as desired. All of the provided values for T can be retrieved by declaring an // []T input parameter. type AutoGroupType interface { @@ -46,7 +46,7 @@ func (g *groupResolver) describeLocation() string { return fmt.Sprintf("auto-group type %v", g.typ) } -func (g *sliceGroupResolver) resolve(c *container, _ Scope, caller Location) (reflect.Value, error) { +func (g *sliceGroupResolver) resolve(c *container, _ *moduleKey, caller Location) (reflect.Value, error) { // Log c.logf("Providing auto-group type slice %v to %s from:", g.sliceType, caller.Name()) c.indentLogger() @@ -80,7 +80,7 @@ func (g *sliceGroupResolver) resolve(c *container, _ Scope, caller Location) (re return g.values, nil } -func (g *groupResolver) resolve(_ *container, _ Scope, _ Location) (reflect.Value, error) { +func (g *groupResolver) resolve(_ *container, _ *moduleKey, _ Location) (reflect.Value, error) { return reflect.Value{}, errors.Errorf("%v is an auto-group type and cannot be used as an input value, instead use %v", g.typ, g.sliceType) } diff --git a/container/module_dep.go b/container/module_dep.go new file mode 100644 index 0000000000..7aecc9c1d1 --- /dev/null +++ b/container/module_dep.go @@ -0,0 +1,57 @@ +package container + +import ( + "reflect" + + "github.com/goccy/go-graphviz/cgraph" +) + +type moduleDepProvider struct { + provider *ProviderDescriptor + calledForModule map[*moduleKey]bool + valueMap map[*moduleKey][]reflect.Value +} + +type moduleDepResolver struct { + typ reflect.Type + idxInValues int + node *moduleDepProvider + valueMap map[*moduleKey]reflect.Value + graphNode *cgraph.Node +} + +func (s moduleDepResolver) describeLocation() string { + return s.node.provider.Location.String() +} + +func (s moduleDepResolver) resolve(ctr *container, moduleKey *moduleKey, caller Location) (reflect.Value, error) { + // Log + ctr.logf("Providing %v from %s to %s", s.typ, s.node.provider.Location, caller.Name()) + + // Resolve + if val, ok := s.valueMap[moduleKey]; ok { + return val, nil + } + + if !s.node.calledForModule[moduleKey] { + values, err := ctr.call(s.node.provider, moduleKey) + if err != nil { + return reflect.Value{}, err + } + + s.node.valueMap[moduleKey] = values + s.node.calledForModule[moduleKey] = true + } + + value := s.node.valueMap[moduleKey][s.idxInValues] + s.valueMap[moduleKey] = value + return value, nil +} + +func (s moduleDepResolver) addNode(p *simpleProvider, _ int) error { + return duplicateDefinitionError(s.typ, p.provider.Location, s.node.provider.Location.String()) +} + +func (s moduleDepResolver) typeGraphNode() *cgraph.Node { + return s.graphNode +} diff --git a/container/module_key.go b/container/module_key.go new file mode 100644 index 0000000000..79777dc093 --- /dev/null +++ b/container/module_key.go @@ -0,0 +1,31 @@ +package container + +import ( + "reflect" +) + +// ModuleKey is a special type used to scope a provider to a "module". +// +// Special module-scoped providers can be used with Provide by declaring a +// provider with an input parameter of type ModuleKey. These providers +// may construct a unique value of a dependency for each module and will +// be called at most once per module. +// +// Providers passed to ProvideInModule can also declare an input parameter +// of type ModuleKey to retrieve their module key but these providers will be +// called at most once. +type ModuleKey struct { + *moduleKey +} + +type moduleKey struct { + name string +} + +func (k ModuleKey) Name() string { + return k.name +} + +var moduleKeyType = reflect.TypeOf(ModuleKey{}) + +var stringType = reflect.TypeOf("") diff --git a/container/one_per_module.go b/container/one_per_module.go new file mode 100644 index 0000000000..e5cab4fb63 --- /dev/null +++ b/container/one_per_module.go @@ -0,0 +1,106 @@ +package container + +import ( + "fmt" + "reflect" + + "github.com/goccy/go-graphviz/cgraph" + + "github.com/pkg/errors" +) + +// OnePerModuleType marks a type which +// can have up to one value per module. All of the values for a one-per-module type T +// and their respective modules, can be retrieved by declaring an input parameter map[string]T. +type OnePerModuleType interface { + // IsOnePerModuleType is a marker function just indicates that this is a one-per-module type. + IsOnePerModuleType() +} + +var onePerModuleTypeType = reflect.TypeOf((*OnePerModuleType)(nil)).Elem() + +func isOnePerModuleType(t reflect.Type) bool { + return t.Implements(onePerModuleTypeType) +} + +func isOnePerModuleMapType(typ reflect.Type) bool { + return typ.Kind() == reflect.Map && isOnePerModuleType(typ.Elem()) && typ.Key().Kind() == reflect.String +} + +type onePerModuleResolver struct { + typ reflect.Type + mapType reflect.Type + providers map[*moduleKey]*simpleProvider + idxMap map[*moduleKey]int + resolved bool + values reflect.Value + graphNode *cgraph.Node +} + +type mapOfOnePerModuleResolver struct { + *onePerModuleResolver +} + +func (o *onePerModuleResolver) resolve(_ *container, _ *moduleKey, _ Location) (reflect.Value, error) { + return reflect.Value{}, errors.Errorf("%v is a one-per-module type and thus can't be used as an input parameter, instead use %v", o.typ, o.mapType) +} + +func (o *onePerModuleResolver) describeLocation() string { + return fmt.Sprintf("one-per-module type %v", o.typ) +} + +func (o *mapOfOnePerModuleResolver) resolve(c *container, _ *moduleKey, caller Location) (reflect.Value, error) { + // Log + c.logf("Providing one-per-module type map %v to %s from:", o.mapType, caller.Name()) + c.indentLogger() + for key, node := range o.providers { + c.logf("%s: %s", key.name, node.provider.Location) + } + c.dedentLogger() + + // Resolve + if !o.resolved { + res := reflect.MakeMap(o.mapType) + for key, node := range o.providers { + values, err := node.resolveValues(c) + if err != nil { + return reflect.Value{}, err + } + idx := o.idxMap[key] + if len(values) < idx { + return reflect.Value{}, errors.Errorf("expected value of type %T at index %d", o.typ, idx) + } + value := values[idx] + res.SetMapIndex(reflect.ValueOf(key.name), value) + } + + o.values = res + o.resolved = true + } + + return o.values, nil +} + +func (o *onePerModuleResolver) addNode(n *simpleProvider, i int) error { + if n.moduleKey == nil { + return errors.Errorf("cannot define a provider with one-per-module dependency %v which isn't provided in a module", o.typ) + } + + if existing, ok := o.providers[n.moduleKey]; ok { + return errors.Errorf("duplicate provision for one-per-module type %v in module %s: %s\n\talready provided by %s", + o.typ, n.moduleKey.name, n.provider.Location, existing.provider.Location) + } + + o.providers[n.moduleKey] = n + o.idxMap[n.moduleKey] = i + + return nil +} + +func (o *mapOfOnePerModuleResolver) addNode(s *simpleProvider, _ int) error { + return errors.Errorf("%v is a one-per-module type and thus %v can't be used as an output parameter in %s", o.typ, o.mapType, s.provider.Location) +} + +func (o onePerModuleResolver) typeGraphNode() *cgraph.Node { + return o.graphNode +} diff --git a/container/one_per_scope.go b/container/one_per_scope.go deleted file mode 100644 index e153884dce..0000000000 --- a/container/one_per_scope.go +++ /dev/null @@ -1,106 +0,0 @@ -package container - -import ( - "fmt" - "reflect" - - "github.com/goccy/go-graphviz/cgraph" - - "github.com/pkg/errors" -) - -// OnePerScopeType marks a type which -// can have up to one value per scope. All of the values for a one-per-scope type T -// and their respective scopes, can be retrieved by declaring an input parameter map[string]T. -type OnePerScopeType interface { - // IsOnePerScopeType is a marker function just indicates that this is a one-per-scope type. - IsOnePerScopeType() -} - -var onePerScopeTypeType = reflect.TypeOf((*OnePerScopeType)(nil)).Elem() - -func isOnePerScopeType(t reflect.Type) bool { - return t.Implements(onePerScopeTypeType) -} - -func isOnePerScopeMapType(typ reflect.Type) bool { - return typ.Kind() == reflect.Map && isOnePerScopeType(typ.Elem()) && typ.Key().Kind() == reflect.String -} - -type onePerScopeResolver struct { - typ reflect.Type - mapType reflect.Type - providers map[Scope]*simpleProvider - idxMap map[Scope]int - resolved bool - values reflect.Value - graphNode *cgraph.Node -} - -type mapOfOnePerScopeResolver struct { - *onePerScopeResolver -} - -func (o *onePerScopeResolver) resolve(_ *container, _ Scope, _ Location) (reflect.Value, error) { - return reflect.Value{}, errors.Errorf("%v is a one-per-scope type and thus can't be used as an input parameter, instead use %v", o.typ, o.mapType) -} - -func (o *onePerScopeResolver) describeLocation() string { - return fmt.Sprintf("one-per-scope type %v", o.typ) -} - -func (o *mapOfOnePerScopeResolver) resolve(c *container, _ Scope, caller Location) (reflect.Value, error) { - // Log - c.logf("Providing one-per-scope type map %v to %s from:", o.mapType, caller.Name()) - c.indentLogger() - for scope, node := range o.providers { - c.logf("%s: %s", scope.Name(), node.provider.Location) - } - c.dedentLogger() - - // Resolve - if !o.resolved { - res := reflect.MakeMap(o.mapType) - for scope, node := range o.providers { - values, err := node.resolveValues(c) - if err != nil { - return reflect.Value{}, err - } - idx := o.idxMap[scope] - if len(values) < idx { - return reflect.Value{}, errors.Errorf("expected value of type %T at index %d", o.typ, idx) - } - value := values[idx] - res.SetMapIndex(reflect.ValueOf(scope.Name()), value) - } - - o.values = res - o.resolved = true - } - - return o.values, nil -} - -func (o *onePerScopeResolver) addNode(n *simpleProvider, i int) error { - if n.scope == nil { - return errors.Errorf("cannot define a constructor with one-per-scope dependency %v which isn't provided in a scope", o.typ) - } - - if existing, ok := o.providers[n.scope]; ok { - return errors.Errorf("duplicate provision for one-per-scope type %v in scope %s: %s\n\talready provided by %s", - o.typ, n.scope.Name(), n.provider.Location, existing.provider.Location) - } - - o.providers[n.scope] = n - o.idxMap[n.scope] = i - - return nil -} - -func (o *mapOfOnePerScopeResolver) addNode(s *simpleProvider, _ int) error { - return errors.Errorf("%v is a one-per-scope type and thus %v can't be used as an output parameter in %s", o.typ, o.mapType, s.provider.Location) -} - -func (o onePerScopeResolver) typeGraphNode() *cgraph.Node { - return o.graphNode -} diff --git a/container/option.go b/container/option.go index e327bc3055..156a27cd62 100644 --- a/container/option.go +++ b/container/option.go @@ -12,35 +12,35 @@ type Option interface { } // Provide creates a container option which registers the provided dependency -// injection constructors. Each constructor will be called at most once with the -// exception of scoped constructors which are called at most once per scope -// (see Scope). -func Provide(constructors ...interface{}) Option { +// injection providers. Each provider will be called at most once with the +// exception of module-scoped providers which are called at most once per module +// (see ModuleKey). +func Provide(providers ...interface{}) Option { return containerOption(func(ctr *container) error { - return provide(ctr, nil, constructors) + return provide(ctr, nil, providers) }) } -// ProvideWithScope creates a container option which registers the provided dependency -// injection constructors that are to be run in the provided scope. Each constructor +// ProvideInModule creates a container option which registers the provided dependency +// injection providers that are to be run in the named module. Each provider // will be called at most once. -func ProvideWithScope(scopeName string, constructors ...interface{}) Option { +func ProvideInModule(moduleName string, providers ...interface{}) Option { return containerOption(func(ctr *container) error { - if scopeName == "" { - return errors.Errorf("expected non-empty scope name") + if moduleName == "" { + return errors.Errorf("expected non-empty module name") } - return provide(ctr, ctr.createOrGetScope(scopeName), constructors) + return provide(ctr, ctr.createOrGetModuleKey(moduleName), providers) }) } -func provide(ctr *container, scope Scope, constructors []interface{}) error { - for _, c := range constructors { +func provide(ctr *container, key *moduleKey, providers []interface{}) error { + for _, c := range providers { rc, err := ExtractProviderDescriptor(c) if err != nil { return errors.WithStack(err) } - _, err = ctr.addNode(&rc, scope) + _, err = ctr.addNode(&rc, key) if err != nil { return errors.WithStack(err) } diff --git a/container/provider_desc.go b/container/provider_desc.go index 76e1cd2ca8..fbbaa42ff2 100644 --- a/container/provider_desc.go +++ b/container/provider_desc.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" ) -// ProviderDescriptor defines a special constructor type that is defined by +// ProviderDescriptor defines a special provider type that is defined by // reflection. It should be passed as a value to the Provide function. // Ex: // option.Provide(ProviderDescriptor{ ... }) @@ -17,10 +17,10 @@ type ProviderDescriptor struct { // Outputs defines the out parameter types to Fn. Outputs []ProviderOutput - // Fn defines the constructor function. + // Fn defines the provider function. Fn func([]reflect.Value) ([]reflect.Value, error) - // Location defines the source code location to be used for this constructor + // Location defines the source code location to be used for this provider // in error messages. Location Location } @@ -44,7 +44,7 @@ func ExtractProviderDescriptor(provider interface{}) (ProviderDescriptor, error) } } - return expandStructArgsConstructor(rctr) + return expandStructArgsProvider(rctr) } func doExtractProviderDescriptor(ctr interface{}) (ProviderDescriptor, error) { @@ -57,7 +57,7 @@ func doExtractProviderDescriptor(ctr interface{}) (ProviderDescriptor, error) { loc := LocationFromPC(val.Pointer()) if typ.IsVariadic() { - return ProviderDescriptor{}, errors.Errorf("variadic function can't be used as a constructor: %s", loc) + return ProviderDescriptor{}, errors.Errorf("variadic function can't be used as a provider: %s", loc) } numIn := typ.NumIn() diff --git a/container/provider_desc_test.go b/container/provider_desc_test.go index b7dcae8964..3b1ba9a944 100644 --- a/container/provider_desc_test.go +++ b/container/provider_desc_test.go @@ -24,7 +24,7 @@ type StructOut struct { Y []byte } -func TestExtractConstructorInfo(t *testing.T) { +func TestExtractProviderDescriptor(t *testing.T) { var ( intType = reflect.TypeOf(0) int16Type = reflect.TypeOf(int16(0)) diff --git a/container/resolver.go b/container/resolver.go index 4549f6ba3e..7583358e04 100644 --- a/container/resolver.go +++ b/container/resolver.go @@ -8,7 +8,7 @@ import ( type resolver interface { addNode(*simpleProvider, int) error - resolve(*container, Scope, Location) (reflect.Value, error) + resolve(*container, *moduleKey, Location) (reflect.Value, error) describeLocation() string typeGraphNode() *cgraph.Node } diff --git a/container/scope.go b/container/scope.go deleted file mode 100644 index 52a087e507..0000000000 --- a/container/scope.go +++ /dev/null @@ -1,41 +0,0 @@ -package container - -import ( - "reflect" -) - -// Scope is a special type used to define a provider scope. -// -// Special scoped constructors can be used with Provide by declaring a -// constructor with an input parameter of type Scope. These constructors -// should construct an unique value for each dependency based on scope and will -// be called at most once per scope. -// -// Constructors passed to ProvideWithScope can also declare an input parameter -// of type Scope to retrieve their scope but these constructors will be called at most once. -type Scope interface { - isScope() - - // Name returns the name of the scope which is unique within a container. - Name() string -} - -// NewScope creates a new scope with the provided name. Only one scope with a -// given name can be created per container. -func newScope(name string) Scope { - return &scope{name: name} -} - -type scope struct { - name string -} - -func (s *scope) Name() string { - return s.name -} - -func (s *scope) isScope() {} - -var scopeType = reflect.TypeOf((*Scope)(nil)).Elem() - -var stringType = reflect.TypeOf("") diff --git a/container/scope_dep.go b/container/scope_dep.go deleted file mode 100644 index e136581403..0000000000 --- a/container/scope_dep.go +++ /dev/null @@ -1,57 +0,0 @@ -package container - -import ( - "reflect" - - "github.com/goccy/go-graphviz/cgraph" -) - -type scopeDepProvider struct { - provider *ProviderDescriptor - calledForScope map[Scope]bool - valueMap map[Scope][]reflect.Value -} - -type scopeDepResolver struct { - typ reflect.Type - idxInValues int - node *scopeDepProvider - valueMap map[Scope]reflect.Value - graphNode *cgraph.Node -} - -func (s scopeDepResolver) describeLocation() string { - return s.node.provider.Location.String() -} - -func (s scopeDepResolver) resolve(ctr *container, scope Scope, caller Location) (reflect.Value, error) { - // Log - ctr.logf("Providing %v from %s to %s", s.typ, s.node.provider.Location, caller.Name()) - - // Resolve - if val, ok := s.valueMap[scope]; ok { - return val, nil - } - - if !s.node.calledForScope[scope] { - values, err := ctr.call(s.node.provider, scope) - if err != nil { - return reflect.Value{}, err - } - - s.node.valueMap[scope] = values - s.node.calledForScope[scope] = true - } - - value := s.node.valueMap[scope][s.idxInValues] - s.valueMap[scope] = value - return value, nil -} - -func (s scopeDepResolver) addNode(p *simpleProvider, _ int) error { - return duplicateDefinitionError(s.typ, p.provider.Location, s.node.provider.Location.String()) -} - -func (s scopeDepResolver) typeGraphNode() *cgraph.Node { - return s.graphNode -} diff --git a/container/simple.go b/container/simple.go index 61bc3780a8..e62744c608 100644 --- a/container/simple.go +++ b/container/simple.go @@ -7,10 +7,10 @@ import ( ) type simpleProvider struct { - provider *ProviderDescriptor - called bool - values []reflect.Value - scope Scope + provider *ProviderDescriptor + called bool + values []reflect.Value + moduleKey *moduleKey } type simpleResolver struct { @@ -28,7 +28,7 @@ func (s *simpleResolver) describeLocation() string { func (s *simpleProvider) resolveValues(ctr *container) ([]reflect.Value, error) { if !s.called { - values, err := ctr.call(s.provider, s.scope) + values, err := ctr.call(s.provider, s.moduleKey) if err != nil { return nil, err } @@ -39,7 +39,7 @@ func (s *simpleProvider) resolveValues(ctr *container) ([]reflect.Value, error) return s.values, nil } -func (s *simpleResolver) resolve(c *container, _ Scope, caller Location) (reflect.Value, error) { +func (s *simpleResolver) resolve(c *container, _ *moduleKey, caller Location) (reflect.Value, error) { // Log c.logf("Providing %v from %s to %s", s.typ, s.node.provider.Location, caller.Name()) diff --git a/container/struct_args.go b/container/struct_args.go index 49df400c40..985d950d46 100644 --- a/container/struct_args.go +++ b/container/struct_args.go @@ -35,11 +35,11 @@ type isOut interface{ isOut() } var isOutType = reflect.TypeOf((*isOut)(nil)).Elem() -func expandStructArgsConstructor(constructor ProviderDescriptor) (ProviderDescriptor, error) { +func expandStructArgsProvider(provider ProviderDescriptor) (ProviderDescriptor, error) { var foundStructArgs bool var newIn []ProviderInput - for _, in := range constructor.Inputs { + for _, in := range provider.Inputs { if in.Type.AssignableTo(isInType) { foundStructArgs = true inTypes, err := structArgsInTypes(in.Type) @@ -53,7 +53,7 @@ func expandStructArgsConstructor(constructor ProviderDescriptor) (ProviderDescri } var newOut []ProviderOutput - for _, out := range constructor.Outputs { + for _, out := range provider.Outputs { if out.Type.AssignableTo(isOutType) { foundStructArgs = true newOut = append(newOut, structArgsOutTypes(out.Type)...) @@ -66,18 +66,18 @@ func expandStructArgsConstructor(constructor ProviderDescriptor) (ProviderDescri return ProviderDescriptor{ Inputs: newIn, Outputs: newOut, - Fn: expandStructArgsFn(constructor), - Location: constructor.Location, + Fn: expandStructArgsFn(provider), + Location: provider.Location, }, nil } - return constructor, nil + return provider, nil } -func expandStructArgsFn(constructor ProviderDescriptor) func(inputs []reflect.Value) ([]reflect.Value, error) { - fn := constructor.Fn - inParams := constructor.Inputs - outParams := constructor.Outputs +func expandStructArgsFn(provider ProviderDescriptor) func(inputs []reflect.Value) ([]reflect.Value, error) { + fn := provider.Fn + inParams := provider.Inputs + outParams := provider.Outputs return func(inputs []reflect.Value) ([]reflect.Value, error) { j := 0 inputs1 := make([]reflect.Value, len(inParams)) diff --git a/container/supply.go b/container/supply.go index eec99f0c99..d75ad59803 100644 --- a/container/supply.go +++ b/container/supply.go @@ -21,7 +21,7 @@ func (s supplyResolver) addNode(provider *simpleProvider, _ int) error { return duplicateDefinitionError(s.typ, provider.provider.Location, s.loc.String()) } -func (s supplyResolver) resolve(c *container, _ Scope, caller Location) (reflect.Value, error) { +func (s supplyResolver) resolve(c *container, _ *moduleKey, caller Location) (reflect.Value, error) { c.logf("Supplying %v from %s to %s", s.typ, s.loc, caller.Name()) return s.value, nil }