Skip to content

Commit d39399c

Browse files
authored
Merge pull request #25 from rameel/add-cancellation-token
Add CancellationToken parameter to FileTreeAsyncEnumerable
2 parents ae1faea + 5957417 commit d39399c

File tree

1 file changed

+44
-24
lines changed

1 file changed

+44
-24
lines changed

Ramstack.Globbing/Traversal/FileTreeAsyncEnumerable.cs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Ramstack.Globbing.Traversal;
1313
public 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

Comments
 (0)