-
Notifications
You must be signed in to change notification settings - Fork 2
/
ore.go
121 lines (98 loc) · 3.49 KB
/
ore.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
117
118
119
120
121
package ore
import (
"context"
)
var (
DefaultContainer = NewContainer()
//contextKeysRepositoryID is a special context key. The value of this key is the collection of other context keys stored in the context.
contextKeysRepositoryID specialContextKey = "__ORE_CTX_KEYS_REPO"
//contextKeyResolversStack is a special context key. The value of this key is the [ResolversStack].
contextKeyResolversStack specialContextKey = "__ORE_DEP_STACK"
//placeholderResolverID is a special resolverID of every "placeholder". "placeholder" is a special resolver
//describing a "promise" for a concrete value, which will be provided in runtime.
placeholderResolverID = -1
//nilKey is a special key used when registering / resolving unkeyed service.
nilKey = specialOreKey(0)
)
var types = []Lifetime{Singleton, Transient, Scoped}
type contextKeysRepository = []contextKey
type Creator[T any] interface {
New(ctx context.Context) (T, context.Context)
}
func init() {
DefaultContainer.SetName("DEFAULT")
}
// Generates a unique identifier for a service resolver based on type and key(s)
func getTypeID[K comparable](pointerTypeName pointerTypeName, key K) typeID {
return typeID{pointerTypeName, key}
}
// Generates a unique identifier for a service resolver based on type and key(s)
func typeIdentifier[T any, K comparable](key K) typeID {
return getTypeID(getPointerTypeName[T](), key)
}
// Appends a service resolver to the container with type and key
func addResolver[T any, K comparable](this *Container, resolver serviceResolverImpl[T], key K) {
if this.isSealed {
panic(alreadyBuiltCannotAdd)
}
typeID := typeIdentifier[T](key)
this.lock.Lock()
defer this.lock.Unlock()
resolverID := len(this.resolvers[typeID])
if resolver.isPlaceholder() {
if resolverID > 0 {
panic(typeAlreadyRegistered(typeID))
}
resolverID = placeholderResolverID
}
resolver.id = contextKey{
typeID: typeID,
containerID: this.containerID,
resolverID: resolverID,
}
this.resolvers[typeID] = append(this.resolvers[typeID], resolver)
}
func replaceResolver[T any](this *Container, resolver serviceResolverImpl[T]) {
this.lock.Lock()
defer this.lock.Unlock()
this.resolvers[resolver.id.typeID][resolver.id.resolverID] = resolver
}
func addAliases[TInterface, TImpl any](this *Container) {
originalType := getPointerTypeName[TImpl]()
aliasType := getPointerTypeName[TInterface]()
if originalType == aliasType {
return
}
this.lock.Lock()
defer this.lock.Unlock()
for _, ot := range this.aliases[aliasType] {
if ot == originalType {
return //already registered
}
}
this.aliases[aliasType] = append(this.aliases[aliasType], originalType)
}
// Seal puts the DEFAULT container into read-only mode, preventing any further registrations.
func Seal() {
DefaultContainer.Seal()
}
// IsSealed checks whether the DEFAULT container is sealed (in readonly mode)
func IsSealed() bool {
return DefaultContainer.IsSealed()
}
// Validate invokes all registered resolvers. It panics if any of them fails.
// It is recommended to call this function on application start, or in the CI/CD test pipeline
// The objective is to panic early when the container is bad configured. For eg:
//
// - (1) Missing dependency (forget to register certain resolvers)
// - (2) cyclic dependency
// - (3) lifetime misalignment (a longer lifetime service depends on a shorter one).
func Validate() {
DefaultContainer.Validate()
}
func ContainerID() int32 {
return DefaultContainer.containerID
}
func Name() string {
return DefaultContainer.name
}