-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlazy.go
More file actions
206 lines (165 loc) · 4.54 KB
/
lazy.go
File metadata and controls
206 lines (165 loc) · 4.54 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package vessel
import (
"fmt"
"sync"
"github.com/xraph/go-utils/di"
)
// Lazy wraps a dependency that is resolved on first access.
// This is useful for breaking circular dependencies or deferring
// resolution of expensive services until they're actually needed.
type Lazy[T any] struct {
container di.Container
name string
mu sync.Once
value T
err error
resolved bool
}
// NewLazy creates a new lazy dependency wrapper.
func NewLazy[T any](container di.Container, name string) *Lazy[T] {
return &Lazy[T]{
container: container,
name: name,
}
}
// Get resolves the dependency and returns it.
// The resolution happens only once; subsequent calls return the cached value.
func (l *Lazy[T]) Get() (T, error) {
l.mu.Do(func() {
instance, err := l.container.Resolve(l.name)
if err != nil {
l.err = err
return
}
typed, ok := instance.(T)
if !ok {
var zero T
l.err = fmt.Errorf("lazy dependency %s: expected type %T, got %T", l.name, zero, instance)
return
}
l.value = typed
l.resolved = true
})
return l.value, l.err
}
// MustGet resolves the dependency and returns it, panicking on error.
func (l *Lazy[T]) MustGet() T {
value, err := l.Get()
if err != nil {
panic(fmt.Sprintf("lazy dependency %s failed: %v", l.name, err))
}
return value
}
// IsResolved returns true if the dependency has been resolved.
func (l *Lazy[T]) IsResolved() bool {
return l.resolved
}
// Name returns the name of the dependency.
func (l *Lazy[T]) Name() string {
return l.name
}
// OptionalLazy wraps an optional dependency that is resolved on first access.
// Returns nil without error if the dependency is not found.
type OptionalLazy[T any] struct {
container di.Container
name string
mu sync.Once
value T
err error
resolved bool
found bool
}
// NewOptionalLazy creates a new optional lazy dependency wrapper.
func NewOptionalLazy[T any](container di.Container, name string) *OptionalLazy[T] {
return &OptionalLazy[T]{
container: container,
name: name,
}
}
// Get resolves the dependency and returns it.
// Returns the zero value without error if the dependency is not found.
func (l *OptionalLazy[T]) Get() (T, error) {
l.mu.Do(func() {
if !l.container.Has(l.name) {
l.resolved = true
l.found = false
return
}
instance, err := l.container.Resolve(l.name)
if err != nil {
l.err = err
return
}
typed, ok := instance.(T)
if !ok {
var zero T
l.err = fmt.Errorf("optional lazy dependency %s: expected type %T, got %T", l.name, zero, instance)
return
}
l.value = typed
l.resolved = true
l.found = true
})
return l.value, l.err
}
// MustGet resolves the dependency and returns it, panicking on error.
// Returns the zero value if the dependency is not found (does not panic).
func (l *OptionalLazy[T]) MustGet() T {
value, err := l.Get()
if err != nil {
panic(fmt.Sprintf("optional lazy dependency %s failed: %v", l.name, err))
}
return value
}
// IsResolved returns true if the dependency has been resolved.
func (l *OptionalLazy[T]) IsResolved() bool {
return l.resolved
}
// IsFound returns true if the dependency was found (only valid after resolution).
func (l *OptionalLazy[T]) IsFound() bool {
return l.found
}
// Name returns the name of the dependency.
func (l *OptionalLazy[T]) Name() string {
return l.name
}
// Provider wraps a dependency that creates new instances on each access.
// This is useful for transient dependencies where a fresh instance is needed each time.
type Provider[T any] struct {
container di.Container
name string
}
// NewProvider creates a new provider for transient dependencies.
func NewProvider[T any](container di.Container, name string) *Provider[T] {
return &Provider[T]{
container: container,
name: name,
}
}
// Provide resolves and returns a new instance of the dependency.
// Each call may return a different instance (if the service is transient).
func (p *Provider[T]) Provide() (T, error) {
instance, err := p.container.Resolve(p.name)
if err != nil {
var zero T
return zero, err
}
typed, ok := instance.(T)
if !ok {
var zero T
return zero, fmt.Errorf("provider %s: expected type %T, got %T", p.name, zero, instance)
}
return typed, nil
}
// MustProvide resolves and returns a new instance, panicking on error.
func (p *Provider[T]) MustProvide() T {
value, err := p.Provide()
if err != nil {
panic(fmt.Sprintf("provider %s failed: %v", p.name, err))
}
return value
}
// Name returns the name of the dependency.
func (p *Provider[T]) Name() string {
return p.name
}