@@ -325,4 +325,160 @@ 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 not 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 <paramref name="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 > PipeFilter < T > ( this ChannelReader < T > source ,
386+ out ChannelReader < T > unmatched ,
387+ ChannelOptions options ,
388+ Func < T , bool > predicate ,
389+ CancellationToken cancellationToken = default )
390+ => PipeFilter ( source , out unmatched , options , 1 , predicate , cancellationToken ) ;
391+
392+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, ChannelOptions, int, Func{T, bool}, CancellationToken)"/>
393+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
394+ out ChannelReader < T > unmatched ,
395+ ChannelOptions options ,
396+ int maxConcurrency ,
397+ Func < T , ValueTask < bool > > predicate ,
398+ CancellationToken cancellationToken = default )
399+ {
400+ var singleWriter = maxConcurrency == 1 ;
401+
402+ var matchedChannel = CreateChannel < T > ( options ) ;
403+ var matchedWriter = matchedChannel . Writer ;
404+
405+ var unmatchedChannel = CreateChannel < T > ( options ) ;
406+ var unmatchedWriter = unmatchedChannel . Writer ;
407+
408+ source
409+ . ReadAllConcurrentlyAsync ( maxConcurrency , async e =>
410+ {
411+ var writer = await predicate ( e ) . ConfigureAwait ( false ) ? matchedWriter : unmatchedWriter ;
412+ await writer . WriteAsync ( e , cancellationToken ) . ConfigureAwait ( false ) ;
413+ } , cancellationToken )
414+ . ContinueWith ( t =>
415+ {
416+ unmatchedWriter . Complete ( t . Exception ) ;
417+ matchedWriter . Complete ( t . Exception ) ;
418+ } ,
419+ CancellationToken . None ,
420+ TaskContinuationOptions . ExecuteSynchronously ,
421+ TaskScheduler . Current ) ;
422+
423+ unmatched = unmatchedChannel . Reader ;
424+ return matchedChannel . Reader ;
425+ }
426+
427+ /// <inheritdoc cref="PipeFilterAsync{T}(ChannelReader{T}, out ChannelReader{T}, ChannelOptions, int, Func{T, ValueTask{bool}}, CancellationToken)"/>
428+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
429+ out ChannelReader < T > unmatched ,
430+ ChannelOptions options ,
431+ Func < T , ValueTask < bool > > predicate ,
432+ CancellationToken cancellationToken = default )
433+ => PipeFilterAsync ( source , out unmatched , options , 1 , predicate , cancellationToken ) ;
434+
435+ /// <param name="source">The asynchronous source data to use.</param>
436+ /// <param name="unmatched">Channel containing the unmatched items</param>
437+ /// <param name="capacity">
438+ /// <para>The width of the pipe: how many entries to buffer while waiting to be read from.</para>
439+ /// <para>Applies to both the matched (return) and <paramref name="unmatched"/> (out) channels.</para>
440+ /// <para>A value less that 1 will produce unbound channels.</para>
441+ /// </param>
442+ /// <param name="maxConcurrency">The maximum number of concurrent operations. Greater than 1 may likely cause results to be out of order.</param>
443+ /// <param name="predicate">Predicate to test against</param>
444+ /// <param name="cancellationToken">An optional cancellation token.</param>
445+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, ChannelOptions, int, Func{T, bool}, CancellationToken)"/>
446+ public static ChannelReader < T > PipeFilter < T > ( this ChannelReader < T > source ,
447+ out ChannelReader < T > unmatched ,
448+ int capacity ,
449+ int maxConcurrency ,
450+ Func < T , bool > predicate ,
451+ CancellationToken cancellationToken = default )
452+ {
453+ var options = CreateOptions ( capacity , false , false , maxConcurrency == 1 ) ;
454+ return PipeFilter ( source , out unmatched , options , maxConcurrency , predicate , cancellationToken ) ;
455+ }
456+
457+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, int, int, Func{T, bool}, CancellationToken)"/>
458+ public static ChannelReader < T > PipeFilter < T > ( this ChannelReader < T > source ,
459+ out ChannelReader < T > unmatched ,
460+ int capacity ,
461+ Func < T , bool > predicate ,
462+ CancellationToken cancellationToken = default )
463+ => PipeFilter ( source , out unmatched , capacity , 1 , predicate , cancellationToken ) ;
464+
465+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, int, int, Func{T, bool}, CancellationToken)"/>
466+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
467+ out ChannelReader < T > unmatched ,
468+ int capacity ,
469+ int maxConcurrency ,
470+ Func < T , ValueTask < bool > > predicate ,
471+ CancellationToken cancellationToken = default )
472+ {
473+ var options = CreateOptions ( capacity , false , false , maxConcurrency == 1 ) ;
474+ return PipeFilterAsync ( source , out unmatched , options , maxConcurrency , predicate , cancellationToken ) ;
475+ }
476+
477+ /// <inheritdoc cref="PipeFilter{T}(ChannelReader{T}, out ChannelReader{T}, int, int, Func{T, bool}, CancellationToken)"/>
478+ public static ChannelReader < T > PipeFilterAsync < T > ( this ChannelReader < T > source ,
479+ out ChannelReader < T > unmatched ,
480+ int capacity ,
481+ Func < T , ValueTask < bool > > predicate ,
482+ CancellationToken cancellationToken = default )
483+ => PipeFilterAsync ( source , out unmatched , capacity , 1 , predicate , cancellationToken ) ;
328484}
0 commit comments