@@ -325,4 +325,128 @@ public static ChannelReader<TOut> Pipe<TWrite, TRead, TOut>(this Channel<TWrite,
325325
326326 return Pipe ( source . Reader , transform , capacity , singleReader , cancellationToken ) ;
327327 }
328+
329+ /// <summary>
330+ /// <para>
331+ /// Reads all entries and filters the values using the <paramref name="predicate"/>
332+ /// function before buffering the results into another channel for consumption.
333+ /// </para>
334+ /// <para>
335+ /// If you do no need the unmatched items,
336+ /// use the <see cref="Filter{T}(ChannelReader{T}, Func{T, bool})"/> extension.
337+ /// </para>
338+ /// </summary>
339+ /// <typeparam name="T">The input type of the channel.</typeparam>
340+ /// <param name="source">The asynchronous source data to use.</param>
341+ /// <param name="unmatched">Channel containing the unmatched items</param>
342+ /// <param name="options">The settings to use for the created channels.</param>
343+ /// <param name="maxConcurrency">The maximum number of concurrent operations. Greater than 1 may likely cause results to be out of order.</param>
344+ /// <param name="predicate">Predicate to test against</param>
345+ /// <param name="cancellationToken">An optional cancellation token.</param>
346+ /// <returns>The <see cref="ChannelReader{T}"/> containing only the items that match the <paramref name="predicate"/>.</returns>
347+ /// <remarks>
348+ /// All items not matching the predicate are written to the <paramref name="unmatched"/> channel.
349+ /// </remarks>
350+ public static ChannelReader < T > PipeFilter < T > ( this ChannelReader < T > source ,
351+ out ChannelReader < T > unmatched ,
352+ ChannelOptions options ,
353+ int maxConcurrency ,
354+ Func < T , bool > predicate ,
355+ CancellationToken cancellationToken = default )
356+ {
357+ var singleWriter = maxConcurrency == 1 ;
358+
359+ var matchedChannel = CreateChannel < T > ( options ) ;
360+ var matchedWriter = matchedChannel . Writer ;
361+
362+ var unmatchedChannel = CreateChannel < T > ( options ) ;
363+ var unmatchedWriter = unmatchedChannel . Writer ;
364+
365+ source
366+ . ReadAllConcurrentlyAsync ( maxConcurrency , e =>
367+ {
368+ var writer = predicate ( e ) ? matchedWriter : unmatchedWriter ;
369+ return writer . WriteAsync ( e , cancellationToken ) ;
370+ } , cancellationToken )
371+ . ContinueWith ( t =>
372+ {
373+ unmatchedWriter . Complete ( t . Exception ) ;
374+ matchedWriter . Complete ( t . Exception ) ;
375+ } ,
376+ CancellationToken . None ,
377+ TaskContinuationOptions . ExecuteSynchronously ,
378+ TaskScheduler . Current ) ;
379+
380+ unmatched = unmatchedChannel . Reader ;
381+ return matchedChannel . Reader ;
382+ }
383+
384+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, ChannelOptions, int, Func{T, bool}, CancellationToken)"/>
385+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
386+ out ChannelReader < T > unmatched ,
387+ ChannelOptions options ,
388+ int maxConcurrency ,
389+ Func < T , ValueTask < bool > > predicate ,
390+ CancellationToken cancellationToken = default )
391+ {
392+ var singleWriter = maxConcurrency == 1 ;
393+
394+ var matchedChannel = CreateChannel < T > ( options ) ;
395+ var matchedWriter = matchedChannel . Writer ;
396+
397+ var unmatchedChannel = CreateChannel < T > ( options ) ;
398+ var unmatchedWriter = unmatchedChannel . Writer ;
399+
400+ source
401+ . ReadAllConcurrentlyAsync ( maxConcurrency , async e =>
402+ {
403+ var writer = await predicate ( e ) . ConfigureAwait ( false ) ? matchedWriter : unmatchedWriter ;
404+ await writer . WriteAsync ( e , cancellationToken ) . ConfigureAwait ( false ) ;
405+ } , cancellationToken )
406+ . ContinueWith ( t =>
407+ {
408+ unmatchedWriter . Complete ( t . Exception ) ;
409+ matchedWriter . Complete ( t . Exception ) ;
410+ } ,
411+ CancellationToken . None ,
412+ TaskContinuationOptions . ExecuteSynchronously ,
413+ TaskScheduler . Current ) ;
414+
415+ unmatched = unmatchedChannel . Reader ;
416+ return matchedChannel . Reader ;
417+ }
418+
419+ /// <param name="source">The asynchronous source data to use.</param>
420+ /// <param name="unmatched">Channel containing the unmatched items</param>
421+ /// <param name="capacity">
422+ /// <para>The width of the pipe: how many entries to buffer while waiting to be read from.</para>
423+ /// <para>Applies to both the matched (return) and <paramref name="unmatched"/> (out) channels.</para>
424+ /// <para>A value less that 1 will produce unbound channels.</para>
425+ /// </param>
426+ /// <param name="maxConcurrency">The maximum number of concurrent operations. Greater than 1 may likely cause results to be out of order.</param>
427+ /// <param name="predicate">Predicate to test against</param>
428+ /// <param name="cancellationToken">An optional cancellation token.</param>
429+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, ChannelOptions, int, Func{T, bool}, CancellationToken)"/>
430+ public static ChannelReader < T > PipeFilter < T > ( this ChannelReader < T > source ,
431+ out ChannelReader < T > unmatched ,
432+ int capacity ,
433+ int maxConcurrency ,
434+ Func < T , bool > predicate ,
435+ CancellationToken cancellationToken = default )
436+ {
437+ var options = CreateOptions ( capacity , false , false , maxConcurrency == 1 ) ;
438+ return PipeFilter ( source , out unmatched , options , maxConcurrency , predicate , cancellationToken ) ;
439+ }
440+
441+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, int, int, Func{T, bool}, CancellationToken)"/>
442+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
443+ out ChannelReader < T > unmatched ,
444+ int capacity ,
445+ int maxConcurrency ,
446+ Func < T , ValueTask < bool > > predicate ,
447+ CancellationToken cancellationToken = default )
448+ {
449+ var options = CreateOptions ( capacity , false , false , maxConcurrency == 1 ) ;
450+ return PipeFilterAsync ( source , out unmatched , options , maxConcurrency , predicate , cancellationToken ) ;
451+ }
328452}
0 commit comments