-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontainer.go
116 lines (99 loc) · 2.81 KB
/
container.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package dino
import (
"reflect"
"strings"
"sync"
)
// Container stores maps between abstractions and concrete implementations.
type Container struct {
m sync.Map
}
// getInnerMapOfNames gets a map of names to bindings.
func (c *Container) getInnerMapOfNames(ty reflect.Type) *sync.Map {
m, ok := c.m.Load(ty)
if ok {
return m.(*sync.Map)
}
m, _ = c.m.LoadOrStore(ty, &sync.Map{})
return m.(*sync.Map)
}
// Get tries to create, retrieve or inject an object of type T.
func Get[T any](c *Container) (svc T, err error) {
svc, err = GetNamed[T](c, "")
return
}
// GetNamed tries to create, retrieve or inject an object of type T.
func GetNamed[T any](c *Container, name string) (svc T, err error) {
ty := getType[T]()
s, err := c.tryGet(ty, name, make([]DepLink, 0, 4))
if err != nil {
return
}
svc, ok := s.Interface().(T)
if !ok {
err = InvalidTypeError{
name: name,
expected: ty,
actual: reflect.TypeOf(s),
}
}
return
}
// tryGets attempts to retrieve a service in a ready state from the container.
func (c *Container) tryGet(ty reflect.Type, name string, chain []DepLink) (reflect.Value, error) {
b, ok := c.tryLoad(ty, name)
if !ok {
return reflect.Value{}, BindingMissingError{ty: ty, name: name}
}
chain = append(chain, DepLink{ty: ty, binding: b})
svc, err := b.Provide(c, chain)
return svc, err
}
// tryLoad attempts to retrieve the Binding for a provided type and name.
func (c *Container) tryLoad(ty reflect.Type, name string) (b Binding, ok bool) {
v, ok := c.getInnerMapOfNames(ty).Load(name)
if ok {
b, ok = v.(Binding)
}
return
}
// store stores the Binding for a provided type and name, replacing all previous values.
func (c *Container) store(ty reflect.Type, name string, binding Binding) {
c.getInnerMapOfNames(ty).Store(name, binding)
}
// InvalidTypeError occurs when a binding is present,
// but it does not implement the requested abstraction.
type InvalidTypeError struct {
name string
expected reflect.Type
actual reflect.Type
}
func (e InvalidTypeError) Error() string {
var b strings.Builder
b.WriteString("container had stored a binding for type ")
b.WriteString(e.expected.String())
b.WriteString(" and name ")
b.WriteString(e.name)
b.WriteString(", but it provided an object of type ")
b.WriteString(e.actual.String())
return b.String()
}
// BindingMissingError happens when a container does not have binding information
// about a provided type-name pair.
type BindingMissingError struct {
ty reflect.Type
name string
}
func (e BindingMissingError) Error() string {
var b strings.Builder
b.WriteString("container did not have any info about type ")
b.WriteString(e.ty.String())
if e.name == "" {
b.WriteString(" in global namespace")
} else {
b.WriteString(" in namespace \"")
b.WriteString(e.name)
b.WriteString("\"")
}
return b.String()
}