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)
This commit is contained in:
Aaron Craelius 2022-01-31 12:12:08 -05:00 committed by GitHub
parent 7baa4f3e05
commit 24c97d529f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 349 additions and 358 deletions

View File

@ -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
}

View File

@ -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",
)

View File

@ -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
}

View File

@ -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

View File

@ -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=

View File

@ -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)
}

57
container/module_dep.go Normal file
View File

@ -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
}

31
container/module_key.go Normal file
View File

@ -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("")

106
container/one_per_module.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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))

View File

@ -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
}

View File

@ -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("")

View File

@ -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
}

View File

@ -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())

View File

@ -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))

View File

@ -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
}