-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscope_impl.go
More file actions
156 lines (124 loc) · 3.04 KB
/
scope_impl.go
File metadata and controls
156 lines (124 loc) · 3.04 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package vessel
import (
"fmt"
"sync"
"github.com/xraph/go-utils/di"
)
// scope implements Scope.
type scope struct {
parent *containerImpl
instances map[string]any
context map[string]any // Context storage for request-specific data
mu sync.RWMutex
ended bool
}
// newScope creates a new scope.
func newScope(parent *containerImpl) *scope {
return &scope{
parent: parent,
instances: make(map[string]any),
context: make(map[string]any),
}
}
// Resolve returns a service by name from this scope.
func (s *scope) Resolve(name string) (any, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.ended {
return nil, ErrScopeEnded
}
// Get registration from parent
s.parent.mu.RLock()
reg, exists := s.parent.services[name]
s.parent.mu.RUnlock()
if !exists {
return nil, ErrServiceNotFound(name)
}
// Singleton services: resolve from parent
if reg.singleton {
return s.parent.Resolve(name)
}
// Scoped services: cache in this scope
if reg.scoped {
if instance, ok := s.instances[name]; ok {
return instance, nil
}
// Create new instance for this scope
instance, err := reg.factory(s.parent)
if err != nil {
return nil, NewServiceError(name, "resolve", err)
}
s.instances[name] = instance
return instance, nil
}
// Transient services: always create new
instance, err := reg.factory(s.parent)
if err != nil {
return nil, NewServiceError(name, "resolve", err)
}
return instance, nil
}
// End cleans up all scoped services in this scope.
func (s *scope) End() error {
s.mu.Lock()
defer s.mu.Unlock()
if s.ended {
return ErrScopeEnded
}
// Dispose of scoped instances in reverse order
var errs []error
for name, instance := range s.instances {
if disposable, ok := instance.(di.Disposable); ok {
if err := disposable.Dispose(); err != nil {
errs = append(errs, fmt.Errorf("failed to dispose %s: %w", name, err))
}
}
}
s.instances = nil
s.context = nil
s.ended = true
if len(errs) > 0 {
return fmt.Errorf("scope cleanup errors: %v", errs)
}
return nil
}
// Has checks if a service is registered (delegates to parent container).
func (s *scope) Has(name string) bool {
return s.parent.Has(name)
}
// IsEnded returns true if the scope has been ended.
func (s *scope) IsEnded() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.ended
}
// Services returns a list of services resolved in this scope.
func (s *scope) Services() []string {
s.mu.RLock()
defer s.mu.RUnlock()
names := make([]string, 0, len(s.instances))
for name := range s.instances {
names = append(names, name)
}
return names
}
// Parent returns the parent container.
func (s *scope) Parent() Vessel {
return s.parent
}
// Set stores a value in the scope context.
func (s *scope) Set(key string, value any) {
s.mu.Lock()
defer s.mu.Unlock()
if s.ended {
return // Silently ignore if scope ended
}
s.context[key] = value
}
// Get retrieves a value from the scope context.
func (s *scope) Get(key string) (any, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
value, ok := s.context[key]
return value, ok
}