Skip to content

Commit a6edcbe

Browse files
Clean up
- Use lower snake_case file names to follow go design... - Move parts of the container instance handling to their own files, that ~700 line file just doesn't cut it for me - Move tests into ./tests to keep things cleaner/tests more specific(due to our file naming) - Test files for different parts of container logic - Rename package to "container" - Rename "IocContainer" struct to "ContainerInstance"... need better name still... wanted to use "Container", but the global container is named "Container". - Renamed "IocContainerConfig" to "ContainerConfig" - Renamed "IocContainerBinding" to "Binding"
1 parent 9b7bfd6 commit a6edcbe

19 files changed

+1337
-1306
lines changed

Container.go

-762
This file was deleted.

Container_test.go

-517
This file was deleted.

ContainerBinding.go binding.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package Container
1+
package container
22

33
import "reflect"
44

5-
type IocContainerBinding struct {
5+
type Binding struct {
66
// Function, Concrete, Abstract
77
// Function is a function we call to get a resolved value
88
// Concrete is resolvable by passing a type of the concrete

container_binding_creators.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package container
2+
3+
import (
4+
"log"
5+
"reflect"
6+
)
7+
8+
// addFunctionBinding - Create a new container binding from the function
9+
// This resolves the return type of the function as the Abstract
10+
// And the functions return value is our Concrete
11+
func (container *ContainerInstance) addFunctionBinding(definition reflect.Type, resolver any) {
12+
numOut := definition.NumOut()
13+
if numOut == 0 {
14+
log.Printf("Trying to register binding but it doesnt have a return type...")
15+
return
16+
}
17+
if numOut > 1 {
18+
log.Printf("Registering a function binding with > 1 return args. Only the first arg is handled.")
19+
}
20+
21+
// if definition.NumIn() > 0 {
22+
// log.Printf("Function binding has args... if these args cannot be found in the container when resolving, your code will error.")
23+
// }
24+
25+
resolverType := reflect.TypeOf(resolver)
26+
27+
container.addBinding(indirectType(definition.Out(0)), &Binding{
28+
bindingType: "Function",
29+
30+
resolverFunction: resolver,
31+
isFunctionResolver: true,
32+
33+
abstractType: definition,
34+
concreteType: resolverType,
35+
36+
invocable: CreateInvocableFunction(resolver),
37+
})
38+
}
39+
40+
// addConcreteBinding - Create a new container binding from the concrete value
41+
// This will set our abstract type to the concrete type and the concrete type will be our concrete type..
42+
// This just allows us to easily bind things to the container if we don't care about abstracts
43+
func (container *ContainerInstance) addConcreteBinding(definition reflect.Type, concrete any) {
44+
concreteType := definition
45+
if definition.Kind() == reflect.Ptr {
46+
concreteType = definition.Elem()
47+
}
48+
49+
// concreteWrapperFuncType := reflect.TypeOf(func() any {
50+
// return concrete
51+
// })
52+
//
53+
// concreteFunc := reflect.MakeFunc(concreteWrapperFuncType, func(args []reflect.Value) []reflect.Value {
54+
// return []reflect.Value{reflect.ValueOf(concrete)}
55+
// })
56+
57+
container.addBinding(concreteType, &Binding{
58+
bindingType: "Concrete",
59+
60+
isFunctionResolver: false,
61+
62+
abstractType: concreteType,
63+
concreteType: definition,
64+
65+
invocable: CreateInvocable(concreteType),
66+
})
67+
}
68+
69+
// addBinding - Convenience function to add a Binding for the type &
70+
// create a reverse lookup for Concrete -> Abstract
71+
func (container *ContainerInstance) addBinding(abstractType reflect.Type, binding *Binding) {
72+
container.bindings[abstractType] = binding
73+
container.concretes[binding.concreteType] = abstractType
74+
}
75+
76+
func (container *ContainerInstance) addSingletonBinding(singletonType reflect.Type, binding *Binding) {
77+
binding.isSingleton = true
78+
container.addBinding(singletonType, binding)
79+
}

container_helpers.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package container
2+
3+
import (
4+
"log"
5+
"reflect"
6+
"unsafe"
7+
)
8+
9+
// hasBinding - Look up a Type in the container and return whether it exists
10+
func (container *ContainerInstance) hasBinding(binding reflect.Type) bool {
11+
_, ok := container.bindings[binding]
12+
13+
return ok
14+
}
15+
16+
// getBindingType - Try to get a binding type from the binding arg in a few different ways
17+
// We'll first assume we're checking for an abstract type binding...
18+
// If we didn't get it from the abstract, we'll then check for the concrete...
19+
// Now as a last ditch effort, we'll look the bindingType up in container.concretes
20+
// If we can't find anything, we return nil
21+
func (container *ContainerInstance) getBindingType(binding any) reflect.Type {
22+
bindingType := getType(binding)
23+
24+
// First, we'll check if we have this type as a singleton binding
25+
testType := getConcreteReturnType(bindingType)
26+
if container.hasBinding(testType) {
27+
return testType
28+
}
29+
30+
// We'll first assume we're checking for an abstract type binding...
31+
testType = getAbstractReturnType(bindingType)
32+
if container.hasBinding(testType) {
33+
return testType
34+
}
35+
36+
// If we didn't get it from the abstract, we'll then check for the concrete...
37+
testType = getConcreteReturnType(bindingType)
38+
if container.hasBinding(testType) {
39+
return testType
40+
}
41+
42+
// Now as a last ditch effort, we'll look the bindingType up in container.concretes
43+
if potentialAbstract, ok := container.concretes[bindingType]; ok {
44+
if container.hasBinding(potentialAbstract) {
45+
return potentialAbstract
46+
}
47+
}
48+
49+
return nil
50+
}
51+
52+
// makeFromBinding - Once we've obtained our binding type from
53+
// Make, we'll then check the containers bindings
54+
// If it doesn't exist, and we have a parent container we'll then call makeFromBinding on the
55+
// parent container. Which will either recurse until a resolve is made, or return nil
56+
func (container *ContainerInstance) makeFromBinding(binding reflect.Type, parameters ...any) any {
57+
containerBinding, ok := container.bindings[binding]
58+
if !ok {
59+
if container.parent != nil {
60+
return container.parent.makeFromBinding(binding, parameters...)
61+
}
62+
log.Printf("Failed to resolve container binding for abstract type %s", binding.String())
63+
return nil
64+
}
65+
66+
return container.resolve(containerBinding, parameters...)
67+
}
68+
69+
func (container *ContainerInstance) pointer() unsafe.Pointer {
70+
return reflect.ValueOf(container).UnsafePointer()
71+
}
72+
73+
func removeAllChildContainerInstances() {
74+
// mainContainerPointer := Container.pointer()
75+
//
76+
// newContainerInstances := []unsafe.Pointer{}
77+
//
78+
// for _, containerPtr := range containerInstances {
79+
// var container = *((*ContainerInstance)(containerPtr))
80+
//
81+
// if container.parent == nil && container.pointer() != mainContainerPointer {
82+
//
83+
// }
84+
// }
85+
}

container_invocation.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package container
2+
3+
// func (container *ContainerInstance) binding(abstract any) *Binding {
4+
// binding := container.getBindingType(abstract)
5+
//
6+
// if binding == nil {
7+
// log.Printf("Failed to resolve binding for abstract type %s", reflect.TypeOf(abstract).String())
8+
// return nil
9+
// }
10+
//
11+
// containerBinding, ok := container.bindings[binding]
12+
//
13+
// if ok {
14+
// return containerBinding
15+
// }
16+
//
17+
// if container.parent != nil {
18+
// return container.parent.Binding(abstract)
19+
// }
20+
//
21+
// log.Printf("Failed to resolve container binding for abstract type %s", binding.String())
22+
// return nil
23+
// }
24+
25+
// Call - Call the specified function via the container, you can add parameters to your function,
26+
// and they will be resolved from the container, if they're registered
27+
func (container *ContainerInstance) Call(function any, parameters ...any) []any {
28+
invocable := CreateInvocableFunction(function)
29+
30+
instanceReturnValues := invocable.CallMethodWith(container, parameters...)
31+
32+
returnResult := make([]any, len(instanceReturnValues))
33+
for i, value := range instanceReturnValues {
34+
returnResult[i] = value.Interface()
35+
}
36+
37+
return returnResult
38+
}

IocContainer.go container_main.go

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package Container
1+
package container
22

33
import (
44
"reflect"
@@ -7,25 +7,26 @@ import (
77
"golang.org/x/exp/maps"
88
)
99

10-
// IocContainerConfig - Holds configuration values... soon I will add some more, make them work fully
10+
// ContainerConfig - Holds configuration values... soon I will add some more, make them work fully
1111
// Right now this is a placeholder
12-
type IocContainerConfig struct {
12+
type ContainerConfig struct {
1313
OnlyInjectStructFieldsWithInjectTag bool
1414
}
1515

16-
type IocContainer struct {
17-
Config *IocContainerConfig
16+
// ContainerInstance - Holds all of our container registration
17+
type ContainerInstance struct {
18+
Config *ContainerConfig
1819

1920
// Store our singleton instances
20-
// instances map[reflect.Type]*IocContainerBinding
21+
// instances map[reflect.Type]*Binding
2122

2223
// Our resolved singleton instances
2324
resolved map[reflect.Type]any
2425

2526
// Store our abstract -> concrete bindings
2627
// If a type doesn't have an abstract type
2728
// We'll store concrete -> concrete
28-
bindings map[reflect.Type]*IocContainerBinding
29+
bindings map[reflect.Type]*Binding
2930

3031
// Store aliases of Concrete -> Abstract, so we can resolve from concrete
3132
// when we only bound Abstract -> Concrete
@@ -36,16 +37,16 @@ type IocContainer struct {
3637
tagged map[string][]reflect.Type
3738

3839
// If our container is a child container, we'll have a pointer to our parent
39-
parent *IocContainer
40+
parent *ContainerInstance
4041
}
4142

4243
// CreateContainer - Create a new container instance
43-
func CreateContainer() *IocContainer {
44-
c := &IocContainer{
45-
Config: &IocContainerConfig{OnlyInjectStructFieldsWithInjectTag: false},
44+
func CreateContainer() *ContainerInstance {
45+
c := &ContainerInstance{
46+
Config: &ContainerConfig{OnlyInjectStructFieldsWithInjectTag: false},
4647

4748
resolved: make(map[reflect.Type]any),
48-
bindings: make(map[reflect.Type]*IocContainerBinding),
49+
bindings: make(map[reflect.Type]*Binding),
4950
concretes: make(map[reflect.Type]reflect.Type),
5051
tagged: make(map[string][]reflect.Type),
5152
}
@@ -60,11 +61,11 @@ var containerInstances = []unsafe.Pointer{}
6061

6162
// CreateChildContainer - Returns a new container, any failed look-ups of our
6263
// child container, will then be looked up in the parent, or returned nil
63-
func (container *IocContainer) CreateChildContainer() *IocContainer {
64-
c := &IocContainer{
65-
Config: &IocContainerConfig{OnlyInjectStructFieldsWithInjectTag: false},
64+
func (container *ContainerInstance) CreateChildContainer() *ContainerInstance {
65+
c := &ContainerInstance{
66+
Config: &ContainerConfig{OnlyInjectStructFieldsWithInjectTag: false},
6667
resolved: make(map[reflect.Type]any),
67-
bindings: make(map[reflect.Type]*IocContainerBinding),
68+
bindings: make(map[reflect.Type]*Binding),
6869
concretes: make(map[reflect.Type]reflect.Type),
6970
tagged: make(map[string][]reflect.Type),
7071
}
@@ -78,19 +79,21 @@ func (container *IocContainer) CreateChildContainer() *IocContainer {
7879

7980
// ClearInstances - This will just remove any singleton instances from the container
8081
// When they are next resolved via Make/MakeTo, they will be instantiated again
81-
func (container *IocContainer) ClearInstances() {
82+
func (container *ContainerInstance) ClearInstances() {
8283
maps.Clear(container.resolved)
8384
}
8485

8586
// Reset - Reset will empty all bindings in this container, you will have to register
8687
// any bindings again before you can resolve them.
87-
func (container *IocContainer) Reset() {
88+
func (container *ContainerInstance) Reset() {
8889
maps.Clear(container.resolved)
8990
maps.Clear(container.bindings)
9091
maps.Clear(container.concretes)
92+
maps.Clear(container.tagged)
93+
container.parent = nil
9194
}
9295

9396
// ParentContainer - Returns the parent container, if one exists
94-
func (container *IocContainer) ParentContainer() *IocContainer {
97+
func (container *ContainerInstance) ParentContainer() *ContainerInstance {
9598
return container.parent
9699
}

0 commit comments

Comments
 (0)