@@ -13,6 +13,7 @@ namespace Ramstack.Globbing.Traversal;
1313public sealed class FileTreeAsyncEnumerable < TEntry , TResult > : IAsyncEnumerable < TResult >
1414{
1515 private readonly TEntry _directory ;
16+ private readonly CancellationToken _cancellationToken ;
1617
1718 /// <summary>
1819 /// Gets or sets the glob patterns to include in the enumeration.
@@ -59,40 +60,59 @@ public sealed class FileTreeAsyncEnumerable<TEntry, TResult> : IAsyncEnumerable<
5960 /// Initializes a new instance of the <see cref="FileTreeAsyncEnumerable{TEntry, TResult}"/> class.
6061 /// </summary>
6162 /// <param name="directory">The root directory to start the enumeration from.</param>
62- public FileTreeAsyncEnumerable ( TEntry directory ) =>
63+ /// <param name="cancellationToken">An optional cancellation token that may be used to cancel the asynchronous iteration.</param>
64+ public FileTreeAsyncEnumerable ( TEntry directory , CancellationToken cancellationToken = default )
65+ {
6366 _directory = directory ;
67+ _cancellationToken = cancellationToken ;
68+ }
6469
6570 /// <inheritdoc />
66- IAsyncEnumerator < TResult > IAsyncEnumerable < TResult > . GetAsyncEnumerator ( CancellationToken cancellationToken ) =>
67- EnumerateAsync ( cancellationToken ) . GetAsyncEnumerator ( cancellationToken ) ;
68-
69- private async IAsyncEnumerable < TResult > EnumerateAsync ( [ EnumeratorCancellation ] CancellationToken cancellationToken )
71+ IAsyncEnumerator < TResult > IAsyncEnumerable < TResult > . GetAsyncEnumerator ( CancellationToken cancellationToken )
7072 {
71- var chars = ArrayPool < char > . Shared . Rent ( 512 ) ;
73+ CancellationTokenSource ? source = null ;
7274
73- var stack = new Stack < ( TEntry Directory , string Path ) > ( ) ;
74- stack . Push ( ( _directory , "" ) ) ;
75+ if ( _cancellationToken != default )
76+ cancellationToken = cancellationToken != default
77+ ? ( source = CancellationTokenSource . CreateLinkedTokenSource ( _cancellationToken , cancellationToken ) ) . Token
78+ : _cancellationToken ;
7579
76- while ( stack . TryPop ( out var e ) )
77- {
78- await foreach ( var entry in ChildrenSelector ( e . Directory , cancellationToken ) )
79- {
80- var name = FileNameSelector ( entry ) ;
81- var fullName = FileTreeHelper . GetFullName ( ref chars , e . Path , name ) ;
80+ return EnumerateAsync ( source , cancellationToken ) . GetAsyncEnumerator ( cancellationToken ) ;
81+ }
8282
83- if ( PathHelper . IsMatch ( fullName , Excludes , Flags ) )
84- continue ;
83+ private async IAsyncEnumerable < TResult > EnumerateAsync ( CancellationTokenSource ? source , [ EnumeratorCancellation ] CancellationToken cancellationToken )
84+ {
85+ var chars = ArrayPool < char > . Shared . Rent ( 512 ) ;
8586
86- if ( ShouldRecursePredicate == null || ShouldRecursePredicate ( entry ) )
87- if ( PathHelper . IsPartialMatch ( fullName , Patterns , Flags ) )
88- stack . Push ( ( entry , fullName . ToString ( ) ) ) ;
87+ try
88+ {
89+ var stack = new Stack < ( TEntry Directory , string Path ) > ( ) ;
90+ stack . Push ( ( _directory , "" ) ) ;
8991
90- if ( ShouldIncludePredicate == null || ShouldIncludePredicate ( entry ) )
91- if ( PathHelper . IsMatch ( fullName , Patterns , Flags ) )
92- yield return ResultSelector ( entry ) ;
92+ while ( stack . TryPop ( out var e ) )
93+ {
94+ await foreach ( var entry in ChildrenSelector ( e . Directory , cancellationToken ) )
95+ {
96+ var name = FileNameSelector ( entry ) ;
97+ var fullName = FileTreeHelper . GetFullName ( ref chars , e . Path , name ) ;
98+
99+ if ( PathHelper . IsMatch ( fullName , Excludes , Flags ) )
100+ continue ;
101+
102+ if ( ShouldRecursePredicate == null || ShouldRecursePredicate ( entry ) )
103+ if ( PathHelper . IsPartialMatch ( fullName , Patterns , Flags ) )
104+ stack . Push ( ( entry , fullName . ToString ( ) ) ) ;
105+
106+ if ( ShouldIncludePredicate == null || ShouldIncludePredicate ( entry ) )
107+ if ( PathHelper . IsMatch ( fullName , Patterns , Flags ) )
108+ yield return ResultSelector ( entry ) ;
109+ }
93110 }
94111 }
95-
96- ArrayPool < char > . Shared . Return ( chars ) ;
112+ finally
113+ {
114+ ArrayPool < char > . Shared . Return ( chars ) ;
115+ source ? . Dispose ( ) ;
116+ }
97117 }
98118}
0 commit comments