diff --git a/go.mod b/go.mod index 364c9fb..a970049 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/illuin-tech/goinject go 1.24.0 -require github.com/stretchr/testify v1.10.0 +require github.com/stretchr/testify v1.11.1 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 713a0b4..dcce432 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/injector.go b/injector.go index d4976ff..98c32cb 100644 --- a/injector.go +++ b/injector.go @@ -19,21 +19,22 @@ type Injector struct { // NewInjector builds up a new Injector out of a list of Modules with singleton scope func NewInjector(options ...Option) (*Injector, error) { - mod := &configuration{ - bindings: make(map[*binding]bool), - scopes: make(map[string]Scope), + conf := &configuration{ + bindings: []*binding{}, + scopes: make(map[string]Scope), + knownProviders: []*provideOption{}, } - for _, o := range options { - err := o.apply(mod) + for _, option := range options { + err := option.apply(conf) if err != nil { return nil, err } } singletonScope := newSingletonScope() - mod.scopes[Singleton] = singletonScope - mod.scopes[PerLookUp] = newPerLookUpScope() + conf.scopes[Singleton] = singletonScope + conf.scopes[PerLookUp] = newPerLookUpScope() injector := &Injector{ bindings: make(map[reflect.Type]map[string][]*binding), @@ -49,8 +50,8 @@ func NewInjector(options ...Option) (*Injector, error) { scope: Singleton, } - injector.scopes = mod.scopes - for b := range mod.bindings { + injector.scopes = conf.scopes + for _, b := range conf.bindings { _, ok := injector.bindings[b.typeof] if !ok { injector.bindings[b.typeof] = make(map[string][]*binding) diff --git a/injector_test.go b/injector_test.go index 1f73eb5..ca43850 100644 --- a/injector_test.go +++ b/injector_test.go @@ -685,3 +685,31 @@ func TestInjectorConfigurationError(t *testing.T) { ) }) } + +func TestNewInjectorShouldIgnoreMultipleBindings(t *testing.T) { + provider := Provide(func() int { return 32 }) + + inj, err := NewInjector( + provider, + provider, + ) + assert.Nil(t, err) + err = inj.Invoke(context.Background(), func(val int) { + assert.Equal(t, 32, val) + }) + assert.Nil(t, err) +} + +func TestNewInjectorShouldIgnoreMultipleBindingsInDifferentModules(t *testing.T) { + sharedModule := Module("shared", Provide(func() int { return 32 })) + + inj, err := NewInjector( + Module("module-A", sharedModule), + Module("module-B", sharedModule), + ) + assert.Nil(t, err) + err = inj.Invoke(context.Background(), func(val int) { + assert.Equal(t, 32, val) + }) + assert.Nil(t, err) +} diff --git a/module.go b/module.go index 98fb697..4ede192 100644 --- a/module.go +++ b/module.go @@ -3,11 +3,13 @@ package goinject import ( "fmt" "reflect" + "slices" ) type configuration struct { - bindings map[*binding]bool - scopes map[string]Scope + bindings []*binding + scopes map[string]Scope + knownProviders []*provideOption } // Option enable to configure the given injector @@ -47,6 +49,12 @@ type provideOption struct { } func (o *provideOption) apply(mod *configuration) error { + if slices.Contains(mod.knownProviders, o) { + // already registered, ignore + return nil + } + mod.knownProviders = append(mod.knownProviders, o) + if o.constructor == nil { return newInjectorConfigurationError("cannot accept nil provider", nil) } @@ -77,7 +85,7 @@ func (o *provideOption) apply(mod *configuration) error { } } - mod.bindings[b] = true + mod.bindings = append(mod.bindings, b) return nil }