├── go.sum ├── go.mod ├── .travis.yml ├── .gitignore ├── LICENSE ├── examples ├── container_test.go ├── value_test.go ├── pointer_test.go ├── instance_test.go ├── provider_test.go ├── slice_test.go ├── map_test.go ├── annotation_test.go └── singleton_test.go ├── key.go ├── README.md ├── _benchmark └── benchmarks_test.go ├── injection.go ├── injection_test.go ├── key_test.go ├── chain.go ├── binding.go ├── binding_test.go └── chain_test.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ompluscator/genjector 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.18 5 | - stable 6 | 7 | script: 8 | - go test -v ./... 9 | - go test -v -race ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Marko Milojevic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/container_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | type ContainerInterface interface { 9 | String() string 10 | } 11 | 12 | type ContainerStruct struct { 13 | value string 14 | } 15 | 16 | func (s *ContainerStruct) Init() { 17 | s.value = "value provided inside the ContainerStruct" 18 | } 19 | 20 | func (s *ContainerStruct) String() string { 21 | return s.value 22 | } 23 | 24 | func TestAsContainer(t *testing.T) { 25 | t.Run("Store binding inside the customer container and not the global one", func(t *testing.T) { 26 | genjector.Clean() 27 | 28 | customContainer := genjector.NewContainer() 29 | 30 | err := genjector.Bind( 31 | genjector.AsPointer[ContainerInterface, *ContainerStruct](), 32 | genjector.WithContainer(customContainer), 33 | ) 34 | if err != nil { 35 | t.Error("binding should not cause an error") 36 | } 37 | 38 | instance, err := genjector.NewInstance[ContainerInterface]() 39 | if err == nil { 40 | t.Error("expected an error, but got nil") 41 | } 42 | if instance != nil { 43 | t.Errorf(`unexpected instance received: "%s"`, instance) 44 | } 45 | 46 | instance, err = genjector.NewInstance[ContainerInterface](genjector.WithContainer(customContainer)) 47 | if err != nil { 48 | t.Error("initialization should not cause an error") 49 | } 50 | 51 | value := instance.String() 52 | if value != "value provided inside the ContainerStruct" { 53 | t.Errorf(`unexpected value received: "%s"`, value) 54 | } 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /examples/value_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | type ValueInterface interface { 9 | String() string 10 | } 11 | 12 | type ValueStruct struct { 13 | value string 14 | } 15 | 16 | func (s *ValueStruct) Init() { 17 | s.value = "value provided inside the ValueStruct" 18 | } 19 | 20 | func (s ValueStruct) String() string { 21 | return s.value 22 | } 23 | 24 | func TestAsValue(t *testing.T) { 25 | t.Run("Bind a struct as an implementation for an interface", func(t *testing.T) { 26 | genjector.Clean() 27 | 28 | err := genjector.Bind(genjector.AsValue[ValueInterface, ValueStruct]()) 29 | if err != nil { 30 | t.Error("binding should not cause an error") 31 | } 32 | 33 | instance, err := genjector.NewInstance[ValueInterface]() 34 | if err != nil { 35 | t.Error("initialization should not cause an error") 36 | } 37 | 38 | value := instance.String() 39 | if value != "value provided inside the ValueStruct" { 40 | t.Errorf(`unexpected value received: "%s"`, value) 41 | } 42 | }) 43 | 44 | t.Run("Bind a struct as an implementation for that struct", func(t *testing.T) { 45 | genjector.Clean() 46 | 47 | err := genjector.Bind(genjector.AsValue[ValueStruct, ValueStruct]()) 48 | if err != nil { 49 | t.Error("binding should not cause an error") 50 | } 51 | 52 | instance, err := genjector.NewInstance[ValueStruct]() 53 | if err != nil { 54 | t.Error("initialization should not cause an error") 55 | } 56 | 57 | value := instance.String() 58 | if value != "value provided inside the ValueStruct" { 59 | t.Errorf(`unexpected value received: "%s"`, value) 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /examples/pointer_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | type PointerInterface interface { 9 | String() string 10 | } 11 | 12 | type PointerStruct struct { 13 | value string 14 | } 15 | 16 | func (s *PointerStruct) Init() { 17 | s.value = "value provided inside the PointerStruct" 18 | } 19 | 20 | func (s *PointerStruct) String() string { 21 | return s.value 22 | } 23 | 24 | func TestAsPointer(t *testing.T) { 25 | t.Run("Bind a pointer to a struct as an implementation for an interface", func(t *testing.T) { 26 | genjector.Clean() 27 | 28 | err := genjector.Bind(genjector.AsPointer[PointerInterface, *PointerStruct]()) 29 | if err != nil { 30 | t.Error("binding should not cause an error") 31 | } 32 | 33 | instance, err := genjector.NewInstance[PointerInterface]() 34 | if err != nil { 35 | t.Error("initialization should not cause an error") 36 | } 37 | 38 | value := instance.String() 39 | if value != "value provided inside the PointerStruct" { 40 | t.Errorf(`unexpected value received: "%s"`, value) 41 | } 42 | }) 43 | 44 | t.Run("Bind a pointer to a struct as an implementation for the pointer of that struct", func(t *testing.T) { 45 | genjector.Clean() 46 | 47 | err := genjector.Bind(genjector.AsPointer[*PointerStruct, *PointerStruct]()) 48 | if err != nil { 49 | t.Error("binding should not cause an error") 50 | } 51 | 52 | instance, err := genjector.NewInstance[*PointerStruct]() 53 | if err != nil { 54 | t.Error("initialization should not cause an error") 55 | } 56 | 57 | value := instance.String() 58 | if value != "value provided inside the PointerStruct" { 59 | t.Errorf(`unexpected value received: "%s"`, value) 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /examples/instance_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ompluscator/genjector" 7 | ) 8 | 9 | type InstanceInterface interface { 10 | String() string 11 | } 12 | 13 | type InstanceStruct struct { 14 | value string 15 | } 16 | 17 | func (s *InstanceStruct) Init() { 18 | s.value = "value provided inside the InstanceStruct" 19 | } 20 | 21 | func (s *InstanceStruct) String() string { 22 | return s.value 23 | } 24 | 25 | func TestAsInstance(t *testing.T) { 26 | t.Run("Bind a pointer to a struct as an implementation for an interface", func(t *testing.T) { 27 | genjector.Clean() 28 | 29 | err := genjector.Bind(genjector.AsInstance[InstanceInterface](&InstanceStruct{ 30 | value: "value provided inside the Test method", 31 | })) 32 | if err != nil { 33 | t.Error("binding should not cause an error") 34 | } 35 | 36 | instance, err := genjector.NewInstance[InstanceInterface]() 37 | if err != nil { 38 | t.Error("initialization should not cause an error") 39 | } 40 | 41 | value := instance.String() 42 | if value != "value provided inside the Test method" { 43 | t.Errorf(`unexpected value received: "%s"`, value) 44 | } 45 | }) 46 | 47 | t.Run("Bind a pointer to a struct as an implementation for the pointer of that struct", func(t *testing.T) { 48 | genjector.Clean() 49 | 50 | err := genjector.Bind(genjector.AsInstance[*InstanceStruct](&InstanceStruct{ 51 | value: "value provided inside the Test method", 52 | })) 53 | if err != nil { 54 | t.Error("binding should not cause an error") 55 | } 56 | 57 | instance, err := genjector.NewInstance[*InstanceStruct]() 58 | if err != nil { 59 | t.Error("initialization should not cause an error") 60 | } 61 | 62 | value := instance.String() 63 | if value != "value provided inside the Test method" { 64 | t.Errorf(`unexpected value received: "%s"`, value) 65 | } 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /examples/provider_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ompluscator/genjector" 7 | ) 8 | 9 | type ProviderInterface interface { 10 | String() string 11 | } 12 | 13 | type ProviderStruct struct { 14 | value string 15 | } 16 | 17 | func (s *ProviderStruct) Init() { 18 | s.value = "value provided inside the ProviderStruct" 19 | } 20 | 21 | func (s *ProviderStruct) String() string { 22 | return s.value 23 | } 24 | 25 | func TestAsProvider(t *testing.T) { 26 | t.Run("Bind a ProviderMethod for a struct as an implementation for an interface", func(t *testing.T) { 27 | genjector.Clean() 28 | 29 | err := genjector.Bind(genjector.AsProvider[ProviderInterface](func() (*ProviderStruct, error) { 30 | return &ProviderStruct{ 31 | value: "value provided inside the ProviderMethod", 32 | }, nil 33 | })) 34 | if err != nil { 35 | t.Error("binding should not cause an error") 36 | } 37 | 38 | instance, err := genjector.NewInstance[ProviderInterface]() 39 | if err != nil { 40 | t.Error("initialization should not cause an error") 41 | } 42 | 43 | value := instance.String() 44 | if value != "value provided inside the ProviderMethod" { 45 | t.Errorf(`unexpected value received: "%s"`, value) 46 | } 47 | }) 48 | 49 | t.Run("Bind a ProviderMethod for a struct as an implementation for that struct", func(t *testing.T) { 50 | genjector.Clean() 51 | 52 | err := genjector.Bind(genjector.AsProvider[*ProviderStruct](func() (*ProviderStruct, error) { 53 | return &ProviderStruct{ 54 | value: "value provided inside the ProviderMethod", 55 | }, nil 56 | })) 57 | if err != nil { 58 | t.Error("binding should not cause an error") 59 | } 60 | 61 | instance, err := genjector.NewInstance[*ProviderStruct]() 62 | if err != nil { 63 | t.Error("initialization should not cause an error") 64 | } 65 | 66 | value := instance.String() 67 | if value != "value provided inside the ProviderMethod" { 68 | t.Errorf(`unexpected value received: "%s"`, value) 69 | } 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /examples/slice_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | type SliceInterface interface { 9 | String() string 10 | } 11 | 12 | type SliceStruct struct { 13 | value string 14 | } 15 | 16 | func (s *SliceStruct) Init() { 17 | s.value = "value provided inside the SliceStruct" 18 | } 19 | 20 | func (s *SliceStruct) String() string { 21 | return s.value 22 | } 23 | 24 | func TestAsSlice(t *testing.T) { 25 | t.Run("Bind multiple pointers to a struct as an implementations for an interface", func(t *testing.T) { 26 | genjector.Clean() 27 | 28 | err := genjector.Bind( 29 | genjector.InSlice(genjector.AsPointer[SliceInterface, *SliceStruct]()), 30 | ) 31 | if err != nil { 32 | t.Error("binding should not cause an error") 33 | } 34 | 35 | err = genjector.Bind( 36 | genjector.InSlice(genjector.AsProvider[SliceInterface](func() (*SliceStruct, error) { 37 | return &SliceStruct{ 38 | value: "value provided inside the ProviderMethod", 39 | }, nil 40 | })), 41 | ) 42 | if err != nil { 43 | t.Error("binding should not cause an error") 44 | } 45 | 46 | err = genjector.Bind( 47 | genjector.InSlice(genjector.AsInstance[SliceInterface](&SliceStruct{ 48 | value: "value provided inside the Test method", 49 | })), 50 | ) 51 | if err != nil { 52 | t.Error("binding should not cause an error") 53 | } 54 | 55 | instance, err := genjector.NewInstance[[]SliceInterface]() 56 | if err != nil { 57 | t.Error("initialization should not cause an error") 58 | } 59 | 60 | value := instance[0].String() 61 | if value != "value provided inside the SliceStruct" { 62 | t.Errorf(`unexpected value received: "%s"`, value) 63 | } 64 | 65 | value = instance[1].String() 66 | if value != "value provided inside the ProviderMethod" { 67 | t.Errorf(`unexpected value received: "%s"`, value) 68 | } 69 | 70 | value = instance[2].String() 71 | if value != "value provided inside the Test method" { 72 | t.Errorf(`unexpected value received: "%s"`, value) 73 | } 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /examples/map_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | type MapInterface interface { 9 | String() string 10 | } 11 | 12 | type MapStruct struct { 13 | value string 14 | } 15 | 16 | func (s *MapStruct) Init() { 17 | s.value = "value provided inside the MapStruct" 18 | } 19 | 20 | func (s *MapStruct) String() string { 21 | return s.value 22 | } 23 | 24 | func TestAsMap(t *testing.T) { 25 | t.Run("Bind multiple pointers to a struct as an implementations for an interface", func(t *testing.T) { 26 | genjector.Clean() 27 | 28 | err := genjector.Bind( 29 | genjector.InMap("first", genjector.AsPointer[MapInterface, *MapStruct]()), 30 | ) 31 | if err != nil { 32 | t.Error("binding should not cause an error") 33 | } 34 | 35 | err = genjector.Bind( 36 | genjector.InMap("second", genjector.AsProvider[MapInterface](func() (*MapStruct, error) { 37 | return &MapStruct{ 38 | value: "value provided inside the ProviderMethod", 39 | }, nil 40 | })), 41 | ) 42 | if err != nil { 43 | t.Error("binding should not cause an error") 44 | } 45 | 46 | err = genjector.Bind( 47 | genjector.InMap("third", genjector.AsInstance[MapInterface](&MapStruct{ 48 | value: "value provided inside the Test method", 49 | })), 50 | ) 51 | if err != nil { 52 | t.Error("binding should not cause an error") 53 | } 54 | 55 | instance, err := genjector.NewInstance[map[string]MapInterface]() 56 | if err != nil { 57 | t.Error("initialization should not cause an error") 58 | } 59 | 60 | value := instance["first"].String() 61 | if value != "value provided inside the MapStruct" { 62 | t.Errorf(`unexpected value received: "%s"`, value) 63 | } 64 | 65 | value = instance["second"].String() 66 | if value != "value provided inside the ProviderMethod" { 67 | t.Errorf(`unexpected value received: "%s"`, value) 68 | } 69 | 70 | value = instance["third"].String() 71 | if value != "value provided inside the Test method" { 72 | t.Errorf(`unexpected value received: "%s"`, value) 73 | } 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /examples/annotation_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ompluscator/genjector" 6 | "testing" 7 | ) 8 | 9 | type AnnotationInterface interface { 10 | String() string 11 | } 12 | 13 | type AnnotationStruct struct { 14 | value string 15 | } 16 | 17 | func (s *AnnotationStruct) Init() { 18 | firstChild := genjector.MustNewInstance[*AnnotationChildStruct](genjector.WithAnnotation("first")) 19 | secondChild := genjector.MustNewInstance[*AnnotationChildStruct](genjector.WithAnnotation("second")) 20 | s.value = fmt.Sprintf("%s | %s", firstChild.value, secondChild.value) 21 | } 22 | 23 | func (s *AnnotationStruct) String() string { 24 | return s.value 25 | } 26 | 27 | type AnnotationChildStruct struct { 28 | value string 29 | } 30 | 31 | func (s *AnnotationChildStruct) Init() { 32 | s.value = "value provided inside the AnnotationChildStruct" 33 | } 34 | 35 | func TestAsAnnotation(t *testing.T) { 36 | t.Run("Take values from inner 2 child objects defined with proper annotation", func(t *testing.T) { 37 | genjector.Clean() 38 | 39 | err := genjector.Bind(genjector.AsPointer[AnnotationInterface, *AnnotationStruct]()) 40 | if err != nil { 41 | t.Error("binding should not cause an error") 42 | } 43 | 44 | err = genjector.Bind(genjector.AsProvider[*AnnotationChildStruct](func() (*AnnotationChildStruct, error) { 45 | return &AnnotationChildStruct{ 46 | value: "value from the first child", 47 | }, nil 48 | }), genjector.WithAnnotation("first")) 49 | if err != nil { 50 | t.Error("binding should not cause an error") 51 | } 52 | 53 | err = genjector.Bind(genjector.AsProvider[*AnnotationChildStruct](func() (*AnnotationChildStruct, error) { 54 | return &AnnotationChildStruct{ 55 | value: "value from the second child", 56 | }, nil 57 | }), genjector.WithAnnotation("second")) 58 | if err != nil { 59 | t.Error("binding should not cause an error") 60 | } 61 | 62 | instance, err := genjector.NewInstance[AnnotationInterface]() 63 | if err != nil { 64 | t.Error("initialization should not cause an error") 65 | } 66 | 67 | value := instance.String() 68 | if value != "value from the first child | value from the second child" { 69 | t.Errorf(`unexpected value received: "%s"`, value) 70 | } 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /examples/singleton_test.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/ompluscator/genjector" 5 | "testing" 6 | ) 7 | 8 | var counter = 0 9 | 10 | type SingletonInterface interface { 11 | String() string 12 | } 13 | 14 | type SingletonStruct struct { 15 | value string 16 | } 17 | 18 | func (s *SingletonStruct) Init() { 19 | counter++ 20 | s.value = "value provided inside the SingletonStruct" 21 | } 22 | 23 | func (s *SingletonStruct) String() string { 24 | return s.value 25 | } 26 | 27 | func TestAsSingleton(t *testing.T) { 28 | t.Run("Expecting counter to stay at 1 when instance is defined as a singleton", func(t *testing.T) { 29 | genjector.Clean() 30 | counter = 0 31 | 32 | err := genjector.Bind( 33 | genjector.AsPointer[SingletonInterface, *SingletonStruct](), 34 | genjector.AsSingleton(), 35 | ) 36 | if err != nil { 37 | t.Error("binding should not cause an error") 38 | } 39 | 40 | instance, err := genjector.NewInstance[SingletonInterface]() 41 | if err != nil { 42 | t.Error("initialization should not cause an error") 43 | } 44 | 45 | value := instance.String() 46 | if value != "value provided inside the SingletonStruct" { 47 | t.Errorf(`unexpected value received: "%s"`, value) 48 | } 49 | if counter != 1 { 50 | t.Errorf(`unexpected counter received: "%d"`, counter) 51 | } 52 | 53 | instance, err = genjector.NewInstance[SingletonInterface]() 54 | if err != nil { 55 | t.Error("initialization should not cause an error") 56 | } 57 | 58 | value = instance.String() 59 | if value != "value provided inside the SingletonStruct" { 60 | t.Errorf(`unexpected value received: "%s"`, value) 61 | } 62 | if counter != 1 { 63 | t.Errorf(`unexpected counter received: "%d"`, counter) 64 | } 65 | }) 66 | 67 | t.Run("Expecting counter to be greater than 1 when instance is not defined as a singleton", func(t *testing.T) { 68 | genjector.Clean() 69 | counter = 0 70 | 71 | err := genjector.Bind(genjector.AsPointer[SingletonInterface, *SingletonStruct]()) 72 | if err != nil { 73 | t.Error("binding should not cause an error") 74 | } 75 | 76 | instance, err := genjector.NewInstance[SingletonInterface]() 77 | if err != nil { 78 | t.Error("initialization should not cause an error") 79 | } 80 | 81 | value := instance.String() 82 | if value != "value provided inside the SingletonStruct" { 83 | t.Errorf(`unexpected value received: "%s"`, value) 84 | } 85 | if counter != 1 { 86 | t.Errorf(`unexpected counter received: "%d"`, counter) 87 | } 88 | 89 | instance, err = genjector.NewInstance[SingletonInterface]() 90 | if err != nil { 91 | t.Error("initialization should not cause an error") 92 | } 93 | 94 | value = instance.String() 95 | if value != "value provided inside the SingletonStruct" { 96 | t.Errorf(`unexpected value received: "%s"`, value) 97 | } 98 | if counter <= 1 { 99 | t.Errorf(`unexpected counter received: "%d"`, counter) 100 | } 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /key.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | // baseKeySource is a concrete implementation for KeyOption interface. 4 | type baseKeySource[T any] struct{} 5 | 6 | // Key returns the instance of Key that represents a Container key for T type. 7 | // 8 | // It respects KeyOption interface. 9 | func (baseKeySource[T]) Key() Key { 10 | return Key{ 11 | Value: (*T)(nil), 12 | } 13 | } 14 | 15 | // Container returns the same instance of Container struct provided as an argument. 16 | // 17 | // It respects KeyOption interface. 18 | func (baseKeySource[T]) Container(container Container) Container { 19 | return container 20 | } 21 | 22 | // sliceKeySource is a concrete implementation for KeyOption interface. 23 | type sliceKeySource[T any] struct{} 24 | 25 | // Key returns the instance of Key that represents a Container key for slice T types. 26 | // 27 | // It respects KeyOption interface. 28 | func (sliceKeySource[T]) Key() Key { 29 | return Key{ 30 | Value: (*[]T)(nil), 31 | } 32 | } 33 | 34 | // Container returns the same instance of Container struct provided as an argument. 35 | // 36 | // It respects KeyOption interface. 37 | func (sliceKeySource[T]) Container(container Container) Container { 38 | return container 39 | } 40 | 41 | // mapKeySource is a concrete implementation for KeyOption interface. 42 | type mapKeySource[K comparable, T any] struct{} 43 | 44 | // Key returns the instance of Key that represents a Container key for map K-T pairs. 45 | // 46 | // It respects KeyOption interface. 47 | func (mapKeySource[K, T]) Key() Key { 48 | return Key{ 49 | Value: (*map[K]T)(nil), 50 | } 51 | } 52 | 53 | // Container returns the same instance of Container struct provided as an argument. 54 | // 55 | // It respects KeyOption interface. 56 | func (mapKeySource[K, T]) Container(container Container) Container { 57 | return container 58 | } 59 | 60 | // sameKeyOption is a concrete implementation for KeyOption interface. 61 | type sameKeyOption struct{} 62 | 63 | // Key returns the same instance of Key struct provided as an argument. 64 | // 65 | // It respects KeyOption interface. 66 | func (sameKeyOption) Key(key Key) Key { 67 | return key 68 | } 69 | 70 | // Container returns the same instance of Container struct provided as an argument. 71 | // 72 | // It respects KeyOption interface. 73 | func (sameKeyOption) Container(container Container) Container { 74 | return container 75 | } 76 | 77 | // annotatedKeyOption is a concrete implementation for KeyOption interface. 78 | type annotatedKeyOption struct { 79 | annotation string 80 | } 81 | 82 | // Key wrapped instance of Key with a new value for the annotation. 83 | // 84 | // It respects KeyOption interface. 85 | func (o *annotatedKeyOption) Key(key Key) Key { 86 | return Key{ 87 | Annotation: o.annotation, 88 | Value: key.Value, 89 | } 90 | } 91 | 92 | // Container returns the same instance of Container struct provided as an argument. 93 | // 94 | // It respects KeyOption interface. 95 | func (*annotatedKeyOption) Container(container Container) Container { 96 | return container 97 | } 98 | 99 | // containerKeyOption is a concrete implementation for KeyOption interface. 100 | type containerKeyOption struct { 101 | container Container 102 | } 103 | 104 | // Key returns the same instance of Key struct provided as an argument. 105 | // 106 | // It respects KeyOption interface. 107 | func (*containerKeyOption) Key(key Key) Key { 108 | return key 109 | } 110 | 111 | // Container returns an inner instance of Container. 112 | // 113 | // It respects KeyOption interface. 114 | func (o *containerKeyOption) Container(Container) Container { 115 | return o.container 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Reference](https://pkg.go.dev/badge/github.com/Ompluscator/genjector.svg)](https://pkg.go.dev/github.com/Ompluscator/genjector) 2 | ![Coverage](https://img.shields.io/badge/Coverage-97.8%25-brightgreen) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/ompluscator/genjector)](https://goreportcard.com/report/github.com/ompluscator/genjector) 4 | 5 | # Package Genjector 6 | Reflection-free Run-Time Dependency Injection framework for Go 1.18+ 7 | 8 | Read [blog](https://hindenbug.io/genjector-reflection-free-run-time-dependency-injection-framework-for-go-1-18-1022d134123d) for better examples! 9 | 10 | ## The Goal 11 | The purpose of The Genjector package is to provide a Dependency 12 | Injection framework without relying on reflection and depending solely 13 | on Go Generics (provided from Go version 1.18). 14 | 15 | It supports many different features: 16 | + Binding concrete implementations to particular interfaces. 17 | + Binding implementations as pointers or values. 18 | + Binding implementations with Provider methods. 19 | + Binding implementations with concrete instances. 20 | + Define Binding as singletons. 21 | + Define annotations for Binding. 22 | + Define slices and maps of implementations. 23 | + ... 24 | 25 | ## Benchmark 26 | While providing the most of known features of Dependency Injection 27 | frameworks, The Genjector Package also delivers top performance 28 | comparing to current widely used Run-Time DI frameworks, (so, solutions 29 | based on code generators are excluded). 30 | 31 | ```shell 32 | goos: darwin 33 | goarch: amd64 34 | pkg: github.com/ompluscator/genjector/_benchmark 35 | cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz 36 | Benchmark 37 | Benchmark/github.com/golobby/container/v3 38 | Benchmark/github.com/golobby/container/v3-8 2834061 409.6 ns/op 39 | Benchmark/github.com/goava/di 40 | Benchmark/github.com/goava/di-8 4568984 261.9 ns/op 41 | Benchmark/github.com/goioc/di 42 | Benchmark/github.com/goioc/di-8 19844284 60.66 ns/op 43 | Benchmark/go.uber.org/dig 44 | Benchmark/go.uber.org/dig-8 755488 1497 ns/op 45 | Benchmark/flamingo.me/dingo 46 | Benchmark/flamingo.me/dingo-8 2373394 503.7 ns/op 47 | Benchmark/github.com/samber/do 48 | Benchmark/github.com/samber/do-8 3585386 336.0 ns/op 49 | Benchmark/github.com/ompluscator/genjector 50 | Benchmark/github.com/ompluscator/genjector-8 21460600 55.71 ns/op 51 | Benchmark/github.com/vardius/gocontainer 52 | Benchmark/github.com/vardius/gocontainer-8 60947049 20.25 ns/op 53 | Benchmark/github.com/go-kata/kinit 54 | Benchmark/github.com/go-kata/kinit-8 733842 1451 ns/op 55 | Benchmark/github.com/Fs02/wire 56 | Benchmark/github.com/Fs02/wire-8 25099182 47.43 ns/op 57 | PASS 58 | ``` 59 | 60 | ## Examples 61 | Detailed examples can be found inside inner "examples" package. 62 | 63 | Some simple code blocks can be found bellow. 64 | 65 | ### Simple Pointer 66 | ```go 67 | package example 68 | 69 | type ServiceInterface interface { 70 | String() string 71 | } 72 | 73 | type Service struct { 74 | value string 75 | } 76 | 77 | func (s *Service) Init() { 78 | s.value = "value provided inside the Service" 79 | } 80 | 81 | func (s *Service) String() string { 82 | return s.value 83 | } 84 | 85 | err := genjector.Bind(genjector.AsPointer[ServiceInterface, *Service]()) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | instance, err := genjector.NewInstance[ServiceInterface]() 91 | if err != nil { 92 | return err 93 | } 94 | 95 | value := instance.String() 96 | if value != "value provided inside the Service" { 97 | return err 98 | } 99 | ``` 100 | 101 | ### Complex Pointer 102 | ```go 103 | package example 104 | 105 | type ServiceInterface interface { 106 | String() string 107 | } 108 | 109 | type Service struct { 110 | value string 111 | } 112 | 113 | func (s *Service) Init() { 114 | s.value = "value provided inside the Service" 115 | } 116 | 117 | func (s *Service) String() string { 118 | return s.value 119 | } 120 | 121 | err := genjector.Bind( 122 | genjector.AsPointer[ServiceInterface, *Service](), 123 | genjector.AsSingleton(), 124 | genjector.WithAnnotation("service") 125 | ) 126 | if err != nil { 127 | return err 128 | } 129 | 130 | instance, err := genjector.NewInstance[ServiceInterface]( 131 | genjector.WithAnnotation("service"), 132 | ) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | value := instance.String() 138 | if value != "value provided inside the Service" { 139 | return err 140 | } 141 | ``` 142 | -------------------------------------------------------------------------------- /_benchmark/benchmarks_test.go: -------------------------------------------------------------------------------- 1 | package _benchmark_test 2 | 3 | import ( 4 | "flamingo.me/dingo" 5 | "github.com/Fs02/wire" 6 | "github.com/go-kata/kinit/kinitx" 7 | "github.com/goava/di" 8 | di2 "github.com/goioc/di" 9 | "github.com/ompluscator/genjector" 10 | "github.com/samber/do" 11 | "github.com/vardius/gocontainer" 12 | "go.uber.org/dig" 13 | "reflect" 14 | "testing" 15 | ) 16 | 17 | type BenchmarkInterface interface { 18 | Method() string 19 | } 20 | 21 | type BenchmarkStruct struct { 22 | value string 23 | } 24 | 25 | func (s *BenchmarkStruct) Method() string { 26 | return s.value 27 | } 28 | 29 | func Benchmark(b *testing.B) { 30 | b.Run("github.com/golobby/container/v3", func(b *testing.B) { 31 | var variable BenchmarkInterface 32 | 33 | cont := container.New() 34 | cont.Transient(func() BenchmarkInterface { 35 | return &BenchmarkStruct{} 36 | }) 37 | b.ResetTimer() 38 | 39 | for i := 0; i < b.N; i++ { 40 | cont.Resolve(&variable) 41 | } 42 | 43 | variable.Method() 44 | }) 45 | 46 | b.Run("github.com/goava/di", func(b *testing.B) { 47 | var variable BenchmarkInterface 48 | 49 | container, _ := di.New(di.Provide(func() *BenchmarkStruct { 50 | return &BenchmarkStruct{} 51 | }, di.As(new(BenchmarkInterface)))) 52 | b.ResetTimer() 53 | 54 | for i := 0; i < b.N; i++ { 55 | container.Resolve(&variable) 56 | } 57 | 58 | variable.Method() 59 | }) 60 | 61 | b.Run("github.com/goioc/di", func(b *testing.B) { 62 | var variable BenchmarkInterface 63 | 64 | di2.RegisterBean("BenchmarkInterface", reflect.TypeOf(new(BenchmarkStruct))) 65 | di2.InitializeContainer() 66 | b.ResetTimer() 67 | 68 | for i := 0; i < b.N; i++ { 69 | variable = di2.GetInstance("BenchmarkInterface").(BenchmarkInterface) 70 | } 71 | 72 | variable.Method() 73 | }) 74 | 75 | b.Run("go.uber.org/dig", func(b *testing.B) { 76 | var variable BenchmarkInterface 77 | 78 | container := dig.New() 79 | container.Provide(func() *BenchmarkStruct { 80 | return &BenchmarkStruct{} 81 | }, dig.As(new(BenchmarkInterface))) 82 | b.ResetTimer() 83 | 84 | for i := 0; i < b.N; i++ { 85 | container.Invoke(func(result BenchmarkInterface) { 86 | variable = result 87 | }) 88 | } 89 | 90 | variable.Method() 91 | }) 92 | 93 | b.Run("flamingo.me/dingo", func(b *testing.B) { 94 | var variable BenchmarkInterface 95 | 96 | injector, _ := dingo.NewInjector() 97 | injector.Bind(new(BenchmarkInterface)).To(&BenchmarkStruct{}) 98 | b.ResetTimer() 99 | 100 | for i := 0; i < b.N; i++ { 101 | value, _ := injector.GetInstance(new(BenchmarkInterface)) 102 | variable = value.(BenchmarkInterface) 103 | } 104 | 105 | variable.Method() 106 | }) 107 | 108 | b.Run("github.com/samber/do", func(b *testing.B) { 109 | var variable BenchmarkInterface 110 | 111 | injector := do.New() 112 | do.Provide(injector, func(injector *do.Injector) (BenchmarkInterface, error) { 113 | return &BenchmarkStruct{}, nil 114 | }) 115 | b.ResetTimer() 116 | 117 | for i := 0; i < b.N; i++ { 118 | variable = do.MustInvoke[BenchmarkInterface](injector) 119 | } 120 | 121 | variable.Method() 122 | }) 123 | 124 | b.Run("github.com/ompluscator/genjector", func(b *testing.B) { 125 | var variable BenchmarkInterface 126 | 127 | genjector.MustBind(genjector.AsPointer[BenchmarkInterface, *BenchmarkStruct]()) 128 | b.ResetTimer() 129 | 130 | for i := 0; i < b.N; i++ { 131 | variable = genjector.MustNewInstance[BenchmarkInterface]() 132 | } 133 | 134 | variable = &BenchmarkStruct{} 135 | variable.Method() 136 | }) 137 | 138 | b.Run("github.com/vardius/gocontainer", func(b *testing.B) { 139 | var variable BenchmarkInterface 140 | 141 | cont := gocontainer.New() 142 | cont.Register("BenchmarkInterface", &BenchmarkStruct{}) 143 | b.ResetTimer() 144 | 145 | for i := 0; i < b.N; i++ { 146 | variable = cont.MustGet("BenchmarkInterface").(BenchmarkInterface) 147 | } 148 | 149 | variable.Method() 150 | }) 151 | 152 | b.Run("github.com/go-kata/kinit", func(b *testing.B) { 153 | var variable BenchmarkInterface 154 | 155 | kinitx.Provide(func() BenchmarkInterface { 156 | return &BenchmarkStruct{} 157 | }) 158 | b.ResetTimer() 159 | 160 | for i := 0; i < b.N; i++ { 161 | kinitx.MustRun(func(benchmarkInterface BenchmarkInterface) { 162 | variable = benchmarkInterface 163 | }) 164 | } 165 | 166 | variable.Method() 167 | }) 168 | 169 | b.Run("github.com/Fs02/wire", func(b *testing.B) { 170 | var variable *BenchmarkStruct 171 | 172 | cont := wire.New() 173 | cont.Connect(&BenchmarkStruct{}) 174 | cont.Apply() 175 | b.ResetTimer() 176 | 177 | for i := 0; i < b.N; i++ { 178 | cont.Resolve(&variable) 179 | } 180 | 181 | variable.Method() 182 | }) 183 | } 184 | -------------------------------------------------------------------------------- /injection.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Key is a struct that contains information for Binding keys 8 | // inside a Container. 9 | // 10 | // It is meant to be used only for internal purposes. 11 | type Key struct { 12 | Annotation string 13 | Value interface{} 14 | } 15 | 16 | // Generate delivers a final Binding key for the Container. 17 | func (k Key) Generate() interface{} { 18 | if len(k.Annotation) > 0 { 19 | return [2]interface{}{k.Annotation, k.Value} 20 | } 21 | return k.Value 22 | } 23 | 24 | // KeySource represents an interface that builds a Key for Binding. 25 | type KeySource interface { 26 | Key() Key 27 | } 28 | 29 | // KeyOption represents an interface that overrides creation of Key 30 | // and Container. 31 | type KeyOption interface { 32 | Key(key Key) Key 33 | Container(container Container) Container 34 | } 35 | 36 | // Binding represents an interface that delivers new instance for 37 | // particular interface (or a struct). 38 | type Binding interface { 39 | Instance(initialize bool) (interface{}, error) 40 | } 41 | 42 | // BindingSource represents an interface that delivers starting Key and 43 | // Binding instances, that later could be overridden by KeyOption or 44 | // BindingOption. 45 | type BindingSource[T any] interface { 46 | Key() Key 47 | Binding() (Binding, error) 48 | } 49 | 50 | // FollowingBindingSource represents an interface for a Binding instance 51 | // that requires to get previous instance of Binding inside Container, 52 | // before the new one should be stored on that place. 53 | type FollowingBindingSource[T any] interface { 54 | SetPrevious(binding Binding) 55 | } 56 | 57 | // BindingOption represents an interface that overrides creation of Key, 58 | // Binding and Container. 59 | type BindingOption interface { 60 | Key(key Key) Key 61 | Container(container Container) Container 62 | Binding(binding Binding) (Binding, error) 63 | } 64 | 65 | // Container is a child type used for storing all Binding instances. 66 | type Container map[interface{}]Binding 67 | 68 | // global is a concrete global Container 69 | var global = NewContainer() 70 | 71 | // NewContainer delivers a new instance of Container. 72 | func NewContainer() Container { 73 | return map[interface{}]Binding{} 74 | } 75 | 76 | // Bind executes complete logic for binding particular value (or pointer) to 77 | // desired interface (or struct). By default, it stores all Binding instances 78 | // into default inner Container. 79 | // 80 | // It requires only BindingSource to be passed as an argument and all other 81 | // instances of BindingOption are optional. 82 | // 83 | // At this point, if Binding for particular interface (or struct) is not defined, 84 | // it uses its own fallback Binding. Still, it works fully only for values, not pointers, 85 | // as for pointers it returns nil value. That means that pointer Binding 86 | // should be always defined. 87 | func Bind[T any](source BindingSource[T], options ...BindingOption) error { 88 | key := source.Key() 89 | 90 | internal := global 91 | for _, option := range options { 92 | key = option.Key(key) 93 | internal = option.Container(internal) 94 | } 95 | 96 | generated := key.Generate() 97 | 98 | if child, ok := source.(FollowingBindingSource[T]); ok { 99 | parent, ok := internal[generated] 100 | if ok { 101 | child.SetPrevious(parent) 102 | } 103 | } 104 | 105 | binding, err := source.Binding() 106 | if err != nil { 107 | return err 108 | } 109 | 110 | for _, option := range options { 111 | binding, err = option.Binding(binding) 112 | if err != nil { 113 | return err 114 | } 115 | } 116 | 117 | internal[generated] = binding 118 | return nil 119 | } 120 | 121 | // MustBind wraps Bind method, by making sure error is not returned as an argument. 122 | // 123 | // Still, in case of error, it panics. 124 | func MustBind[T any](source BindingSource[T], options ...BindingOption) { 125 | err := Bind(source, options...) 126 | if err != nil { 127 | panic(err) 128 | } 129 | } 130 | 131 | // NewInstance executes complete logic for initializing value (or pointer) for 132 | // desired interface (or struct). By default, it uses Binding instance from default 133 | // inner Container. If such Binding can not be found, it tries to make its own 134 | // fallback Binding. 135 | // 136 | // All instances of BindingOption are optional. 137 | // 138 | // At this point, if Binding for particular interface (or struct) is not defined, 139 | // it uses its own fallback Binding. Still, it works fully only for values, not pointers, 140 | // as for pointers it returns nil value. That means that pointer Binding 141 | // should be always defined. 142 | func NewInstance[T any](options ...KeyOption) (T, error) { 143 | var empty T 144 | source := &baseKeySource[T]{} 145 | 146 | key := source.Key() 147 | 148 | internal := global 149 | for _, option := range options { 150 | key = option.Key(key) 151 | internal = option.Container(internal) 152 | } 153 | 154 | generated := key.Generate() 155 | 156 | binding, ok := internal[generated] 157 | if !ok { 158 | var err error 159 | binding, err = getFallbackBinding[T]() 160 | if err != nil { 161 | return empty, err 162 | } 163 | } 164 | 165 | instance, err := binding.Instance(true) 166 | if err != nil { 167 | return empty, err 168 | } 169 | 170 | result, ok := instance.(T) 171 | if !ok { 172 | return empty, fmt.Errorf(`invalid binding is defined for key "%v"`, generated) 173 | } 174 | 175 | return result, nil 176 | } 177 | 178 | // MustNewInstance wraps NewInstance method, by making sure error is not returned as an argument. 179 | // 180 | // Still, in case of error, it panics. 181 | func MustNewInstance[T any](options ...KeyOption) T { 182 | instance, err := NewInstance[T](options...) 183 | if err != nil { 184 | panic(err) 185 | } 186 | 187 | return instance 188 | } 189 | 190 | // Clean creates a new instance of inner Container. 191 | func Clean() { 192 | global = NewContainer() 193 | } 194 | 195 | // getFallbackBinding creates a new instance of fallback Binding. 196 | func getFallbackBinding[T any]() (Binding, error) { 197 | var binding Binding 198 | var err error 199 | 200 | source := AsValue[T, T]() 201 | binding, err = source.Binding() 202 | if err == nil { 203 | instance, err := binding.Instance(false) 204 | if err == nil { 205 | if _, ok := instance.(T); ok { 206 | return binding, nil 207 | } 208 | } 209 | } 210 | 211 | return nil, err 212 | } 213 | -------------------------------------------------------------------------------- /injection_test.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestKey_Generate(t *testing.T) { 10 | key := Key{} 11 | generated := key.Generate() 12 | if generated != nil { 13 | t.Errorf("expected nil, got %v", generated) 14 | } 15 | 16 | key = Key{ 17 | Annotation: "annotation", 18 | } 19 | generated = key.Generate() 20 | if !reflect.DeepEqual(generated, [2]interface{}{"annotation", nil}) { 21 | t.Errorf("expected concrete value, got %v", generated) 22 | } 23 | 24 | key = Key{ 25 | Annotation: "annotation", 26 | Value: "value", 27 | } 28 | generated = key.Generate() 29 | if !reflect.DeepEqual(generated, [2]interface{}{"annotation", "value"}) { 30 | t.Errorf("expected concrete value, got %v", generated) 31 | } 32 | } 33 | 34 | func TestNewContainer(t *testing.T) { 35 | container := NewContainer() 36 | if !reflect.DeepEqual(container, Container{}) { 37 | t.Errorf("expected concrete value, got %v", container) 38 | } 39 | } 40 | 41 | func TestBind_bindingError(t *testing.T) { 42 | err := Bind[int](&testBindingSource{ 43 | binding: func() (Binding, error) { 44 | return nil, errors.New("error") 45 | }, 46 | key: func() Key { 47 | return Key{} 48 | }, 49 | }) 50 | if err == nil { 51 | t.Error("expected error, got nil") 52 | } 53 | } 54 | 55 | func TestBind_bindingOptionError(t *testing.T) { 56 | err := Bind[int](&testBindingSource{ 57 | binding: func() (Binding, error) { 58 | return &testBinding{}, nil 59 | }, 60 | key: func() Key { 61 | return Key{} 62 | }, 63 | }, &testBindingOption{ 64 | binding: func(binding Binding) (Binding, error) { 65 | return nil, errors.New("error") 66 | }, 67 | key: func(key Key) Key { 68 | return key 69 | }, 70 | container: func(container Container) Container { 71 | return NewContainer() 72 | }, 73 | }) 74 | if err == nil { 75 | t.Error("expected error, got nil") 76 | } 77 | } 78 | 79 | func TestBind_binding_success(t *testing.T) { 80 | inner := NewContainer() 81 | if !reflect.DeepEqual(inner, Container{}) { 82 | t.Errorf("expected concrete value, got %v", inner) 83 | } 84 | 85 | err := Bind[int](&testBindingSource{ 86 | binding: func() (Binding, error) { 87 | return &testBinding{}, nil 88 | }, 89 | key: func() Key { 90 | return Key{ 91 | Annotation: "first", 92 | Value: "1", 93 | } 94 | }, 95 | }, &testBindingOption{ 96 | binding: func(binding Binding) (Binding, error) { 97 | binding.(*testBinding).value = "value" 98 | return binding, nil 99 | }, 100 | key: func(key Key) Key { 101 | return Key{ 102 | Annotation: key.Annotation + "second", 103 | Value: key.Value.(string) + "2", 104 | } 105 | }, 106 | container: func(Container) Container { 107 | return inner 108 | }, 109 | }) 110 | if err != nil { 111 | t.Errorf("expected nil, got error %v", err) 112 | } 113 | 114 | if !reflect.DeepEqual(inner, Container{ 115 | [2]interface{}{ 116 | "firstsecond", 117 | "12", 118 | }: &testBinding{ 119 | value: "value", 120 | }, 121 | }) { 122 | t.Errorf("expected concrete value, got %v", inner) 123 | } 124 | } 125 | 126 | func TestMustBind(t *testing.T) { 127 | defer func() { 128 | if r := recover(); r == nil { 129 | t.Error("the code did not panic") 130 | } 131 | }() 132 | 133 | MustBind[int](&testBindingSource{ 134 | binding: func() (Binding, error) { 135 | return nil, errors.New("error") 136 | }, 137 | key: func() Key { 138 | return Key{} 139 | }, 140 | }) 141 | } 142 | 143 | func TestNewInstance_defaultValue(t *testing.T) { 144 | instance, err := NewInstance[testStruct]() 145 | if err != nil { 146 | t.Errorf("expected nil, got error %s", err) 147 | } 148 | 149 | if !reflect.DeepEqual(instance, testStruct{ 150 | a: "test", 151 | b: 10, 152 | }) { 153 | t.Errorf("expected concrete value, got %v", instance) 154 | } 155 | } 156 | 157 | func TestNewInstance_defaultPointer(t *testing.T) { 158 | instance, err := NewInstance[*testStruct]() 159 | if err != nil { 160 | t.Errorf("expected nil, got error %s", err) 161 | } 162 | 163 | if instance != nil { 164 | t.Errorf("expected nil, got %v", instance) 165 | } 166 | } 167 | 168 | func TestNewInstance_defaultError(t *testing.T) { 169 | instance, err := NewInstance[interface{}]() 170 | if err == nil { 171 | t.Error("expected error, got nil") 172 | } 173 | 174 | if instance != nil { 175 | t.Errorf("expected nil, got %v", instance) 176 | } 177 | } 178 | 179 | func TestNewInstance_success(t *testing.T) { 180 | inner := Container{ 181 | (*int)(nil): &testBinding{ 182 | instance: func(initialize bool) (interface{}, error) { 183 | return nil, errors.New("error") 184 | }, 185 | }, 186 | } 187 | 188 | instance, err := NewInstance[int](WithContainer(inner)) 189 | if err == nil { 190 | t.Error("expected error, got nil") 191 | } 192 | 193 | if instance != 0 { 194 | t.Errorf("expected 0, got %v", instance) 195 | } 196 | } 197 | 198 | func TestNewInstance_invalid(t *testing.T) { 199 | inner := Container{ 200 | (*int)(nil): &testBinding{ 201 | instance: func(initialize bool) (interface{}, error) { 202 | return "value", nil 203 | }, 204 | }, 205 | } 206 | 207 | instance, err := NewInstance[int](WithContainer(inner)) 208 | if err == nil { 209 | t.Error("expected error, got nil") 210 | } 211 | 212 | if instance != 0 { 213 | t.Errorf("expected 0, got %v", instance) 214 | } 215 | } 216 | 217 | func TestNewInstance_simple_success(t *testing.T) { 218 | inner := Container{ 219 | (*int)(nil): &testBinding{ 220 | instance: func(initialize bool) (interface{}, error) { 221 | return 10, nil 222 | }, 223 | }, 224 | } 225 | 226 | instance, err := NewInstance[int](WithContainer(inner)) 227 | if err != nil { 228 | t.Errorf("expected nil, got error %s", err) 229 | } 230 | 231 | if instance != 10 { 232 | t.Errorf("expected 10, got %v", instance) 233 | } 234 | } 235 | 236 | func TestNewInstance_complex_success(t *testing.T) { 237 | inner := Container{ 238 | [2]interface{}{"annotation", nil}: &testBinding{ 239 | instance: func(initialize bool) (interface{}, error) { 240 | return 10, nil 241 | }, 242 | }, 243 | } 244 | 245 | instance, err := NewInstance[int](&testKeyOption{ 246 | key: func(key Key) Key { 247 | return Key{ 248 | Annotation: "annotation", 249 | } 250 | }, 251 | container: func(container Container) Container { 252 | return inner 253 | }, 254 | }) 255 | if err != nil { 256 | t.Errorf("expected nil, got error %s", err) 257 | } 258 | 259 | if instance != 10 { 260 | t.Errorf("expected 10, got %v", instance) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /key_test.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_baseKeySource_Key(t *testing.T) { 9 | if new(baseKeySource[int]).Key() != new(baseKeySource[int]).Key() { 10 | t.Error("keys are different") 11 | } 12 | 13 | if new(baseKeySource[interface{}]).Key() != new(baseKeySource[interface{}]).Key() { 14 | t.Error("keys are different") 15 | } 16 | 17 | if new(baseKeySource[string]).Key() == new(baseKeySource[int]).Key() { 18 | t.Error("keys are the same") 19 | } 20 | 21 | if new(baseKeySource[interface{}]).Key() == new(baseKeySource[*int]).Key() { 22 | t.Error("keys are the same") 23 | } 24 | 25 | if new(baseKeySource[interface{}]).Key() == new(baseKeySource[*struct{}]).Key() { 26 | t.Error("keys are the same") 27 | } 28 | 29 | if new(baseKeySource[struct{}]).Key() == new(baseKeySource[*struct{}]).Key() { 30 | t.Error("keys are the same") 31 | } 32 | } 33 | 34 | func Test_baseKeySource_Container(t *testing.T) { 35 | container := map[interface{}]Binding{ 36 | "something": nil, 37 | } 38 | 39 | first := new(baseKeySource[int]).Container(container) 40 | second := new(baseKeySource[int]).Container(container) 41 | if !reflect.DeepEqual(first, second) { 42 | t.Error("containers are different") 43 | } 44 | 45 | first = new(baseKeySource[interface{}]).Container(container) 46 | second = new(baseKeySource[struct{}]).Container(container) 47 | if !reflect.DeepEqual(first, second) { 48 | t.Error("containers are different") 49 | } 50 | } 51 | 52 | func Test_sliceKeySource_Key(t *testing.T) { 53 | if new(sliceKeySource[int]).Key() != new(sliceKeySource[int]).Key() { 54 | t.Error("keys are different") 55 | } 56 | 57 | if new(sliceKeySource[interface{}]).Key() != new(sliceKeySource[interface{}]).Key() { 58 | t.Error("keys are different") 59 | } 60 | 61 | if new(sliceKeySource[string]).Key() == new(baseKeySource[string]).Key() { 62 | t.Error("keys are the same") 63 | } 64 | 65 | if new(sliceKeySource[string]).Key() == new(sliceKeySource[int]).Key() { 66 | t.Error("keys are the same") 67 | } 68 | 69 | if new(sliceKeySource[interface{}]).Key() == new(sliceKeySource[*int]).Key() { 70 | t.Error("keys are the same") 71 | } 72 | 73 | if new(sliceKeySource[interface{}]).Key() == new(sliceKeySource[*struct{}]).Key() { 74 | t.Error("keys are the same") 75 | } 76 | 77 | if new(sliceKeySource[struct{}]).Key() == new(sliceKeySource[*struct{}]).Key() { 78 | t.Error("keys are the same") 79 | } 80 | } 81 | 82 | func Test_sliceKeySource_Container(t *testing.T) { 83 | container := map[interface{}]Binding{ 84 | "something": nil, 85 | } 86 | 87 | first := new(sliceKeySource[int]).Container(container) 88 | second := new(sliceKeySource[int]).Container(container) 89 | if !reflect.DeepEqual(first, second) { 90 | t.Error("containers are different") 91 | } 92 | 93 | first = new(sliceKeySource[interface{}]).Container(container) 94 | second = new(sliceKeySource[struct{}]).Container(container) 95 | if !reflect.DeepEqual(first, second) { 96 | t.Error("containers are different") 97 | } 98 | } 99 | 100 | func Test_mapKeySource_Key(t *testing.T) { 101 | if new(mapKeySource[string, int]).Key() != new(mapKeySource[string, int]).Key() { 102 | t.Error("keys are different") 103 | } 104 | 105 | if new(mapKeySource[int, interface{}]).Key() != new(mapKeySource[int, interface{}]).Key() { 106 | t.Error("keys are different") 107 | } 108 | 109 | if new(mapKeySource[string, string]).Key() == new(baseKeySource[string]).Key() { 110 | t.Error("keys are the same") 111 | } 112 | 113 | if new(mapKeySource[string, string]).Key() == new(mapKeySource[string, int]).Key() { 114 | t.Error("keys are the same") 115 | } 116 | 117 | if new(mapKeySource[string, string]).Key() == new(mapKeySource[int, string]).Key() { 118 | t.Error("keys are the same") 119 | } 120 | 121 | if new(mapKeySource[string, interface{}]).Key() == new(mapKeySource[string, *int]).Key() { 122 | t.Error("keys are the same") 123 | } 124 | 125 | if new(mapKeySource[int, interface{}]).Key() == new(mapKeySource[string, *struct{}]).Key() { 126 | t.Error("keys are the same") 127 | } 128 | 129 | if new(mapKeySource[string, struct{}]).Key() == new(mapKeySource[string, *struct{}]).Key() { 130 | t.Error("keys are the same") 131 | } 132 | } 133 | 134 | func Test_mapKeySource_Container(t *testing.T) { 135 | container := map[interface{}]Binding{ 136 | "something": nil, 137 | } 138 | 139 | first := new(mapKeySource[string, int]).Container(container) 140 | second := new(mapKeySource[string, int]).Container(container) 141 | if !reflect.DeepEqual(first, second) { 142 | t.Error("containers are different") 143 | } 144 | 145 | first = new(mapKeySource[int, interface{}]).Container(container) 146 | second = new(mapKeySource[string, struct{}]).Container(container) 147 | if !reflect.DeepEqual(first, second) { 148 | t.Error("containers are different") 149 | } 150 | } 151 | 152 | func Test_sameKeyOption_Key(t *testing.T) { 153 | key := Key{ 154 | Value: 2, 155 | Annotation: "value", 156 | } 157 | 158 | first := new(sameKeyOption).Key(key) 159 | second := new(sameKeyOption).Key(key) 160 | if !reflect.DeepEqual(first, second) { 161 | t.Error("keys are different") 162 | } 163 | } 164 | 165 | func Test_sameKeyOption_Container(t *testing.T) { 166 | container := map[interface{}]Binding{ 167 | "something": nil, 168 | } 169 | 170 | first := new(sameKeyOption).Container(container) 171 | second := new(sameKeyOption).Container(container) 172 | if !reflect.DeepEqual(first, second) { 173 | t.Error("containers are different") 174 | } 175 | } 176 | 177 | func Test_annotatedKeyOption_Key(t *testing.T) { 178 | result := (&annotatedKeyOption{ 179 | annotation: "annotation", 180 | }).Key(Key{ 181 | Value: 2, 182 | Annotation: "value", 183 | }) 184 | if !reflect.DeepEqual(Key{ 185 | Value: 2, 186 | Annotation: "annotation", 187 | }, result) { 188 | t.Error("keys are different") 189 | } 190 | 191 | result = (&annotatedKeyOption{ 192 | annotation: "", 193 | }).Key(Key{ 194 | Value: 2, 195 | Annotation: "value", 196 | }) 197 | if !reflect.DeepEqual(Key{ 198 | Value: 2, 199 | Annotation: "", 200 | }, result) { 201 | t.Error("keys are different") 202 | } 203 | } 204 | 205 | func Test_annotatedKeyOption_Container(t *testing.T) { 206 | container := map[interface{}]Binding{ 207 | "something": nil, 208 | } 209 | 210 | first := new(annotatedKeyOption).Container(container) 211 | second := new(annotatedKeyOption).Container(container) 212 | if !reflect.DeepEqual(first, second) { 213 | t.Error("containers are different") 214 | } 215 | } 216 | 217 | func Test_containerKeyOption_Key(t *testing.T) { 218 | key := Key{ 219 | Value: 2, 220 | Annotation: "value", 221 | } 222 | 223 | first := new(containerKeyOption).Key(key) 224 | second := new(containerKeyOption).Key(key) 225 | if !reflect.DeepEqual(first, second) { 226 | t.Error("keys are different") 227 | } 228 | } 229 | 230 | func Test_containerKeyOption_Container(t *testing.T) { 231 | result := (&containerKeyOption{ 232 | container: Container{ 233 | "first": nil, 234 | }, 235 | }).Container(Container{ 236 | "second": nil, 237 | }) 238 | if !reflect.DeepEqual(Container{ 239 | "first": nil, 240 | }, result) { 241 | t.Error("containers are different") 242 | } 243 | 244 | result = (&containerKeyOption{ 245 | container: nil, 246 | }).Container(Container{ 247 | "first": nil, 248 | }) 249 | if result != nil { 250 | t.Error("containers are different") 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /chain.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import "fmt" 4 | 5 | // sliceBinding is a concrete implementation for Binding interface. 6 | type sliceBinding[T any] struct { 7 | previous *sliceBinding[T] 8 | current Binding 9 | } 10 | 11 | // Instance returns a slice of T types by executing current Binding and 12 | // all other preceding ones. First it places previous in a slice, and 13 | // then stores the instance of the current. 14 | // 15 | // It respects Binding interface. 16 | func (b *sliceBinding[T]) Instance(initialize bool) (interface{}, error) { 17 | var result []T 18 | if initialize && b.previous != nil { 19 | instance, err := b.previous.Instance(initialize) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | transformed, ok := instance.([]T) 25 | if !ok { 26 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, result, instance) 27 | } 28 | result = append(result, transformed...) 29 | } 30 | 31 | instance, err := b.current.Instance(initialize) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | transformed, ok := instance.(T) 37 | if !ok { 38 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, result, instance) 39 | } 40 | 41 | result = append(result, transformed) 42 | return result, err 43 | } 44 | 45 | // sliceBindingSource is a concrete implementation for BindingSource interface. 46 | type sliceBindingSource[T any] struct { 47 | previous Binding 48 | source BindingSource[T] 49 | keySource KeySource 50 | } 51 | 52 | // Binding returns an instance of a new Binding. If there is no any 53 | // stored predecessor, it will deliver new Binding without containing any 54 | // previous Binding. In case predecessor is defined, all will be returned 55 | // together. 56 | // 57 | // It respects BindingSource interface. 58 | func (b *sliceBindingSource[T]) Binding() (Binding, error) { 59 | binding, err := b.source.Binding() 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | instance, err := binding.Instance(false) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | if _, ok := instance.(T); !ok { 70 | var initial T 71 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, initial, instance) 72 | } 73 | 74 | previous, ok := b.previous.(*sliceBinding[T]) 75 | if !ok { 76 | return &sliceBinding[T]{ 77 | current: binding, 78 | }, nil 79 | } 80 | 81 | return &sliceBinding[T]{ 82 | previous: previous, 83 | current: binding, 84 | }, nil 85 | } 86 | 87 | // SetPrevious stores preceding Binding as a previous one. 88 | // 89 | // It respects FollowingBindingSource interface. 90 | func (b *sliceBindingSource[T]) SetPrevious(binding Binding) { 91 | b.previous = binding 92 | } 93 | 94 | // Key executes the same method from inner KeyOption instance. 95 | // 96 | // It respects BindingOption interface. 97 | func (b *sliceBindingSource[T]) Key() Key { 98 | return b.keySource.Key() 99 | } 100 | 101 | // InSlice delivers a BindingSource for a slice of types T.. It is used as a wrapping 102 | // BindingSource for any other inner. It creates complex Binding in the background 103 | // that stores all T types in a slice and delivers it upon request by executing NewInstance 104 | // method for a slice of T types. 105 | // 106 | // Example: 107 | // err := :genjector.Bind( 108 | // 109 | // genjector.InSlice( 110 | // genjector.AsInstance[SliceInterface](&SliceStruct{ 111 | // value: "concrete value", 112 | // }), 113 | // ), 114 | // 115 | // ) 116 | // 117 | // BindingSource can be only used as the first argument to Bind method. 118 | func InSlice[T any](source BindingSource[T]) BindingSource[T] { 119 | return &sliceBindingSource[T]{ 120 | source: source, 121 | keySource: sliceKeySource[T]{}, 122 | } 123 | } 124 | 125 | // mapBinding is a concrete implementation for Binding interface. 126 | type mapBinding[K comparable, T any] struct { 127 | previous *mapBinding[K, T] 128 | key K 129 | current Binding 130 | } 131 | 132 | // Instance returns a map of K-T pairs by executing current Binding and 133 | // all other preceding ones. 134 | // 135 | // It respects Binding interface. 136 | func (b *mapBinding[K, T]) Instance(initialize bool) (interface{}, error) { 137 | result := map[K]T{} 138 | if initialize && b.previous != nil { 139 | instance, err := b.previous.Instance(initialize) 140 | if err != nil { 141 | return nil, err 142 | } 143 | 144 | transformed, ok := instance.(map[K]T) 145 | if !ok { 146 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, result, instance) 147 | } 148 | result = transformed 149 | } 150 | 151 | instance, err := b.current.Instance(initialize) 152 | if err != nil { 153 | return nil, err 154 | } 155 | 156 | transformed, ok := instance.(T) 157 | if !ok { 158 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, result, instance) 159 | } 160 | 161 | result[b.key] = transformed 162 | return result, err 163 | } 164 | 165 | // mapBindingSource is a concrete implementation for BindingSource interface. 166 | type mapBindingSource[K comparable, T any] struct { 167 | previous Binding 168 | source BindingSource[T] 169 | key K 170 | keySource KeySource 171 | } 172 | 173 | // Binding returns an instance of a new Binding. If there is no any 174 | // stored predecessor, it will deliver new Binding without containing any 175 | // previous Binding. In case predecessor is defined, all will be returned 176 | // together. 177 | // 178 | // It respects BindingSource interface. 179 | func (b *mapBindingSource[K, T]) Binding() (Binding, error) { 180 | binding, err := b.source.Binding() 181 | if err != nil { 182 | return nil, err 183 | } 184 | 185 | instance, _ := binding.Instance(false) 186 | if _, ok := instance.(T); !ok { 187 | var initial T 188 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, initial, instance) 189 | } 190 | 191 | previous, ok := b.previous.(*mapBinding[K, T]) 192 | if !ok { 193 | return &mapBinding[K, T]{ 194 | key: b.key, 195 | current: binding, 196 | }, nil 197 | } 198 | 199 | return &mapBinding[K, T]{ 200 | previous: previous, 201 | key: b.key, 202 | current: binding, 203 | }, nil 204 | } 205 | 206 | // SetPrevious stores preceding Binding as a previous one. 207 | // 208 | // It respects FollowingBindingSource interface. 209 | func (b *mapBindingSource[K, T]) SetPrevious(binding Binding) { 210 | b.previous = binding 211 | } 212 | 213 | // Key executes the same method from inner KeyOption instance. 214 | // 215 | // It respects BindingOption interface. 216 | func (b *mapBindingSource[K, T]) Key() Key { 217 | return b.keySource.Key() 218 | } 219 | 220 | // InMap delivers a BindingSource for a type T and key's type K, that creates a map 221 | // of K-T pairs. It is used as a wrapping BindingSource for any other inner. It creates 222 | // complex Binding in the background that stores all T types in a map and delivers it 223 | // upon request by executing NewInstance method for a K-T map. 224 | // 225 | // Example: 226 | // err := :genjector.Bind( 227 | // 228 | // genjector.InMap( 229 | // "third", 230 | // genjector.AsInstance[MapInterface](&MapStruct{ 231 | // value: "concrete value", 232 | // }), 233 | // ), 234 | // 235 | // ) 236 | // 237 | // BindingSource can be only used as the first argument to Bind method. 238 | func InMap[K comparable, T any](key K, source BindingSource[T]) BindingSource[T] { 239 | return &mapBindingSource[K, T]{ 240 | key: key, 241 | source: source, 242 | keySource: mapKeySource[K, T]{}, 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /binding.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import "fmt" 4 | 5 | // bindingSource is a concrete implementation for BindingSource interface. 6 | type bindingSource[T any] struct { 7 | binding Binding 8 | keySource baseKeySource[T] 9 | } 10 | 11 | // Binding returns containing instance of Binding interface. Initially it makes 12 | // the concrete instance, to check if instance matches desired type of Binding. 13 | // 14 | // It respects BindingSource interface. 15 | func (s *bindingSource[T]) Binding() (Binding, error) { 16 | instance, err := s.binding.Instance(false) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | if _, ok := instance.(T); !ok { 22 | var initial T 23 | return nil, fmt.Errorf(`binding is not possible for "%v" and "%v"`, initial, instance) 24 | } 25 | return s.binding, nil 26 | } 27 | 28 | // Key executes the same method from inner KeyOption instance. 29 | // 30 | // It respects BindingSource interface. 31 | func (s *bindingSource[T]) Key() Key { 32 | return s.keySource.Key() 33 | } 34 | 35 | // Initializable represents any struct that contains a method Init. 36 | // When such struct as defined AsPointer or AsValue, method Init will be 37 | // called during initialization process. 38 | type Initializable interface { 39 | Init() 40 | } 41 | 42 | // valueBinding is a concrete implementation for Binding interface. 43 | type valueBinding[S any] struct{} 44 | 45 | // Instance delivers the value of the concrete instance of type S. 46 | // If the pointer to the struct respects Initializable interface, 47 | // Init method will be called. 48 | // 49 | // It respects Binding interface. 50 | func (valueBinding[S]) Instance(initialize bool) (interface{}, error) { 51 | initial := *new(S) 52 | var instance interface{} = &initial 53 | if !initialize { 54 | return initial, nil 55 | } 56 | 57 | if value, ok := instance.(Initializable); ok { 58 | value.Init() 59 | } 60 | 61 | return initial, nil 62 | } 63 | 64 | // AsValue delivers a BindingSource for a type T, by binding a value of a struct 65 | // to the concrete interface (or the struct itself). It must be only used with value and 66 | // not pointer. In case pointer is used, code will return a nil value for the instance. 67 | // 68 | // Example: 69 | // err := genjector.Bind(genjector.AsValue[ValueInterface, ValueStruct]()) 70 | // 71 | // BindingSource can be only used as the first argument to Bind method. 72 | func AsValue[T any, S any]() BindingSource[T] { 73 | return &bindingSource[T]{ 74 | binding: valueBinding[S]{}, 75 | keySource: baseKeySource[T]{}, 76 | } 77 | } 78 | 79 | // pointerBinding is a concrete implementation for Binding interface. 80 | type pointerBinding[R any] struct{} 81 | 82 | // Instance delivers the pointer of the concrete instance of type S. 83 | // If the struct respects Initializable interface, Init method will be called. 84 | // 85 | // It respects Binding interface. 86 | func (pointerBinding[R]) Instance(initialize bool) (interface{}, error) { 87 | var instance interface{} = new(R) 88 | if !initialize { 89 | return instance, nil 90 | } 91 | 92 | if value, ok := instance.(Initializable); ok { 93 | value.Init() 94 | } 95 | 96 | return instance, nil 97 | } 98 | 99 | // AsPointer delivers a BindingSource for a type T, by binding pointer of a struct 100 | // to the concrete interface (or the struct itself). It must be only used with pointers and 101 | // not values. In case values is used, code will panic. 102 | // 103 | // Example: 104 | // err := genjector.Bind(genjector.AsPointer[PointerInterface, *PointerStruct]()) 105 | // 106 | // BindingSource can be only used as the first argument to Bind method. 107 | func AsPointer[T any, S *R, R any]() BindingSource[T] { 108 | return &bindingSource[T]{ 109 | binding: pointerBinding[R]{}, 110 | keySource: baseKeySource[T]{}, 111 | } 112 | } 113 | 114 | // ProviderMethod defines a type of a method that should delivers 115 | // an instance od type S. This method acts as an constructor method 116 | // and it is executed at the time of NewInstance method. 117 | // 118 | // It respects Binding interface. 119 | type ProviderMethod[S any] func() (S, error) 120 | 121 | // Instance delivers the concrete instance of type S, by executing 122 | // root ProviderMethod itself. 123 | // 124 | // It respects Binding interface. 125 | func (s ProviderMethod[S]) Instance(bool) (interface{}, error) { 126 | return s() 127 | } 128 | 129 | // AsProvider delivers a BindingSource for a type T, by defining a ProviderMethod 130 | // (or constructor method) for the new instance of some interface (or a struct). 131 | // 132 | // Example: 133 | // 134 | // err := genjector.Bind(genjector.AsProvider[ProviderInterface](func() (*ProviderStruct, error) { 135 | // return &ProviderStruct{ 136 | // value: "value provided inside the ProviderMethod", 137 | // }, nil 138 | // })) 139 | // 140 | // BindingSource can be only used as the first argument to Bind method. 141 | func AsProvider[T any, S any](provider ProviderMethod[S]) BindingSource[T] { 142 | return &bindingSource[T]{ 143 | binding: provider, 144 | keySource: baseKeySource[T]{}, 145 | } 146 | } 147 | 148 | // instanceBinding is a concrete implementation for Binding interface. 149 | type instanceBinding[S any] struct { 150 | instance S 151 | } 152 | 153 | // Instance delivers the concrete instance of type S, by returning already 154 | // initialized instance that instanceBinding holds. 155 | // 156 | // It respects Binding interface. 157 | func (s *instanceBinding[S]) Instance(bool) (interface{}, error) { 158 | return s.instance, nil 159 | } 160 | 161 | // AsInstance delivers a BindingSource for a type T, by using a concrete 162 | // instance that is passed as an argument to AsInstance method, to returns 163 | // that instance whenever it is required from Binding. 164 | // 165 | // Example: 166 | // 167 | // err := genjector.Bind(genjector.AsInstance[*InstanceStruct](&InstanceStruct{ 168 | // value: "value provided in concrete instance", 169 | // })) 170 | // 171 | // BindingSource can be only used as the first argument to Bind method. 172 | func AsInstance[T any, S any](instance S) BindingSource[T] { 173 | return &bindingSource[T]{ 174 | binding: &instanceBinding[S]{ 175 | instance: instance, 176 | }, 177 | keySource: baseKeySource[T]{}, 178 | } 179 | } 180 | 181 | // bindingOption is a concrete implementation for BindingOption interface. 182 | type bindingOption struct { 183 | bindingFunc func(binding Binding) (Binding, error) 184 | keyOption KeyOption 185 | } 186 | 187 | // Binding executes the inner bindingFunc method. 188 | // 189 | // It respects BindingOption interface. 190 | func (b *bindingOption) Binding(binding Binding) (Binding, error) { 191 | return b.bindingFunc(binding) 192 | } 193 | 194 | // Key executes the same method from inner KeyOption instance. 195 | // 196 | // It respects BindingOption interface. 197 | func (b *bindingOption) Key(key Key) Key { 198 | return b.keyOption.Key(key) 199 | } 200 | 201 | // Container executes the same method from inner KeyOption instance. 202 | // 203 | // It respects BindingOption interface. 204 | func (b *bindingOption) Container(container Container) Container { 205 | return b.keyOption.Container(container) 206 | } 207 | 208 | // singletonBinding is a concrete implementation for Binding interface. 209 | type singletonBinding struct { 210 | parent Binding 211 | singleton interface{} 212 | initialized bool 213 | } 214 | 215 | // Instance delivers already stored instance, which should be present if this 216 | // method was already executed before. Otherwise it retrieves the instance from 217 | // a child Binding and stores it internally for the next calls. 218 | // 219 | // It respects Binding interface. 220 | func (b *singletonBinding) Instance(initialize bool) (interface{}, error) { 221 | if b.initialized { 222 | return b.singleton, nil 223 | } 224 | 225 | instance, err := b.parent.Instance(initialize) 226 | if err != nil { 227 | return nil, err 228 | } 229 | 230 | b.initialized = true 231 | b.singleton = instance 232 | return instance, nil 233 | } 234 | 235 | // AsSingleton delivers a BindingOption that defines the instance of desired 236 | // Binding as a singleton. That means only first time the Init method (or ProviderMethod) 237 | // will be called, and every next time the same instance will be delivered 238 | // as a result of NewInstance method. 239 | // 240 | // Example: 241 | // err := genjector.Bind( 242 | // 243 | // genjector.AsPointer[SingletonInterface, *SingletonStruct](), 244 | // genjector.AsSingleton(), 245 | // 246 | // ) 247 | // 248 | // AsSingleton should be only used as a BindingOption for Bind method, as it 249 | // does not affect functionality if it is used in NewInstance method. 250 | func AsSingleton() BindingOption { 251 | return &bindingOption{ 252 | bindingFunc: func(binding Binding) (Binding, error) { 253 | return &singletonBinding{ 254 | parent: binding, 255 | }, nil 256 | }, 257 | keyOption: sameKeyOption{}, 258 | } 259 | } 260 | 261 | // WithAnnotation delivers a BindingOption that allows to name specific Binding 262 | // with any annotation desired. 263 | // 264 | // Example: 265 | // err = genjector.Bind( 266 | // 267 | // genjector.AsPointer[AnnotationInterface, *AnnotationStruct](), 268 | // genjector.WithAnnotation("first"), 269 | // 270 | // ) 271 | // 272 | // To properly use a customer Container, WithAnnotation should be used in both 273 | // Bind and NewInstance methods. 274 | func WithAnnotation(annotation string) BindingOption { 275 | return &bindingOption{ 276 | bindingFunc: func(binding Binding) (Binding, error) { 277 | return binding, nil 278 | }, 279 | keyOption: &annotatedKeyOption{ 280 | annotation: annotation, 281 | }, 282 | } 283 | } 284 | 285 | // WithContainer delivers a BindingOption that overrides the usage of standard 286 | // internal (global) Container. It allows to provide a fresh, a custom instance 287 | // of Container, that can be made from NewContainer method. 288 | // 289 | // Example: 290 | // err := genjector.Bind( 291 | // 292 | // genjector.AsPointer[ContainerInterface, *ContainerStruct](), 293 | // genjector.WithContainer(customContainer), 294 | // 295 | // ) 296 | // 297 | // To properly use a customer Container, WithContainer should be used in both 298 | // Bind and NewInstance methods. 299 | func WithContainer(container Container) BindingOption { 300 | return &bindingOption{ 301 | bindingFunc: func(binding Binding) (Binding, error) { 302 | return binding, nil 303 | }, 304 | keyOption: &containerKeyOption{ 305 | container: container, 306 | }, 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /binding_test.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | type testBinding struct { 10 | value string 11 | instance func(initialize bool) (interface{}, error) 12 | } 13 | 14 | func (b *testBinding) Instance(initialize bool) (interface{}, error) { 15 | return b.instance(initialize) 16 | } 17 | 18 | type testKeyOption struct { 19 | key func(key Key) Key 20 | container func(container Container) Container 21 | } 22 | 23 | func (o *testKeyOption) Key(key Key) Key { 24 | return o.key(key) 25 | } 26 | 27 | func (o *testKeyOption) Container(container Container) Container { 28 | return o.container(container) 29 | } 30 | 31 | type testStruct struct { 32 | a string 33 | b int 34 | } 35 | 36 | func (s *testStruct) Init() { 37 | s.a = "test" 38 | s.b = 10 39 | } 40 | 41 | var singleton = 1 42 | 43 | type testSingletonStruct struct{} 44 | 45 | func (s *testSingletonStruct) Init() { 46 | singleton++ 47 | } 48 | 49 | func Test_bindingSource_Binding_error(t *testing.T) { 50 | binding := &testBinding{} 51 | binding.instance = func(initialize bool) (interface{}, error) { 52 | return nil, errors.New("error") 53 | } 54 | 55 | source := &bindingSource[int]{ 56 | binding: binding, 57 | } 58 | 59 | result, err := source.Binding() 60 | if err == nil { 61 | t.Error("expected error, got nil") 62 | } 63 | 64 | if result != nil { 65 | t.Errorf(`expected nil, got: %v`, result) 66 | } 67 | } 68 | 69 | func Test_bindingSource_Binding_wrong(t *testing.T) { 70 | binding := &testBinding{} 71 | binding.instance = func(initialize bool) (interface{}, error) { 72 | return "1", nil 73 | } 74 | 75 | source := &bindingSource[int]{ 76 | binding: binding, 77 | } 78 | 79 | result, err := source.Binding() 80 | if err == nil { 81 | t.Error("expected error, got nil") 82 | } 83 | 84 | if result != nil { 85 | t.Errorf(`expected nil, got: %v`, result) 86 | } 87 | } 88 | 89 | func Test_bindingSource_Binding_success(t *testing.T) { 90 | binding := &testBinding{} 91 | binding.instance = func(initialize bool) (interface{}, error) { 92 | return 1, nil 93 | } 94 | 95 | source := &bindingSource[int]{ 96 | binding: binding, 97 | } 98 | 99 | result, err := source.Binding() 100 | if err != nil { 101 | t.Errorf(`expected nil, got: %v`, err) 102 | } 103 | 104 | if result == nil { 105 | t.Error("expected concrete value, got nil") 106 | } 107 | } 108 | 109 | func Test_bindingSource_Key(t *testing.T) { 110 | source := &bindingSource[int]{ 111 | keySource: baseKeySource[int]{}, 112 | } 113 | 114 | result := source.Key() 115 | expected := Key{ 116 | Value: (*int)(nil), 117 | } 118 | if !reflect.DeepEqual(result, expected) { 119 | t.Error("expected to get same keys") 120 | } 121 | } 122 | 123 | func Test_valueBinding_Instance_noInitialize(t *testing.T) { 124 | stringBinding := &valueBinding[string]{} 125 | result, err := stringBinding.Instance(false) 126 | if err != nil { 127 | t.Errorf(`expected nil, got: %v`, err) 128 | } 129 | if result != "" { 130 | t.Errorf(`expected empty string, got: %v`, result) 131 | } 132 | 133 | intBinding := &valueBinding[int]{} 134 | result, err = intBinding.Instance(false) 135 | if err != nil { 136 | t.Errorf(`expected nil, got: %v`, err) 137 | } 138 | if result != 0 { 139 | t.Errorf(`expected 0, got: %v`, result) 140 | } 141 | 142 | structBinding := &valueBinding[testStruct]{} 143 | result, err = structBinding.Instance(false) 144 | if err != nil { 145 | t.Errorf(`expected nil, got: %v`, err) 146 | } 147 | if !reflect.DeepEqual(result, testStruct{}) { 148 | t.Errorf(`expected empty struct, got: %v`, result) 149 | } 150 | } 151 | 152 | func Test_valueBinding_Instance_initialize(t *testing.T) { 153 | stringBinding := &valueBinding[string]{} 154 | result, err := stringBinding.Instance(true) 155 | if err != nil { 156 | t.Errorf(`expected nil, got: %v`, err) 157 | } 158 | if result != "" { 159 | t.Errorf(`expected empty string, got: %v`, result) 160 | } 161 | 162 | intBinding := &valueBinding[int]{} 163 | result, err = intBinding.Instance(true) 164 | if err != nil { 165 | t.Errorf(`expected nil, got: %v`, err) 166 | } 167 | if result != 0 { 168 | t.Errorf(`expected 0, got: %v`, result) 169 | } 170 | 171 | structBinding := &valueBinding[testStruct]{} 172 | result, err = structBinding.Instance(true) 173 | if err != nil { 174 | t.Errorf(`expected nil, got: %v`, err) 175 | } 176 | if !reflect.DeepEqual(result, testStruct{ 177 | a: "test", 178 | b: 10, 179 | }) { 180 | t.Errorf(`expected empty struct, got: %v`, result) 181 | } 182 | } 183 | 184 | func TestAsValue(t *testing.T) { 185 | result := AsValue[string, string]() 186 | if !reflect.DeepEqual(result, &bindingSource[string]{ 187 | binding: valueBinding[string]{}, 188 | keySource: baseKeySource[string]{}, 189 | }) { 190 | t.Error("binding sources are different") 191 | } 192 | 193 | result = AsValue[interface{}, testStruct]() 194 | if !reflect.DeepEqual(result, &bindingSource[interface{}]{ 195 | binding: valueBinding[testStruct]{}, 196 | keySource: baseKeySource[interface{}]{}, 197 | }) { 198 | t.Error("binding sources are different") 199 | } 200 | } 201 | 202 | func Test_pointerBinding_Instance_noInitialize(t *testing.T) { 203 | stringBinding := &pointerBinding[string]{} 204 | result, err := stringBinding.Instance(false) 205 | if err != nil { 206 | t.Errorf(`expected nil, got: %v`, err) 207 | } 208 | stringActual := result.(*string) 209 | if *stringActual != "" { 210 | t.Errorf(`expected pointer to empty string, got: %v`, result) 211 | } 212 | 213 | intBinding := &pointerBinding[int]{} 214 | result, err = intBinding.Instance(false) 215 | if err != nil { 216 | t.Errorf(`expected nil, got: %v`, err) 217 | } 218 | intActual := result.(*int) 219 | if *intActual != 0 { 220 | t.Errorf(`expected pointer to 0, got: %v`, result) 221 | } 222 | 223 | structBinding := &pointerBinding[testStruct]{} 224 | result, err = structBinding.Instance(false) 225 | if err != nil { 226 | t.Errorf(`expected nil, got: %v`, err) 227 | } 228 | if !reflect.DeepEqual(result, &testStruct{}) { 229 | t.Errorf(`expected pointer to empty struct, got: %v`, result) 230 | } 231 | } 232 | 233 | func Test_pointerBinding_Instance_initialize(t *testing.T) { 234 | stringBinding := &pointerBinding[string]{} 235 | result, err := stringBinding.Instance(true) 236 | if err != nil { 237 | t.Errorf(`expected nil, got: %v`, err) 238 | } 239 | stringActual := result.(*string) 240 | if *stringActual != "" { 241 | t.Errorf(`expected pointer to empty string, got: %v`, result) 242 | } 243 | 244 | intBinding := &pointerBinding[int]{} 245 | result, err = intBinding.Instance(true) 246 | if err != nil { 247 | t.Errorf(`expected nil, got: %v`, err) 248 | } 249 | intActual := result.(*int) 250 | if *intActual != 0 { 251 | t.Errorf(`expected pointer to 0, got: %v`, result) 252 | } 253 | 254 | structBinding := &pointerBinding[testStruct]{} 255 | result, err = structBinding.Instance(true) 256 | if err != nil { 257 | t.Errorf(`expected nil, got: %v`, err) 258 | } 259 | if !reflect.DeepEqual(result, &testStruct{ 260 | a: "test", 261 | b: 10, 262 | }) { 263 | t.Errorf(`expected pointer to empty struct, got: %v`, result) 264 | } 265 | } 266 | 267 | func TestAsPointer(t *testing.T) { 268 | result := AsPointer[*string, *string]() 269 | if !reflect.DeepEqual(result, &bindingSource[*string]{ 270 | binding: pointerBinding[string]{}, 271 | keySource: baseKeySource[*string]{}, 272 | }) { 273 | t.Error("binding sources are different") 274 | } 275 | 276 | result = AsPointer[interface{}, *testStruct]() 277 | if !reflect.DeepEqual(result, &bindingSource[interface{}]{ 278 | binding: pointerBinding[testStruct]{}, 279 | keySource: baseKeySource[interface{}]{}, 280 | }) { 281 | t.Error("binding sources are different") 282 | } 283 | } 284 | 285 | func Test_ProviderMethod_Instance(t *testing.T) { 286 | var stringBinding ProviderMethod[string] = func() (string, error) { 287 | return "value", nil 288 | } 289 | result, err := stringBinding.Instance(true) 290 | if err != nil { 291 | t.Errorf(`expected nil, got: %v`, err) 292 | } 293 | if result != "value" { 294 | t.Errorf(`expected pointer to empty string, got: %v`, result) 295 | } 296 | 297 | var intBinding ProviderMethod[*int] = func() (*int, error) { 298 | value := 10 299 | return &value, nil 300 | } 301 | result, err = intBinding.Instance(true) 302 | if err != nil { 303 | t.Errorf(`expected nil, got: %v`, err) 304 | } 305 | intActual := result.(*int) 306 | if *intActual != 10 { 307 | t.Errorf(`expected pointer to 0, got: %v`, result) 308 | } 309 | 310 | var structBinding ProviderMethod[*testStruct] = func() (*testStruct, error) { 311 | return &testStruct{ 312 | a: "a", 313 | b: 5, 314 | }, nil 315 | } 316 | result, err = structBinding.Instance(true) 317 | if err != nil { 318 | t.Errorf(`expected nil, got: %v`, err) 319 | } 320 | if !reflect.DeepEqual(result, &testStruct{ 321 | a: "a", 322 | b: 5, 323 | }) { 324 | t.Errorf(`expected pointer to empty struct, got: %v`, result) 325 | } 326 | } 327 | 328 | func TestAsProvider(t *testing.T) { 329 | result := AsProvider[*testStruct](func() (*testStruct, error) { 330 | return &testStruct{ 331 | a: "a", 332 | b: 5, 333 | }, nil 334 | }) 335 | binding, err := result.Binding() 336 | if err != nil { 337 | t.Errorf(`expected nil, got: %v`, err) 338 | } 339 | 340 | instance, err := binding.Instance(true) 341 | if err != nil { 342 | t.Errorf(`expected nil, got: %v`, err) 343 | } 344 | 345 | if !reflect.DeepEqual(instance, &testStruct{ 346 | a: "a", 347 | b: 5, 348 | }) { 349 | t.Error("instances are different") 350 | } 351 | } 352 | 353 | func Test_instanceBinding_Instance(t *testing.T) { 354 | stringBinding := &instanceBinding[string]{ 355 | instance: "value", 356 | } 357 | result, err := stringBinding.Instance(true) 358 | if err != nil { 359 | t.Errorf(`expected nil, got: %v`, err) 360 | } 361 | if result != "value" { 362 | t.Errorf(`expected pointer to empty string, got: %v`, result) 363 | } 364 | 365 | intBinding := &instanceBinding[int]{ 366 | instance: 10, 367 | } 368 | result, err = intBinding.Instance(true) 369 | if err != nil { 370 | t.Errorf(`expected nil, got: %v`, err) 371 | } 372 | if result != 10 { 373 | t.Errorf(`expected pointer to 0, got: %v`, result) 374 | } 375 | 376 | structBinding := &instanceBinding[*testStruct]{ 377 | instance: &testStruct{ 378 | a: "a", 379 | b: 5, 380 | }, 381 | } 382 | result, err = structBinding.Instance(true) 383 | if err != nil { 384 | t.Errorf(`expected nil, got: %v`, err) 385 | } 386 | if !reflect.DeepEqual(result, &testStruct{ 387 | a: "a", 388 | b: 5, 389 | }) { 390 | t.Errorf(`expected pointer to empty struct, got: %v`, result) 391 | } 392 | } 393 | 394 | func TestAsInstance(t *testing.T) { 395 | result := AsInstance[string]("value") 396 | if !reflect.DeepEqual(result, &bindingSource[string]{ 397 | binding: &instanceBinding[string]{ 398 | instance: "value", 399 | }, 400 | keySource: baseKeySource[string]{}, 401 | }) { 402 | t.Error("binding sources are different") 403 | } 404 | 405 | result = AsInstance[interface{}](&testStruct{ 406 | a: "test", 407 | }) 408 | if !reflect.DeepEqual(result, &bindingSource[interface{}]{ 409 | binding: &instanceBinding[*testStruct]{ 410 | instance: &testStruct{ 411 | a: "test", 412 | }, 413 | }, 414 | keySource: baseKeySource[interface{}]{}, 415 | }) { 416 | t.Error("binding sources are different") 417 | } 418 | } 419 | 420 | func Test_bindingOption_Binding(t *testing.T) { 421 | option := &bindingOption{ 422 | bindingFunc: func(binding Binding) (Binding, error) { 423 | return &instanceBinding[string]{ 424 | instance: "value", 425 | }, nil 426 | }, 427 | } 428 | 429 | binding, err := option.Binding(nil) 430 | if err != nil { 431 | t.Errorf(`expected nil, got: %v`, err) 432 | } 433 | if !reflect.DeepEqual(binding, &instanceBinding[string]{ 434 | instance: "value", 435 | }) { 436 | t.Error("bindings are different") 437 | } 438 | } 439 | 440 | func Test_bindingOption_Container(t *testing.T) { 441 | option := &bindingOption{ 442 | keyOption: &testKeyOption{ 443 | container: func(container Container) Container { 444 | return Container{ 445 | "something": nil, 446 | } 447 | }, 448 | }, 449 | } 450 | 451 | container := option.Container(nil) 452 | if !reflect.DeepEqual(container, Container{ 453 | "something": nil, 454 | }) { 455 | t.Error("containers are different") 456 | } 457 | } 458 | 459 | func Test_bindingOption_Key(t *testing.T) { 460 | option := &bindingOption{ 461 | keyOption: &testKeyOption{ 462 | key: func(key Key) Key { 463 | return Key{ 464 | Annotation: "annotation", 465 | } 466 | }, 467 | }, 468 | } 469 | 470 | key := option.Key(Key{}) 471 | if !reflect.DeepEqual(key, Key{ 472 | Annotation: "annotation", 473 | }) { 474 | t.Error("containers are different") 475 | } 476 | } 477 | 478 | func Test_singletonBinding_Instance(t *testing.T) { 479 | option := &bindingOption{ 480 | bindingFunc: func(binding Binding) (Binding, error) { 481 | return &singletonBinding{ 482 | parent: binding, 483 | }, nil 484 | }, 485 | } 486 | 487 | binding, err := option.Binding(&pointerBinding[testSingletonStruct]{}) 488 | if err != nil { 489 | t.Error("unexpected error") 490 | } 491 | 492 | instance, err := binding.Instance(true) 493 | if err != nil { 494 | t.Error("unexpected error") 495 | } 496 | if !reflect.DeepEqual(instance, &testSingletonStruct{}) { 497 | t.Error("instance are different") 498 | } 499 | if singleton != 2 { 500 | t.Errorf(`expected value 2, go: %d`, singleton) 501 | } 502 | 503 | instance, err = binding.Instance(true) 504 | if err != nil { 505 | t.Error("unexpected error") 506 | } 507 | if !reflect.DeepEqual(instance, &testSingletonStruct{}) { 508 | t.Error("instance are different") 509 | } 510 | if singleton != 2 { 511 | t.Errorf(`expected value 2, go: %d`, singleton) 512 | } 513 | } 514 | 515 | func TestAsSingleton(t *testing.T) { 516 | result := AsSingleton() 517 | 518 | binding, err := result.(*bindingOption).bindingFunc(&valueBinding[int]{}) 519 | if err != nil { 520 | t.Error("unexpected error") 521 | } 522 | if !reflect.DeepEqual(binding, &singletonBinding{ 523 | parent: &valueBinding[int]{}, 524 | }) { 525 | t.Error("bindings are different") 526 | } 527 | 528 | result.(*bindingOption).bindingFunc = nil 529 | 530 | if !reflect.DeepEqual(result, &bindingOption{ 531 | keyOption: sameKeyOption{}, 532 | }) { 533 | t.Error("binding options are different") 534 | } 535 | } 536 | 537 | func TestWithAnnotation(t *testing.T) { 538 | result := WithAnnotation("annotation") 539 | result.(*bindingOption).bindingFunc = nil 540 | if !reflect.DeepEqual(result, &bindingOption{ 541 | keyOption: &annotatedKeyOption{ 542 | annotation: "annotation", 543 | }, 544 | }) { 545 | t.Error("binding options are different") 546 | } 547 | } 548 | 549 | func TestWithContainer(t *testing.T) { 550 | result := WithContainer(Container{ 551 | "first": nil, 552 | }) 553 | if !reflect.DeepEqual(&containerKeyOption{ 554 | container: Container{ 555 | "first": nil, 556 | }, 557 | }, result.(*bindingOption).keyOption) { 558 | t.Error("containerKeyOption does not contain the right value") 559 | } 560 | 561 | result = WithContainer(nil) 562 | if !reflect.DeepEqual(new(containerKeyOption), result.(*bindingOption).keyOption) { 563 | t.Error("containerKeyOption does not contain the right value") 564 | } 565 | } 566 | -------------------------------------------------------------------------------- /chain_test.go: -------------------------------------------------------------------------------- 1 | package genjector 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | type testBindingSource struct { 10 | binding func() (Binding, error) 11 | key func() Key 12 | } 13 | 14 | func (o *testBindingSource) Binding() (Binding, error) { 15 | return o.binding() 16 | } 17 | 18 | func (o *testBindingSource) Key() Key { 19 | return o.key() 20 | } 21 | 22 | type testBindingOption struct { 23 | binding func(binding Binding) (Binding, error) 24 | key func(key Key) Key 25 | container func(container Container) Container 26 | } 27 | 28 | func (o *testBindingOption) Binding(binding Binding) (Binding, error) { 29 | return o.binding(binding) 30 | } 31 | 32 | func (o *testBindingOption) Key(key Key) Key { 33 | return o.key(key) 34 | } 35 | 36 | func (o *testBindingOption) Container(container Container) Container { 37 | return o.container(container) 38 | } 39 | 40 | func Test_sliceBinding_firstItem_error(t *testing.T) { 41 | binding := &sliceBinding[testStruct]{ 42 | current: &testBinding{ 43 | instance: func(initialize bool) (interface{}, error) { 44 | return nil, errors.New("error") 45 | }, 46 | }, 47 | } 48 | 49 | instance, err := binding.Instance(true) 50 | if err == nil { 51 | t.Error("expected error, got nil") 52 | } 53 | 54 | if instance != nil { 55 | t.Errorf(`expected nil, go %v`, instance) 56 | } 57 | } 58 | 59 | func Test_sliceBinding_firstItem_invalid(t *testing.T) { 60 | binding := &sliceBinding[testStruct]{ 61 | current: &testBinding{ 62 | instance: func(initialize bool) (interface{}, error) { 63 | return "value", nil 64 | }, 65 | }, 66 | } 67 | 68 | instance, err := binding.Instance(true) 69 | if err == nil { 70 | t.Error("expected error, got nil") 71 | } 72 | 73 | if instance != nil { 74 | t.Errorf(`expected nil, go %v`, instance) 75 | } 76 | } 77 | 78 | func Test_sliceBinding_firstItem_success(t *testing.T) { 79 | binding := &sliceBinding[testStruct]{ 80 | current: &testBinding{ 81 | instance: func(initialize bool) (interface{}, error) { 82 | return testStruct{ 83 | a: "value", 84 | b: 20, 85 | }, nil 86 | }, 87 | }, 88 | } 89 | 90 | instance, err := binding.Instance(true) 91 | if err != nil { 92 | t.Errorf("expected nil, got error %s", err) 93 | } 94 | 95 | if !reflect.DeepEqual(instance, []testStruct{ 96 | { 97 | a: "value", 98 | b: 20, 99 | }, 100 | }) { 101 | t.Error("expected instance to match concrete value") 102 | } 103 | } 104 | 105 | func Test_sliceBinding_slice_error(t *testing.T) { 106 | binding := &sliceBinding[testStruct]{ 107 | previous: &sliceBinding[testStruct]{ 108 | current: &testBinding{ 109 | instance: func(initialize bool) (interface{}, error) { 110 | return nil, errors.New("error") 111 | }, 112 | }, 113 | }, 114 | current: &testBinding{ 115 | instance: func(initialize bool) (interface{}, error) { 116 | return testStruct{ 117 | a: "value", 118 | b: 20, 119 | }, nil 120 | }, 121 | }, 122 | } 123 | 124 | instance, err := binding.Instance(true) 125 | if err == nil { 126 | t.Error("expected error, got nil") 127 | } 128 | 129 | if instance != nil { 130 | t.Errorf(`expected nil, go %v`, instance) 131 | } 132 | } 133 | 134 | func Test_sliceBinding_slice_invalid(t *testing.T) { 135 | binding := &sliceBinding[testStruct]{ 136 | previous: &sliceBinding[testStruct]{ 137 | current: &testBinding{ 138 | instance: func(initialize bool) (interface{}, error) { 139 | return "value", nil 140 | }, 141 | }, 142 | }, 143 | current: &testBinding{ 144 | instance: func(initialize bool) (interface{}, error) { 145 | return testStruct{ 146 | a: "value", 147 | b: 20, 148 | }, nil 149 | }, 150 | }, 151 | } 152 | 153 | instance, err := binding.Instance(true) 154 | if err == nil { 155 | t.Error("expected error, got nil") 156 | } 157 | 158 | if instance != nil { 159 | t.Errorf(`expected nil, go %v`, instance) 160 | } 161 | } 162 | 163 | func Test_sliceBinding_slice_success(t *testing.T) { 164 | binding := &sliceBinding[testStruct]{ 165 | previous: &sliceBinding[testStruct]{ 166 | current: &testBinding{ 167 | instance: func(initialize bool) (interface{}, error) { 168 | return testStruct{ 169 | a: "first", 170 | b: 5, 171 | }, nil 172 | }, 173 | }, 174 | }, 175 | current: &testBinding{ 176 | instance: func(initialize bool) (interface{}, error) { 177 | return testStruct{ 178 | a: "value", 179 | b: 20, 180 | }, nil 181 | }, 182 | }, 183 | } 184 | 185 | instance, err := binding.Instance(true) 186 | if err != nil { 187 | t.Errorf("expected nil, got error %s", err) 188 | } 189 | 190 | if !reflect.DeepEqual(instance, []testStruct{ 191 | { 192 | a: "first", 193 | b: 5, 194 | }, 195 | { 196 | a: "value", 197 | b: 20, 198 | }, 199 | }) { 200 | t.Error("expected instance to match concrete value") 201 | } 202 | } 203 | 204 | func Test_sliceBinding_slice_success_noInitialize(t *testing.T) { 205 | binding := &sliceBinding[testStruct]{ 206 | previous: &sliceBinding[testStruct]{ 207 | current: &testBinding{ 208 | instance: func(initialize bool) (interface{}, error) { 209 | return testStruct{ 210 | a: "first", 211 | b: 5, 212 | }, nil 213 | }, 214 | }, 215 | }, 216 | current: &testBinding{ 217 | instance: func(initialize bool) (interface{}, error) { 218 | return testStruct{ 219 | a: "value", 220 | b: 20, 221 | }, nil 222 | }, 223 | }, 224 | } 225 | 226 | instance, err := binding.Instance(false) 227 | if err != nil { 228 | t.Errorf("expected nil, got error %s", err) 229 | } 230 | 231 | if !reflect.DeepEqual(instance, []testStruct{ 232 | { 233 | a: "value", 234 | b: 20, 235 | }, 236 | }) { 237 | t.Error("expected instance to match concrete value") 238 | } 239 | } 240 | 241 | func Test_sliceBindingSource_Binding_error(t *testing.T) { 242 | source := &sliceBindingSource[testStruct]{ 243 | source: &testBindingSource{ 244 | binding: func() (Binding, error) { 245 | return nil, errors.New("error") 246 | }, 247 | }, 248 | } 249 | 250 | binding, err := source.Binding() 251 | if err == nil { 252 | t.Error("expected error, got nil") 253 | } 254 | 255 | if binding != nil { 256 | t.Errorf(`expected nil, go %v`, binding) 257 | } 258 | } 259 | 260 | func Test_sliceBindingSource_Binding_instanceError(t *testing.T) { 261 | source := &sliceBindingSource[testStruct]{ 262 | source: &testBindingSource{ 263 | binding: func() (Binding, error) { 264 | return &testBinding{ 265 | instance: func(initialize bool) (interface{}, error) { 266 | return nil, errors.New("error") 267 | }, 268 | }, nil 269 | }, 270 | }, 271 | } 272 | 273 | binding, err := source.Binding() 274 | if err == nil { 275 | t.Error("expected error, got nil") 276 | } 277 | 278 | if binding != nil { 279 | t.Errorf(`expected nil, go %v`, binding) 280 | } 281 | } 282 | 283 | func Test_sliceBindingSource_Binding_invalid(t *testing.T) { 284 | source := &sliceBindingSource[testStruct]{ 285 | source: &testBindingSource{ 286 | binding: func() (Binding, error) { 287 | return &testBinding{ 288 | instance: func(initialize bool) (interface{}, error) { 289 | return "value", nil 290 | }, 291 | }, nil 292 | }, 293 | }, 294 | } 295 | 296 | binding, err := source.Binding() 297 | if err == nil { 298 | t.Error("expected error, got nil") 299 | } 300 | 301 | if binding != nil { 302 | t.Errorf(`expected nil, go %v`, binding) 303 | } 304 | } 305 | 306 | func Test_sliceBindingSource_Binding_current_success(t *testing.T) { 307 | source := &sliceBindingSource[testStruct]{ 308 | source: &testBindingSource{ 309 | binding: func() (Binding, error) { 310 | return &testBinding{ 311 | instance: func(initialize bool) (interface{}, error) { 312 | return testStruct{ 313 | a: "value", 314 | b: 20, 315 | }, nil 316 | }, 317 | }, nil 318 | }, 319 | }, 320 | } 321 | 322 | binding, err := source.Binding() 323 | if err != nil { 324 | t.Errorf("expected nil, got error %s", err) 325 | } 326 | 327 | instance, err := binding.Instance(true) 328 | if err != nil { 329 | t.Errorf("expected nil, got error %s", err) 330 | } 331 | 332 | if !reflect.DeepEqual(instance, []testStruct{ 333 | { 334 | a: "value", 335 | b: 20, 336 | }, 337 | }) { 338 | t.Error("expected instance to match concrete value") 339 | } 340 | } 341 | 342 | func Test_sliceBindingSource_Binding_previous_success(t *testing.T) { 343 | source := &sliceBindingSource[testStruct]{ 344 | source: &testBindingSource{ 345 | binding: func() (Binding, error) { 346 | return &testBinding{ 347 | instance: func(initialize bool) (interface{}, error) { 348 | return testStruct{ 349 | a: "value", 350 | b: 20, 351 | }, nil 352 | }, 353 | }, nil 354 | }, 355 | }, 356 | previous: &sliceBinding[testStruct]{ 357 | current: &testBinding{ 358 | instance: func(initialize bool) (interface{}, error) { 359 | return testStruct{ 360 | a: "first", 361 | b: 5, 362 | }, nil 363 | }, 364 | }, 365 | }, 366 | } 367 | 368 | binding, err := source.Binding() 369 | if err != nil { 370 | t.Errorf("expected nil, got error %s", err) 371 | } 372 | 373 | instance, err := binding.Instance(true) 374 | if err != nil { 375 | t.Errorf("expected nil, got error %s", err) 376 | } 377 | 378 | if !reflect.DeepEqual(instance, []testStruct{ 379 | { 380 | a: "first", 381 | b: 5, 382 | }, 383 | { 384 | a: "value", 385 | b: 20, 386 | }, 387 | }) { 388 | t.Error("expected instance to match concrete value") 389 | } 390 | } 391 | 392 | func Test_sliceBindingSource_SetPrevious(t *testing.T) { 393 | source := &sliceBindingSource[int]{} 394 | source.SetPrevious(&testBinding{}) 395 | 396 | if !reflect.DeepEqual(source, &sliceBindingSource[int]{ 397 | previous: &testBinding{}, 398 | }) { 399 | t.Error("expected source to match concrete value") 400 | } 401 | } 402 | 403 | func Test_sliceBindingSource_Key(t *testing.T) { 404 | source := &sliceBindingSource[int]{ 405 | keySource: sliceKeySource[int]{}, 406 | } 407 | key := source.Key() 408 | 409 | if !reflect.DeepEqual(key, Key{ 410 | Value: (*[]int)(nil), 411 | }) { 412 | t.Error("expected source to match concrete value") 413 | } 414 | } 415 | 416 | func TestInSlice(t *testing.T) { 417 | source := InSlice[string](&testBindingSource{}) 418 | 419 | if !reflect.DeepEqual(source, &sliceBindingSource[string]{ 420 | source: &testBindingSource{}, 421 | keySource: sliceKeySource[string]{}, 422 | }) { 423 | t.Error("expected source to match concrete value") 424 | } 425 | } 426 | 427 | func Test_mapBinding_firstItem_error(t *testing.T) { 428 | binding := &mapBinding[string, testStruct]{ 429 | current: &testBinding{ 430 | instance: func(initialize bool) (interface{}, error) { 431 | return nil, errors.New("error") 432 | }, 433 | }, 434 | } 435 | 436 | instance, err := binding.Instance(true) 437 | if err == nil { 438 | t.Error("expected error, got nil") 439 | } 440 | 441 | if instance != nil { 442 | t.Errorf(`expected nil, go %v`, instance) 443 | } 444 | } 445 | 446 | func Test_mapBinding_firstItem_invalid(t *testing.T) { 447 | binding := &mapBinding[string, testStruct]{ 448 | current: &testBinding{ 449 | instance: func(initialize bool) (interface{}, error) { 450 | return "value", nil 451 | }, 452 | }, 453 | } 454 | 455 | instance, err := binding.Instance(true) 456 | if err == nil { 457 | t.Error("expected error, got nil") 458 | } 459 | 460 | if instance != nil { 461 | t.Errorf(`expected nil, go %v`, instance) 462 | } 463 | } 464 | 465 | func Test_mapBinding_firstItem_success(t *testing.T) { 466 | binding := &mapBinding[string, testStruct]{ 467 | key: "value", 468 | current: &testBinding{ 469 | instance: func(initialize bool) (interface{}, error) { 470 | return testStruct{ 471 | a: "value", 472 | b: 20, 473 | }, nil 474 | }, 475 | }, 476 | } 477 | 478 | instance, err := binding.Instance(true) 479 | if err != nil { 480 | t.Errorf("expected nil, got error %s", err) 481 | } 482 | 483 | if !reflect.DeepEqual(instance, map[string]testStruct{ 484 | "value": { 485 | a: "value", 486 | b: 20, 487 | }, 488 | }) { 489 | t.Error("expected instance to match concrete value") 490 | } 491 | } 492 | 493 | func Test_mapBinding_map_error(t *testing.T) { 494 | binding := &mapBinding[string, testStruct]{ 495 | previous: &mapBinding[string, testStruct]{ 496 | current: &testBinding{ 497 | instance: func(initialize bool) (interface{}, error) { 498 | return nil, errors.New("error") 499 | }, 500 | }, 501 | }, 502 | current: &testBinding{ 503 | instance: func(initialize bool) (interface{}, error) { 504 | return testStruct{ 505 | a: "value", 506 | b: 20, 507 | }, nil 508 | }, 509 | }, 510 | } 511 | 512 | instance, err := binding.Instance(true) 513 | if err == nil { 514 | t.Error("expected error, got nil") 515 | } 516 | 517 | if instance != nil { 518 | t.Errorf(`expected nil, go %v`, instance) 519 | } 520 | } 521 | 522 | func Test_mapBinding_map_invalid(t *testing.T) { 523 | binding := &mapBinding[string, testStruct]{ 524 | previous: &mapBinding[string, testStruct]{ 525 | current: &testBinding{ 526 | instance: func(initialize bool) (interface{}, error) { 527 | return "value", nil 528 | }, 529 | }, 530 | }, 531 | current: &testBinding{ 532 | instance: func(initialize bool) (interface{}, error) { 533 | return testStruct{ 534 | a: "value", 535 | b: 20, 536 | }, nil 537 | }, 538 | }, 539 | } 540 | 541 | instance, err := binding.Instance(true) 542 | if err == nil { 543 | t.Error("expected error, got nil") 544 | } 545 | 546 | if instance != nil { 547 | t.Errorf(`expected nil, go %v`, instance) 548 | } 549 | } 550 | 551 | func Test_mapBinding_map_success(t *testing.T) { 552 | binding := &mapBinding[string, testStruct]{ 553 | previous: &mapBinding[string, testStruct]{ 554 | key: "first", 555 | current: &testBinding{ 556 | instance: func(initialize bool) (interface{}, error) { 557 | return testStruct{ 558 | a: "first", 559 | b: 5, 560 | }, nil 561 | }, 562 | }, 563 | }, 564 | key: "value", 565 | current: &testBinding{ 566 | instance: func(initialize bool) (interface{}, error) { 567 | return testStruct{ 568 | a: "value", 569 | b: 20, 570 | }, nil 571 | }, 572 | }, 573 | } 574 | 575 | instance, err := binding.Instance(true) 576 | if err != nil { 577 | t.Errorf("expected nil, got error %s", err) 578 | } 579 | 580 | if !reflect.DeepEqual(instance, map[string]testStruct{ 581 | "first": { 582 | a: "first", 583 | b: 5, 584 | }, 585 | "value": { 586 | a: "value", 587 | b: 20, 588 | }, 589 | }) { 590 | t.Error("expected instance to match concrete value") 591 | } 592 | } 593 | 594 | func Test_mapBinding_map_success_noInitialize(t *testing.T) { 595 | binding := &mapBinding[string, testStruct]{ 596 | previous: &mapBinding[string, testStruct]{ 597 | key: "first", 598 | current: &testBinding{ 599 | instance: func(initialize bool) (interface{}, error) { 600 | return testStruct{ 601 | a: "first", 602 | b: 5, 603 | }, nil 604 | }, 605 | }, 606 | }, 607 | key: "value", 608 | current: &testBinding{ 609 | instance: func(initialize bool) (interface{}, error) { 610 | return testStruct{ 611 | a: "value", 612 | b: 20, 613 | }, nil 614 | }, 615 | }, 616 | } 617 | 618 | instance, err := binding.Instance(false) 619 | if err != nil { 620 | t.Errorf("expected nil, got error %s", err) 621 | } 622 | 623 | if !reflect.DeepEqual(instance, map[string]testStruct{ 624 | "value": { 625 | a: "value", 626 | b: 20, 627 | }, 628 | }) { 629 | t.Error("expected instance to match concrete value") 630 | } 631 | } 632 | 633 | func Test_mapBindingSource_Binding_error(t *testing.T) { 634 | source := &mapBindingSource[string, testStruct]{ 635 | source: &testBindingSource{ 636 | binding: func() (Binding, error) { 637 | return nil, errors.New("error") 638 | }, 639 | }, 640 | } 641 | 642 | binding, err := source.Binding() 643 | if err == nil { 644 | t.Error("expected error, got nil") 645 | } 646 | 647 | if binding != nil { 648 | t.Errorf(`expected nil, go %v`, binding) 649 | } 650 | } 651 | 652 | func Test_mapBindingSource_Binding_instanceError(t *testing.T) { 653 | source := &mapBindingSource[string, testStruct]{ 654 | source: &testBindingSource{ 655 | binding: func() (Binding, error) { 656 | return &testBinding{ 657 | instance: func(initialize bool) (interface{}, error) { 658 | return nil, errors.New("error") 659 | }, 660 | }, nil 661 | }, 662 | }, 663 | } 664 | 665 | binding, err := source.Binding() 666 | if err == nil { 667 | t.Error("expected error, got nil") 668 | } 669 | 670 | if binding != nil { 671 | t.Errorf(`expected nil, go %v`, binding) 672 | } 673 | } 674 | 675 | func Test_mapBindingSource_Binding_invalid(t *testing.T) { 676 | source := &mapBindingSource[string, testStruct]{ 677 | source: &testBindingSource{ 678 | binding: func() (Binding, error) { 679 | return &testBinding{ 680 | instance: func(initialize bool) (interface{}, error) { 681 | return "value", nil 682 | }, 683 | }, nil 684 | }, 685 | }, 686 | } 687 | 688 | binding, err := source.Binding() 689 | if err == nil { 690 | t.Error("expected error, got nil") 691 | } 692 | 693 | if binding != nil { 694 | t.Errorf(`expected nil, go %v`, binding) 695 | } 696 | } 697 | 698 | func Test_mapBindingSource_Binding_current_success(t *testing.T) { 699 | source := &mapBindingSource[string, testStruct]{ 700 | key: "value", 701 | source: &testBindingSource{ 702 | binding: func() (Binding, error) { 703 | return &testBinding{ 704 | instance: func(initialize bool) (interface{}, error) { 705 | return testStruct{ 706 | a: "value", 707 | b: 20, 708 | }, nil 709 | }, 710 | }, nil 711 | }, 712 | }, 713 | } 714 | 715 | binding, err := source.Binding() 716 | if err != nil { 717 | t.Errorf("expected nil, got error %s", err) 718 | } 719 | 720 | instance, err := binding.Instance(true) 721 | if err != nil { 722 | t.Errorf("expected nil, got error %s", err) 723 | } 724 | 725 | if !reflect.DeepEqual(instance, map[string]testStruct{ 726 | "value": { 727 | a: "value", 728 | b: 20, 729 | }, 730 | }) { 731 | t.Error("expected instance to match concrete value") 732 | } 733 | } 734 | 735 | func Test_mapBindingSource_Binding_previous_success(t *testing.T) { 736 | source := &mapBindingSource[string, testStruct]{ 737 | key: "value", 738 | source: &testBindingSource{ 739 | binding: func() (Binding, error) { 740 | return &testBinding{ 741 | instance: func(initialize bool) (interface{}, error) { 742 | return testStruct{ 743 | a: "value", 744 | b: 20, 745 | }, nil 746 | }, 747 | }, nil 748 | }, 749 | }, 750 | previous: &mapBinding[string, testStruct]{ 751 | key: "first", 752 | current: &testBinding{ 753 | instance: func(initialize bool) (interface{}, error) { 754 | return testStruct{ 755 | a: "first", 756 | b: 5, 757 | }, nil 758 | }, 759 | }, 760 | }, 761 | } 762 | 763 | binding, err := source.Binding() 764 | if err != nil { 765 | t.Errorf("expected nil, got error %s", err) 766 | } 767 | 768 | instance, err := binding.Instance(true) 769 | if err != nil { 770 | t.Errorf("expected nil, got error %s", err) 771 | } 772 | 773 | if !reflect.DeepEqual(instance, map[string]testStruct{ 774 | "first": { 775 | a: "first", 776 | b: 5, 777 | }, 778 | "value": { 779 | a: "value", 780 | b: 20, 781 | }, 782 | }) { 783 | t.Error("expected instance to match concrete value") 784 | } 785 | } 786 | 787 | func Test_mapBindingSource_SetPrevious(t *testing.T) { 788 | source := &mapBindingSource[string, int]{} 789 | source.SetPrevious(&testBinding{}) 790 | 791 | if !reflect.DeepEqual(source, &mapBindingSource[string, int]{ 792 | previous: &testBinding{}, 793 | }) { 794 | t.Error("expected source to match concrete value") 795 | } 796 | } 797 | 798 | func Test_mapBindingSource_Key(t *testing.T) { 799 | source := &mapBindingSource[string, int]{ 800 | keySource: mapKeySource[string, int]{}, 801 | } 802 | key := source.Key() 803 | 804 | if !reflect.DeepEqual(key, Key{ 805 | Value: (*map[string]int)(nil), 806 | }) { 807 | t.Error("expected source to match concrete value") 808 | } 809 | } 810 | 811 | func TestInMap(t *testing.T) { 812 | source := InMap[string, int]("key", &testBindingSource{}) 813 | 814 | if !reflect.DeepEqual(source, &mapBindingSource[string, int]{ 815 | source: &testBindingSource{}, 816 | key: "key", 817 | keySource: mapKeySource[string, int]{}, 818 | }) { 819 | t.Error("expected source to match concrete value") 820 | } 821 | } 822 | --------------------------------------------------------------------------------