3
3
//
4
4
// It is recommended to use the package via the following import:
5
5
//
6
- // import . "go.atomizer.io/stream"
6
+ // import . "go.atomizer.io/stream"
7
7
//
8
8
// Using the `.` import allows for functions to be called directly as if
9
9
// the functions were in the same namespace without the need to append
@@ -18,13 +18,23 @@ import (
18
18
"go.structs.dev/gen"
19
19
)
20
20
21
+ type readonly [T any ] interface {
22
+ <- chan T | chan T
23
+ }
24
+
25
+ type writeonly [T any ] interface {
26
+ chan <- T | chan T
27
+ }
28
+
21
29
// Pipe accepts an incoming data channel and pipes it to the supplied
22
30
// outgoing data channel.
23
31
//
24
32
// NOTE: Execute the Pipe function in a goroutine if parallel execution is
25
- // desired. Cancelling the context or closing the incoming channel is important
33
+ // desired. Canceling the context or closing the incoming channel is important
26
34
// to ensure that the goroutine is properly terminated.
27
- func Pipe [T any ](ctx context.Context , in <- chan T , out chan <- T ) {
35
+ func Pipe [In readonly [T ], Out writeonly [T ], T any ](
36
+ ctx context.Context , in In , out Out ,
37
+ ) {
28
38
ctx = _ctx (ctx )
29
39
30
40
for {
@@ -51,10 +61,10 @@ type InterceptFunc[T, U any] func(context.Context, T) (U, bool)
51
61
// accepts the incoming data and returns data of the same type and a boolean
52
62
// indicating whether the data should be forwarded to the output channel.
53
63
// The function is executed for each data item in the incoming channel as long
54
- // as the context is not cancelled or the incoming channel remains open.
55
- func Intercept [T , U any ](
64
+ // as the context is not canceled or the incoming channel remains open.
65
+ func Intercept [In readonly [ T ], T , U any ](
56
66
ctx context.Context ,
57
- in <- chan T ,
67
+ in In ,
58
68
fn InterceptFunc [T , U ],
59
69
) <- chan U {
60
70
ctx = _ctx (ctx )
@@ -100,9 +110,9 @@ func Intercept[T, U any](
100
110
// that receives all the data from the supplied channels.
101
111
//
102
112
// NOTE: The transfer takes place in a goroutine for each channel
103
- // so ensuring that the context is cancelled or the incoming channels
113
+ // so ensuring that the context is canceled or the incoming channels
104
114
// are closed is important to ensure that the goroutine is terminated.
105
- func FanIn [T any ](ctx context.Context , in ... <- chan T ) <- chan T {
115
+ func FanIn [In readonly [ T ], T any ](ctx context.Context , in ... In ) <- chan T {
106
116
ctx = _ctx (ctx )
107
117
out := make (chan T )
108
118
@@ -135,9 +145,11 @@ func FanIn[T any](ctx context.Context, in ...<-chan T) <-chan T {
135
145
// supplied outgoing data channels.
136
146
//
137
147
// NOTE: Execute the FanOut function in a goroutine if parallel execution is
138
- // desired. Cancelling the context or closing the incoming channel is important
148
+ // desired. Canceling the context or closing the incoming channel is important
139
149
// to ensure that the goroutine is properly terminated.
140
- func FanOut [T any ](ctx context.Context , in <- chan T , out ... chan <- T ) {
150
+ func FanOut [In readonly [T ], Out writeonly [T ], T any ](
151
+ ctx context.Context , in In , out ... Out ,
152
+ ) {
141
153
ctx = _ctx (ctx )
142
154
143
155
if len (out ) == 0 {
@@ -178,25 +190,26 @@ func FanOut[T any](ctx context.Context, in <-chan T, out ...chan<- T) {
178
190
for len (selectCases ) > 1 {
179
191
chosen , _ , _ := reflect .Select (selectCases )
180
192
181
- // The context was cancelled .
193
+ // The context was canceled .
182
194
if chosen == 0 {
183
195
return
184
196
}
185
197
186
198
selectCases = gen .Exclude (selectCases , selectCases [chosen ])
187
199
}
188
200
}
189
-
190
201
}
191
202
}
192
203
193
204
// Distribute accepts an incoming data channel and distributes the data among
194
205
// the supplied outgoing data channels using a dynamic select statement.
195
206
//
196
207
// NOTE: Execute the Distribute function in a goroutine if parallel execution is
197
- // desired. Cancelling the context or closing the incoming channel is important
208
+ // desired. Canceling the context or closing the incoming channel is important
198
209
// to ensure that the goroutine is properly terminated.
199
- func Distribute [T any ](ctx context.Context , in <- chan T , out ... chan <- T ) {
210
+ func Distribute [In readonly [T ], Out writeonly [T ], T any ](
211
+ ctx context.Context , in In , out ... Out ,
212
+ ) {
200
213
ctx = _ctx (ctx )
201
214
202
215
if len (out ) == 0 {
@@ -226,6 +239,32 @@ func Distribute[T any](ctx context.Context, in <-chan T, out ...chan<- T) {
226
239
})
227
240
_ , _ , _ = reflect .Select (selectCases )
228
241
}
229
-
230
242
}
231
243
}
244
+
245
+ // Drain accepts a channel and drains the channel until the channel is closed
246
+ // or the context is canceled.
247
+ func Drain [U readonly [T ], T any ](ctx context.Context , in U ) {
248
+ ctx = _ctx (ctx )
249
+
250
+ go func () {
251
+ for {
252
+ select {
253
+ case <- ctx .Done ():
254
+ return
255
+ case _ , ok := <- in :
256
+ if ! ok {
257
+ return
258
+ }
259
+ }
260
+ }
261
+ }()
262
+ }
263
+
264
+ // Any accepts an incoming data channel and converts the channel to a readonly
265
+ // channel of the `any` type.
266
+ func Any [U readonly [T ], T any ](ctx context.Context , in U ) <- chan any {
267
+ return Intercept (ctx , in , func (_ context.Context , in T ) (any , bool ) {
268
+ return in , true
269
+ })
270
+ }
0 commit comments