@@ -28,9 +28,19 @@ where Upstream.Element == ArraySlice<UInt8> {
28
28
/// The upstream sequence.
29
29
private let upstream : Upstream
30
30
31
+ /// A closure that determines whether the given byte chunk should be forwarded to the consumer.
32
+ /// - Parameter: A byte chunk.
33
+ /// - Returns: `true` if the byte chunk should be forwarded, `false` if this byte chunk is the terminating sequence.
34
+ private let predicate : @Sendable ( ArraySlice < UInt8 > ) -> Bool
35
+
31
36
/// Creates a new sequence.
32
- /// - Parameter upstream: The upstream sequence of arbitrary byte chunks.
33
- public init ( upstream: Upstream ) { self . upstream = upstream }
37
+ /// - Parameters:
38
+ /// - upstream: The upstream sequence of arbitrary byte chunks.
39
+ /// - predicate: A closure that determines whether the given byte chunk should be forwarded to the consumer.
40
+ public init ( upstream: Upstream , while predicate: @escaping @Sendable ( ArraySlice < UInt8 > ) -> Bool ) {
41
+ self . upstream = upstream
42
+ self . predicate = predicate
43
+ }
34
44
}
35
45
36
46
extension ServerSentEventsDeserializationSequence : AsyncSequence {
@@ -46,7 +56,16 @@ extension ServerSentEventsDeserializationSequence: AsyncSequence {
46
56
var upstream : UpstreamIterator
47
57
48
58
/// The state machine of the iterator.
49
- var stateMachine : StateMachine = . init( )
59
+ var stateMachine : StateMachine
60
+
61
+ /// Creates a new sequence.
62
+ /// - Parameters:
63
+ /// - upstream: The upstream sequence of arbitrary byte chunks.
64
+ /// - predicate: A closure that determines whether the given byte chunk should be forwarded to the consumer.
65
+ init ( upstream: UpstreamIterator , while predicate: @escaping ( ( ArraySlice < UInt8 > ) -> Bool ) ) {
66
+ self . upstream = upstream
67
+ self . stateMachine = . init( while: predicate)
68
+ }
50
69
51
70
/// Asynchronously advances to the next element and returns it, or ends the
52
71
/// sequence if there is no next element.
@@ -70,7 +89,7 @@ extension ServerSentEventsDeserializationSequence: AsyncSequence {
70
89
/// Creates the asynchronous iterator that produces elements of this
71
90
/// asynchronous sequence.
72
91
public func makeAsyncIterator( ) -> Iterator < Upstream . AsyncIterator > {
73
- Iterator ( upstream: upstream. makeAsyncIterator ( ) )
92
+ Iterator ( upstream: upstream. makeAsyncIterator ( ) , while : predicate )
74
93
}
75
94
}
76
95
@@ -79,26 +98,30 @@ extension AsyncSequence where Element == ArraySlice<UInt8>, Self: Sendable {
79
98
/// Returns another sequence that decodes each event's data as the provided type using the provided decoder.
80
99
///
81
100
/// Use this method if the event's `data` field is not JSON, or if you don't want to parse it using `asDecodedServerSentEventsWithJSONData`.
101
+ /// - Parameter: A closure that determines whether the given byte chunk should be forwarded to the consumer.
82
102
/// - Returns: A sequence that provides the events.
83
- public func asDecodedServerSentEvents( ) -> ServerSentEventsDeserializationSequence <
84
- ServerSentEventsLineDeserializationSequence < Self >
85
- > { . init( upstream: ServerSentEventsLineDeserializationSequence ( upstream: self ) ) }
86
-
103
+ public func asDecodedServerSentEvents(
104
+ while predicate: @escaping @Sendable ( ArraySlice < UInt8 > ) -> Bool = { _ in true }
105
+ ) -> ServerSentEventsDeserializationSequence < ServerSentEventsLineDeserializationSequence < Self > > {
106
+ . init( upstream: ServerSentEventsLineDeserializationSequence ( upstream: self ) , while: predicate)
107
+ }
87
108
/// Returns another sequence that decodes each event's data as the provided type using the provided decoder.
88
109
///
89
110
/// Use this method if the event's `data` field is JSON.
90
111
/// - Parameters:
91
112
/// - dataType: The type to decode the JSON data into.
92
113
/// - decoder: The JSON decoder to use.
114
+ /// - predicate: A closure that determines whether the given byte sequence is the terminating byte sequence defined by the API.
93
115
/// - Returns: A sequence that provides the events with the decoded JSON data.
94
116
public func asDecodedServerSentEventsWithJSONData< JSONDataType: Decodable > (
95
117
of dataType: JSONDataType . Type = JSONDataType . self,
96
- decoder: JSONDecoder = . init( )
118
+ decoder: JSONDecoder = . init( ) ,
119
+ while predicate: @escaping @Sendable ( ArraySlice < UInt8 > ) -> Bool = { _ in true }
97
120
) -> AsyncThrowingMapSequence <
98
121
ServerSentEventsDeserializationSequence < ServerSentEventsLineDeserializationSequence < Self > > ,
99
122
ServerSentEventWithJSONData < JSONDataType >
100
123
> {
101
- asDecodedServerSentEvents ( )
124
+ asDecodedServerSentEvents ( while : predicate )
102
125
. map { event in
103
126
ServerSentEventWithJSONData (
104
127
event: event. event,
@@ -118,10 +141,10 @@ extension ServerSentEventsDeserializationSequence.Iterator {
118
141
struct StateMachine {
119
142
120
143
/// The possible states of the state machine.
121
- enum State : Hashable {
144
+ enum State {
122
145
123
146
/// Accumulating an event, which hasn't been emitted yet.
124
- case accumulatingEvent( ServerSentEvent , buffer: [ ArraySlice < UInt8 > ] )
147
+ case accumulatingEvent( ServerSentEvent , buffer: [ ArraySlice < UInt8 > ] , predicate : ( ArraySlice < UInt8 > ) -> Bool )
125
148
126
149
/// Finished, the terminal state.
127
150
case finished
@@ -134,7 +157,9 @@ extension ServerSentEventsDeserializationSequence.Iterator {
134
157
private( set) var state : State
135
158
136
159
/// Creates a new state machine.
137
- init ( ) { self . state = . accumulatingEvent( . init( ) , buffer: [ ] ) }
160
+ init ( while predicate: @escaping ( ArraySlice < UInt8 > ) -> Bool ) {
161
+ self . state = . accumulatingEvent( . init( ) , buffer: [ ] , predicate: predicate)
162
+ }
138
163
139
164
/// An action returned by the `next` method.
140
165
enum NextAction {
@@ -156,20 +181,24 @@ extension ServerSentEventsDeserializationSequence.Iterator {
156
181
/// - Returns: An action to perform.
157
182
mutating func next( ) -> NextAction {
158
183
switch state {
159
- case . accumulatingEvent( var event, var buffer) :
184
+ case . accumulatingEvent( var event, var buffer, let predicate ) :
160
185
guard let line = buffer. first else { return . needsMore }
161
186
state = . mutating
162
187
buffer. removeFirst ( )
163
188
if line. isEmpty {
164
189
// Dispatch the accumulated event.
165
- state = . accumulatingEvent( . init( ) , buffer: buffer)
166
190
// If the last character of data is a newline, strip it.
167
191
if event. data? . hasSuffix ( " \n " ) ?? false { event. data? . removeLast ( ) }
192
+ if let data = event. data, !predicate( ArraySlice ( data. utf8) ) {
193
+ state = . finished
194
+ return . returnNil
195
+ }
196
+ state = . accumulatingEvent( . init( ) , buffer: buffer, predicate: predicate)
168
197
return . emitEvent( event)
169
198
}
170
199
if line. first! == ASCII . colon {
171
200
// A comment, skip this line.
172
- state = . accumulatingEvent( event, buffer: buffer)
201
+ state = . accumulatingEvent( event, buffer: buffer, predicate : predicate )
173
202
return . noop
174
203
}
175
204
// Parse the field name and value.
@@ -193,7 +222,7 @@ extension ServerSentEventsDeserializationSequence.Iterator {
193
222
}
194
223
guard let value else {
195
224
// An unknown type of event, skip.
196
- state = . accumulatingEvent( event, buffer: buffer)
225
+ state = . accumulatingEvent( event, buffer: buffer, predicate : predicate )
197
226
return . noop
198
227
}
199
228
// Process the field.
@@ -214,11 +243,11 @@ extension ServerSentEventsDeserializationSequence.Iterator {
214
243
}
215
244
default :
216
245
// An unknown or invalid field, skip.
217
- state = . accumulatingEvent( event, buffer: buffer)
246
+ state = . accumulatingEvent( event, buffer: buffer, predicate : predicate )
218
247
return . noop
219
248
}
220
249
// Processed the field, continue.
221
- state = . accumulatingEvent( event, buffer: buffer)
250
+ state = . accumulatingEvent( event, buffer: buffer, predicate : predicate )
222
251
return . noop
223
252
case . finished: return . returnNil
224
253
case . mutating: preconditionFailure ( " Invalid state " )
@@ -240,11 +269,11 @@ extension ServerSentEventsDeserializationSequence.Iterator {
240
269
/// - Returns: An action to perform.
241
270
mutating func receivedValue( _ value: ArraySlice < UInt8 > ? ) -> ReceivedValueAction {
242
271
switch state {
243
- case . accumulatingEvent( let event, var buffer) :
272
+ case . accumulatingEvent( let event, var buffer, let predicate ) :
244
273
if let value {
245
274
state = . mutating
246
275
buffer. append ( value)
247
- state = . accumulatingEvent( event, buffer: buffer)
276
+ state = . accumulatingEvent( event, buffer: buffer, predicate : predicate )
248
277
return . noop
249
278
} else {
250
279
// If no value is received, drop the existing event on the floor.
0 commit comments