923 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			923 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Cysharp.Threading.Tasks.Internal;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| using System.Threading;
 | |
| 
 | |
| namespace Cysharp.Threading.Tasks.Linq
 | |
| {
 | |
|     public static partial class UniTaskAsyncEnumerable
 | |
|     {
 | |
|         // Ix-Async returns IGrouping but it is competely waste, use standard IGrouping.
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             return new GroupBy<TSource, TKey, TSource>(source, keySelector, x => x, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupBy<TSource, TKey, TSource>(source, keySelector, x => x, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             return new GroupBy<TSource, TKey, TElement>(source, keySelector, elementSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupBy<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupBy<TSource, TKey, TSource, TResult>(source, keySelector, x => x, resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupBy<TSource, TKey, TSource, TResult>(source, keySelector, x => x, resultSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupBy<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupBy<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         // await
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupByAwait<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             return new GroupByAwait<TSource, TKey, TSource>(source, keySelector, x => UniTask.FromResult(x), EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupByAwait<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwait<TSource, TKey, TSource>(source, keySelector, x => UniTask.FromResult(x), comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupByAwait<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             return new GroupByAwait<TSource, TKey, TElement>(source, keySelector, elementSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupByAwait<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwait<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwait<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TKey, IEnumerable<TSource>, UniTask<TResult>> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupByAwait<TSource, TKey, TSource, TResult>(source, keySelector, x => UniTask.FromResult(x), resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwait<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupByAwait<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwait<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TKey, IEnumerable<TSource>, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwait<TSource, TKey, TSource, TResult>(source, keySelector, x => UniTask.FromResult(x), resultSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwait<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwait<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         // with ct
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupByAwaitWithCancellation<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TSource>(source, keySelector, (x, _) => UniTask.FromResult(x), EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TSource>> GroupByAwaitWithCancellation<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TSource>(source, keySelector, (x, _) => UniTask.FromResult(x), comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupByAwaitWithCancellation<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>> GroupByAwaitWithCancellation<TSource, TKey, TElement>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwaitWithCancellation<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TKey, IEnumerable<TSource>, CancellationToken, UniTask<TResult>> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TSource, TResult>(source, keySelector, (x, _) => UniTask.FromResult(x), resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwaitWithCancellation<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, EqualityComparer<TKey>.Default);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwaitWithCancellation<TSource, TKey, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TKey, IEnumerable<TSource>, CancellationToken, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TSource, TResult>(source, keySelector, (x, _) => UniTask.FromResult(x), resultSelector, comparer);
 | |
|         }
 | |
| 
 | |
|         public static IUniTaskAsyncEnumerable<TResult> GroupByAwaitWithCancellation<TSource, TKey, TElement, TResult>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             Error.ThrowArgumentNullException(source, nameof(source));
 | |
|             Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
 | |
|             Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector));
 | |
|             Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
 | |
|             Error.ThrowArgumentNullException(comparer, nameof(comparer));
 | |
|             return new GroupByAwaitWithCancellation<TSource, TKey, TElement, TResult>(source, keySelector, elementSelector, resultSelector, comparer);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupBy<TSource, TKey, TElement> : IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, TKey> keySelector;
 | |
|         readonly Func<TSource, TElement> elementSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupBy(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupBy(source, keySelector, elementSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupBy : MoveNextSource, IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>>
 | |
|         {
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, TKey> keySelector;
 | |
|             readonly Func<TSource, TElement> elementSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
| 
 | |
|             public _GroupBy(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public IGrouping<TKey, TElement> Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         Current = groupEnumerator.Current as IGrouping<TKey, TElement>;
 | |
|                         completionSource.TrySetResult(true);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupBy<TSource, TKey, TElement, TResult> : IUniTaskAsyncEnumerable<TResult>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, TKey> keySelector;
 | |
|         readonly Func<TSource, TElement> elementSelector;
 | |
|         readonly Func<TKey, IEnumerable<TElement>, TResult> resultSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupBy(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.resultSelector = resultSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupBy(source, keySelector, elementSelector, resultSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupBy : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
 | |
|         {
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, TKey> keySelector;
 | |
|             readonly Func<TSource, TElement> elementSelector;
 | |
|             readonly Func<TKey, IEnumerable<TElement>, TResult> resultSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
| 
 | |
|             public _GroupBy(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.resultSelector = resultSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public TResult Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         var current = groupEnumerator.Current;
 | |
|                         Current = resultSelector(current.Key, current);
 | |
|                         completionSource.TrySetResult(true);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupByAwait<TSource, TKey, TElement> : IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, UniTask<TKey>> keySelector;
 | |
|         readonly Func<TSource, UniTask<TElement>> elementSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupByAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupByAwait(source, keySelector, elementSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupByAwait : MoveNextSource, IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>>
 | |
|         {
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, UniTask<TKey>> keySelector;
 | |
|             readonly Func<TSource, UniTask<TElement>> elementSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
| 
 | |
|             public _GroupByAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public IGrouping<TKey, TElement> Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAwaitAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         Current = groupEnumerator.Current as IGrouping<TKey, TElement>;
 | |
|                         completionSource.TrySetResult(true);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupByAwait<TSource, TKey, TElement, TResult> : IUniTaskAsyncEnumerable<TResult>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, UniTask<TKey>> keySelector;
 | |
|         readonly Func<TSource, UniTask<TElement>> elementSelector;
 | |
|         readonly Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupByAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.resultSelector = resultSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupByAwait(source, keySelector, elementSelector, resultSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupByAwait : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
 | |
|         {
 | |
|             readonly static Action<object> ResultSelectCoreDelegate = ResultSelectCore;
 | |
| 
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, UniTask<TKey>> keySelector;
 | |
|             readonly Func<TSource, UniTask<TElement>> elementSelector;
 | |
|             readonly Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
|             UniTask<TResult>.Awaiter awaiter;
 | |
| 
 | |
|             public _GroupByAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, Func<TSource, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.resultSelector = resultSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public TResult Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAwaitAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         var current = groupEnumerator.Current;
 | |
| 
 | |
|                         awaiter = resultSelector(current.Key, current).GetAwaiter();
 | |
|                         if (awaiter.IsCompleted)
 | |
|                         {
 | |
|                             ResultSelectCore(this);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             awaiter.SourceOnCompleted(ResultSelectCoreDelegate, this);
 | |
|                         }
 | |
|                         return;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             static void ResultSelectCore(object state)
 | |
|             {
 | |
|                 var self = (_GroupByAwait)state;
 | |
| 
 | |
|                 if (self.TryGetResult(self.awaiter, out var result))
 | |
|                 {
 | |
|                     self.Current = result;
 | |
|                     self.completionSource.TrySetResult(true);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupByAwaitWithCancellation<TSource, TKey, TElement> : IUniTaskAsyncEnumerable<IGrouping<TKey, TElement>>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
 | |
|         readonly Func<TSource, CancellationToken, UniTask<TElement>> elementSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupByAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupByAwaitWithCancellation(source, keySelector, elementSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupByAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<IGrouping<TKey, TElement>>
 | |
|         {
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
 | |
|             readonly Func<TSource, CancellationToken, UniTask<TElement>> elementSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
| 
 | |
|             public _GroupByAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public IGrouping<TKey, TElement> Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAwaitWithCancellationAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         Current = groupEnumerator.Current as IGrouping<TKey, TElement>;
 | |
|                         completionSource.TrySetResult(true);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     internal sealed class GroupByAwaitWithCancellation<TSource, TKey, TElement, TResult> : IUniTaskAsyncEnumerable<TResult>
 | |
|     {
 | |
|         readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|         readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
 | |
|         readonly Func<TSource, CancellationToken, UniTask<TElement>> elementSelector;
 | |
|         readonly Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector;
 | |
|         readonly IEqualityComparer<TKey> comparer;
 | |
| 
 | |
|         public GroupByAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
 | |
|         {
 | |
|             this.source = source;
 | |
|             this.keySelector = keySelector;
 | |
|             this.elementSelector = elementSelector;
 | |
|             this.resultSelector = resultSelector;
 | |
|             this.comparer = comparer;
 | |
|         }
 | |
| 
 | |
|         public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
 | |
|         {
 | |
|             return new _GroupByAwaitWithCancellation(source, keySelector, elementSelector, resultSelector, comparer, cancellationToken);
 | |
|         }
 | |
| 
 | |
|         sealed class _GroupByAwaitWithCancellation : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
 | |
|         {
 | |
|             readonly static Action<object> ResultSelectCoreDelegate = ResultSelectCore;
 | |
| 
 | |
|             readonly IUniTaskAsyncEnumerable<TSource> source;
 | |
|             readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
 | |
|             readonly Func<TSource, CancellationToken, UniTask<TElement>> elementSelector;
 | |
|             readonly Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector;
 | |
|             readonly IEqualityComparer<TKey> comparer;
 | |
|             CancellationToken cancellationToken;
 | |
| 
 | |
|             IEnumerator<IGrouping<TKey, TElement>> groupEnumerator;
 | |
|             UniTask<TResult>.Awaiter awaiter;
 | |
| 
 | |
|             public _GroupByAwaitWithCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, Func<TSource, CancellationToken, UniTask<TElement>> elementSelector, Func<TKey, IEnumerable<TElement>, CancellationToken, UniTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
 | |
|             {
 | |
|                 this.source = source;
 | |
|                 this.keySelector = keySelector;
 | |
|                 this.elementSelector = elementSelector;
 | |
|                 this.resultSelector = resultSelector;
 | |
|                 this.comparer = comparer;
 | |
|                 this.cancellationToken = cancellationToken;
 | |
|                 TaskTracker.TrackActiveTask(this, 3);
 | |
|             }
 | |
| 
 | |
|             public TResult Current { get; private set; }
 | |
| 
 | |
|             public UniTask<bool> MoveNextAsync()
 | |
|             {
 | |
|                 cancellationToken.ThrowIfCancellationRequested();
 | |
|                 completionSource.Reset();
 | |
| 
 | |
|                 if (groupEnumerator == null)
 | |
|                 {
 | |
|                     CreateLookup().Forget();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     SourceMoveNext();
 | |
|                 }
 | |
|                 return new UniTask<bool>(this, completionSource.Version);
 | |
|             }
 | |
| 
 | |
|             async UniTaskVoid CreateLookup()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     var lookup = await source.ToLookupAwaitWithCancellationAsync(keySelector, elementSelector, comparer, cancellationToken);
 | |
|                     groupEnumerator = lookup.GetEnumerator();
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|                 SourceMoveNext();
 | |
|             }
 | |
| 
 | |
|             void SourceMoveNext()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     if (groupEnumerator.MoveNext())
 | |
|                     {
 | |
|                         var current = groupEnumerator.Current;
 | |
| 
 | |
|                         awaiter = resultSelector(current.Key, current, cancellationToken).GetAwaiter();
 | |
|                         if (awaiter.IsCompleted)
 | |
|                         {
 | |
|                             ResultSelectCore(this);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             awaiter.SourceOnCompleted(ResultSelectCoreDelegate, this);
 | |
|                         }
 | |
|                         return;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         completionSource.TrySetResult(false);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     completionSource.TrySetException(ex);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             static void ResultSelectCore(object state)
 | |
|             {
 | |
|                 var self = (_GroupByAwaitWithCancellation)state;
 | |
| 
 | |
|                 if (self.TryGetResult(self.awaiter, out var result))
 | |
|                 {
 | |
|                     self.Current = result;
 | |
|                     self.completionSource.TrySetResult(true);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public UniTask DisposeAsync()
 | |
|             {
 | |
|                 TaskTracker.RemoveTracking(this);
 | |
|                 if (groupEnumerator != null)
 | |
|                 {
 | |
|                     groupEnumerator.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 return default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| } |