-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
context_wrapper.go
215 lines (186 loc) · 6.33 KB
/
context_wrapper.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
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
207
208
209
210
211
212
213
214
215
package iris
import (
"sync"
"time"
"github.com/kataras/iris/v12/context"
)
type (
// ContextSetter is an interface which can be implemented by a struct
// to set the iris.Context to the struct.
// The receiver must be a pointer of the struct.
ContextSetter interface {
// SetContext sets the iris.Context to the struct.
SetContext(Context)
}
// ContextSetterPtr is a pointer of T which implements the `ContextSetter` interface.
// The T must be a struct.
ContextSetterPtr[T any] interface {
*T
ContextSetter
}
// emptyContextSetter is an empty struct which implements the `ContextSetter` interface.
emptyContextSetter struct{}
)
// SetContext method implements `ContextSetter` interface.
func (*emptyContextSetter) SetContext(Context) {}
// ContextPool is a pool of T. It's used to acquire and release custom context.
// Use of custom implementation or `NewContextPool`.
//
// See `NewContextWrapper` and `NewContextPool` for more.
type (
ContextPool[T any] interface {
// Acquire must return a new T from a pool.
Acquire(ctx Context) T
// Release must put the T back to the pool.
Release(T)
}
// syncContextPool is a sync pool implementation of T.
// It's used to acquire and release T.
// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
// The contextPtr is passed to the handler as an argument.
// ThecontextPtr is not shared between requests.
// The contextPtr must implement the `ContextSetter` interface.
// The T must be a struct.
// The contextPtr must be a pointer of T.
syncContextPool[T any, contextPtr ContextSetterPtr[T]] struct {
pool *sync.Pool
}
)
// Ensure that syncContextPool implements ContextPool.
var _ ContextPool[*emptyContextSetter] = (*syncContextPool[emptyContextSetter, *emptyContextSetter])(nil)
// NewContextPool returns a new ContextPool default implementation which
// uses sync.Pool to implement its Acquire and Release methods.
// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
// The contextPtr is passed to the handler as an argument.
// ThecontextPtr is not shared between requests.
// The contextPtr must implement the `ContextSetter` interface.
// The T must be a struct.
// The contextPtr must be a pointer of T.
//
// Example:
// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())
func NewContextPool[T any, contextPtr ContextSetterPtr[T]]() ContextPool[contextPtr] {
return &syncContextPool[T, contextPtr]{
pool: &sync.Pool{
New: func() interface{} {
var t contextPtr = new(T)
return t
},
},
}
}
// Acquire returns a new T from the sync pool.
func (p *syncContextPool[T, contextPtr]) Acquire(ctx Context) contextPtr {
// var t contextPtr
// if v := p.pool.Get(); v == nil {
// t = new(T)
// } else {
// t = v.(contextPtr)
// }
t := p.pool.Get().(contextPtr)
t.SetContext(ctx)
return t
}
// Release puts the T back to the sync pool.
func (p *syncContextPool[T, contextPtr]) Release(t contextPtr) {
p.pool.Put(t)
}
// ContextWrapper is a wrapper for handlers which expect a T instead of iris.Context.
//
// See the `NewContextWrapper` function for more.
type ContextWrapper[T any] struct {
pool ContextPool[T]
}
// NewContextWrapper returns a new ContextWrapper.
// If pool is nil, a default pool is used.
// The default pool's AcquireFunc returns a zero value of T.
// The default pool's ReleaseFunc does nothing.
// The default pool is used when the pool is nil.
// Use the `iris.NewContextPool[T, *T]()` to pass a simple context pool.
// Then, use the `Handler` method to wrap custom handlers to iris ones.
//
// Example: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context
func NewContextWrapper[T any](pool ContextPool[T]) *ContextWrapper[T] {
if pool == nil {
panic("pool cannot be nil")
}
return &ContextWrapper[T]{
pool: pool,
}
}
// Pool returns the pool, useful when manually Acquire and Release of custom context is required.
func (w *ContextWrapper[T]) Pool() ContextPool[T] {
return w.pool
}
// Handler wraps the handler with the pool's Acquire and Release methods.
// It returns a new handler which expects a T instead of iris.Context.
// The T is the type of the pool.
// The T is acquired from the pool and released back to the pool after the handler's execution.
// The T is passed to the handler as an argument.
// The T is not shared between requests.
func (w *ContextWrapper[T]) Handler(handler func(T)) Handler {
if handler == nil {
return nil
}
return func(ctx Context) {
newT := w.pool.Acquire(ctx)
handler(newT)
w.pool.Release(newT)
}
}
// Handlers wraps the handlers with the pool's Acquire and Release methods.
func (w *ContextWrapper[T]) Handlers(handlers ...func(T)) context.Handlers {
newHandlers := make(context.Handlers, len(handlers))
for i, handler := range handlers {
newHandlers[i] = w.Handler(handler)
}
return newHandlers
}
// HandlerReturnError same as `Handler` but it converts a handler which returns an error.
func (w *ContextWrapper[T]) HandlerReturnError(handler func(T) error) func(Context) error {
if handler == nil {
return nil
}
return func(ctx Context) error {
newT := w.pool.Acquire(ctx)
err := handler(newT)
w.pool.Release(newT)
return err
}
}
// HandlerReturnDuration same as `Handler` but it converts a handler which returns a time.Duration.
func (w *ContextWrapper[T]) HandlerReturnDuration(handler func(T) time.Duration) func(Context) time.Duration {
if handler == nil {
return nil
}
return func(ctx Context) time.Duration {
newT := w.pool.Acquire(ctx)
duration := handler(newT)
w.pool.Release(newT)
return duration
}
}
// Filter same as `Handler` but it converts a handler to Filter.
func (w *ContextWrapper[T]) Filter(handler func(T) bool) Filter {
if handler == nil {
return nil
}
return func(ctx Context) bool {
newT := w.pool.Acquire(ctx)
shouldContinue := handler(newT)
w.pool.Release(newT)
return shouldContinue
}
}
// FallbackViewFunc same as `Handler` but it converts a handler to FallbackViewFunc.
func (w *ContextWrapper[T]) FallbackViewFunc(handler func(ctx T, err ErrViewNotExist) error) FallbackViewFunc {
if handler == nil {
return nil
}
return func(ctx Context, err ErrViewNotExist) error {
newT := w.pool.Acquire(ctx)
returningErr := handler(newT, err)
w.pool.Release(newT)
return returningErr
}
}