From 4d13523df76e664afe29eeda86f54d5a929b86bf Mon Sep 17 00:00:00 2001 From: neuecc Date: Mon, 11 May 2020 02:01:49 +0900 Subject: [PATCH] SelectMany --- src/UniTask.NetCore/Linq/SelectMany.cs | 659 +++++++++++++++++++- src/UniTask.NetCore/Linq/_FileMaker.cs | 59 -- src/UniTask.NetCoreTests/Linq/Projection.cs | 119 ++++ 3 files changed, 752 insertions(+), 85 deletions(-) diff --git a/src/UniTask.NetCore/Linq/SelectMany.cs b/src/UniTask.NetCore/Linq/SelectMany.cs index da8924a..d286441 100644 --- a/src/UniTask.NetCore/Linq/SelectMany.cs +++ b/src/UniTask.NetCore/Linq/SelectMany.cs @@ -12,79 +12,124 @@ namespace Cysharp.Threading.Tasks.Linq Error.ThrowArgumentNullException(source, nameof(source)); Error.ThrowArgumentNullException(selector, nameof(selector)); - return new SelectMany(source, selector); + return new SelectMany(source, selector, (x, y) => y); } public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> selector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(selector, nameof(selector)); + + return new SelectMany(source, selector, (x, y) => y); } public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectMany(source, collectionSelector, resultSelector); } public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectMany(source, collectionSelector, resultSelector); } public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> selector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(selector, nameof(selector)); + + return new SelectManyAwait(source, selector, (x, y) => UniTask.FromResult(y)); } public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> selector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(selector, nameof(selector)); + + return new SelectManyAwait(source, selector, (x, y) => UniTask.FromResult(y)); } public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectManyAwait(source, collectionSelector, resultSelector); } public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectManyAwait(source, collectionSelector, resultSelector); } public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> selector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(selector, nameof(selector)); + + return new SelectManyAwaitWithCancellation(source, selector, (x, y, c) => UniTask.FromResult(y)); } public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> selector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(selector, nameof(selector)); + + return new SelectManyAwaitWithCancellation(source, selector, (x, y, c) => UniTask.FromResult(y)); } public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectManyAwaitWithCancellation(source, collectionSelector, resultSelector); } public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(collectionSelector, nameof(collectionSelector)); + + return new SelectManyAwaitWithCancellation(source, collectionSelector, resultSelector); } } - internal sealed class SelectMany : IUniTaskAsyncEnumerable + internal sealed class SelectMany : IUniTaskAsyncEnumerable { readonly IUniTaskAsyncEnumerable source; - readonly Func> selector; + readonly Func> selector1; + readonly Func> selector2; + readonly Func resultSelector; - public SelectMany(IUniTaskAsyncEnumerable source, Func> selector) + public SelectMany(IUniTaskAsyncEnumerable source, Func> selector, Func resultSelector) { this.source = source; - this.selector = selector; + this.selector1 = selector; + this.selector2 = null; + this.resultSelector = resultSelector; + } + + public SelectMany(IUniTaskAsyncEnumerable source, Func> selector, Func resultSelector) + { + this.source = source; + this.selector1 = null; + this.selector2 = selector; + this.resultSelector = resultSelector; } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - return new Enumerator(source, selector, cancellationToken); + return new Enumerator(source, selector1, selector2, resultSelector, cancellationToken); } sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator @@ -94,19 +139,26 @@ namespace Cysharp.Threading.Tasks.Linq static readonly Action selectedEnumeratorDisposeAsyncCoreDelegate = SelectedEnumeratorDisposeAsyncCore; readonly IUniTaskAsyncEnumerable source; - readonly Func> selector; + + readonly Func> selector1; + readonly Func> selector2; + readonly Func resultSelector; CancellationToken cancellationToken; + TSource sourceCurrent; + int sourceIndex; IUniTaskAsyncEnumerator sourceEnumerator; - IUniTaskAsyncEnumerator selectedEnumerator; + IUniTaskAsyncEnumerator selectedEnumerator; UniTask.Awaiter sourceAwaiter; UniTask.Awaiter selectedAwaiter; UniTask.Awaiter selectedDisposeAsyncAwaiter; - public Enumerator(IUniTaskAsyncEnumerable source, Func> selector, CancellationToken cancellationToken) + public Enumerator(IUniTaskAsyncEnumerable source, Func> selector1, Func> selector2, Func resultSelector, CancellationToken cancellationToken) { this.source = source; - this.selector = selector; + this.selector1 = selector1; + this.selector2 = selector2; + this.resultSelector = resultSelector; this.cancellationToken = cancellationToken; } @@ -182,14 +234,21 @@ namespace Cysharp.Threading.Tasks.Linq { var self = (Enumerator)state; - if (self.TryGetResult(self.selectedAwaiter, out var result)) + if (self.TryGetResult(self.sourceAwaiter, out var result)) { if (result) { try { - var current = self.sourceEnumerator.Current; - self.selectedEnumerator = self.selector(current).GetAsyncEnumerator(self.cancellationToken); + self.sourceCurrent = self.sourceEnumerator.Current; + if (self.selector1 != null) + { + self.selectedEnumerator = self.selector1(self.sourceCurrent).GetAsyncEnumerator(self.cancellationToken); + } + else + { + self.selectedEnumerator = self.selector2(self.sourceCurrent, checked(self.sourceIndex++)).GetAsyncEnumerator(self.cancellationToken); + } } catch (Exception ex) { @@ -216,7 +275,7 @@ namespace Cysharp.Threading.Tasks.Linq { try { - self.Current = self.selectedEnumerator.Current; + self.Current = self.resultSelector(self.sourceCurrent, self.selectedEnumerator.Current); } catch (Exception ex) { @@ -276,4 +335,552 @@ namespace Cysharp.Threading.Tasks.Linq } } } -} + + internal sealed class SelectManyAwait : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func>> selector1; + readonly Func>> selector2; + readonly Func> resultSelector; + + public SelectManyAwait(IUniTaskAsyncEnumerable source, Func>> selector, Func> resultSelector) + { + this.source = source; + this.selector1 = selector; + this.selector2 = null; + this.resultSelector = resultSelector; + } + + public SelectManyAwait(IUniTaskAsyncEnumerable source, Func>> selector, Func> resultSelector) + { + this.source = source; + this.selector1 = null; + this.selector2 = selector; + this.resultSelector = resultSelector; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, selector1, selector2, resultSelector, cancellationToken); + } + + sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator + { + static readonly Action sourceMoveNextCoreDelegate = SourceMoveNextCore; + static readonly Action selectedSourceMoveNextCoreDelegate = SeletedSourceMoveNextCore; + static readonly Action selectedEnumeratorDisposeAsyncCoreDelegate = SelectedEnumeratorDisposeAsyncCore; + static readonly Action selectorAwaitCoreDelegate = SelectorAwaitCore; + static readonly Action resultSelectorAwaitCoreDelegate = ResultSelectorAwaitCore; + + readonly IUniTaskAsyncEnumerable source; + + readonly Func>> selector1; + readonly Func>> selector2; + readonly Func> resultSelector; + CancellationToken cancellationToken; + + TSource sourceCurrent; + int sourceIndex; + IUniTaskAsyncEnumerator sourceEnumerator; + IUniTaskAsyncEnumerator selectedEnumerator; + UniTask.Awaiter sourceAwaiter; + UniTask.Awaiter selectedAwaiter; + UniTask.Awaiter selectedDisposeAsyncAwaiter; + + // await additional + UniTask>.Awaiter collectionSelectorAwaiter; + UniTask.Awaiter resultSelectorAwaiter; + + public Enumerator(IUniTaskAsyncEnumerable source, Func>> selector1, Func>> selector2, Func> resultSelector, CancellationToken cancellationToken) + { + this.source = source; + this.selector1 = selector1; + this.selector2 = selector2; + this.resultSelector = resultSelector; + this.cancellationToken = cancellationToken; + } + + public TResult Current { get; private set; } + + public UniTask MoveNextAsync() + { + completionSource.Reset(); + + // iterate selected field + if (selectedEnumerator != null) + { + MoveNextSelected(); + } + else + { + // iterate source field + if (sourceEnumerator == null) + { + sourceEnumerator = source.GetAsyncEnumerator(cancellationToken); + } + MoveNextSource(); + } + + return new UniTask(this, completionSource.Version); + } + + void MoveNextSource() + { + try + { + sourceAwaiter = sourceEnumerator.MoveNextAsync().GetAwaiter(); + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + return; + } + + if (sourceAwaiter.IsCompleted) + { + SourceMoveNextCore(this); + } + else + { + sourceAwaiter.SourceOnCompleted(sourceMoveNextCoreDelegate, this); + } + } + + void MoveNextSelected() + { + try + { + selectedAwaiter = selectedEnumerator.MoveNextAsync().GetAwaiter(); + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + return; + } + + if (selectedAwaiter.IsCompleted) + { + SeletedSourceMoveNextCore(this); + } + else + { + selectedAwaiter.SourceOnCompleted(selectedSourceMoveNextCoreDelegate, this); + } + } + + static void SourceMoveNextCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.sourceAwaiter, out var result)) + { + if (result) + { + try + { + self.sourceCurrent = self.sourceEnumerator.Current; + + if (self.selector1 != null) + { + self.collectionSelectorAwaiter = self.selector1(self.sourceCurrent).GetAwaiter(); + } + else + { + self.collectionSelectorAwaiter = self.selector2(self.sourceCurrent, checked(self.sourceIndex++)).GetAwaiter(); + } + + if (self.collectionSelectorAwaiter.IsCompleted) + { + SelectorAwaitCore(self); + } + else + { + self.collectionSelectorAwaiter.SourceOnCompleted(selectorAwaitCoreDelegate, self); + } + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + } + else + { + self.completionSource.TrySetResult(false); + } + } + } + + static void SeletedSourceMoveNextCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.selectedAwaiter, out var result)) + { + if (result) + { + try + { + self.resultSelectorAwaiter = self.resultSelector(self.sourceCurrent, self.selectedEnumerator.Current).GetAwaiter(); + if (self.resultSelectorAwaiter.IsCompleted) + { + ResultSelectorAwaitCore(self); + } + else + { + self.resultSelectorAwaiter.SourceOnCompleted(resultSelectorAwaitCoreDelegate, self); + } + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + } + else + { + // dispose selected source and try iterate source. + try + { + self.selectedDisposeAsyncAwaiter = self.selectedEnumerator.DisposeAsync().GetAwaiter(); + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + if (self.selectedDisposeAsyncAwaiter.IsCompleted) + { + SelectedEnumeratorDisposeAsyncCore(self); + } + else + { + self.selectedDisposeAsyncAwaiter.SourceOnCompleted(selectedEnumeratorDisposeAsyncCoreDelegate, self); + } + } + } + } + + static void SelectedEnumeratorDisposeAsyncCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.selectedDisposeAsyncAwaiter)) + { + self.selectedEnumerator = null; + self.selectedAwaiter = default; + + self.MoveNextSource(); // iterate next source + } + } + + static void SelectorAwaitCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.collectionSelectorAwaiter, out var result)) + { + self.selectedEnumerator = result.GetAsyncEnumerator(self.cancellationToken); + self.MoveNextSelected(); // iterated selected source. + } + } + + static void ResultSelectorAwaitCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.resultSelectorAwaiter, out var result)) + { + self.Current = result; + self.completionSource.TrySetResult(true); + } + } + + public async UniTask DisposeAsync() + { + if (selectedEnumerator != null) + { + await selectedEnumerator.DisposeAsync(); + } + if (sourceEnumerator != null) + { + await sourceEnumerator.DisposeAsync(); + } + } + } + } + + internal sealed class SelectManyAwaitWithCancellation : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func>> selector1; + readonly Func>> selector2; + readonly Func> resultSelector; + + public SelectManyAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func>> selector, Func> resultSelector) + { + this.source = source; + this.selector1 = selector; + this.selector2 = null; + this.resultSelector = resultSelector; + } + + public SelectManyAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func>> selector, Func> resultSelector) + { + this.source = source; + this.selector1 = null; + this.selector2 = selector; + this.resultSelector = resultSelector; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, selector1, selector2, resultSelector, cancellationToken); + } + + sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator + { + static readonly Action sourceMoveNextCoreDelegate = SourceMoveNextCore; + static readonly Action selectedSourceMoveNextCoreDelegate = SeletedSourceMoveNextCore; + static readonly Action selectedEnumeratorDisposeAsyncCoreDelegate = SelectedEnumeratorDisposeAsyncCore; + static readonly Action selectorAwaitCoreDelegate = SelectorAwaitCore; + static readonly Action resultSelectorAwaitCoreDelegate = ResultSelectorAwaitCore; + + readonly IUniTaskAsyncEnumerable source; + + readonly Func>> selector1; + readonly Func>> selector2; + readonly Func> resultSelector; + CancellationToken cancellationToken; + + TSource sourceCurrent; + int sourceIndex; + IUniTaskAsyncEnumerator sourceEnumerator; + IUniTaskAsyncEnumerator selectedEnumerator; + UniTask.Awaiter sourceAwaiter; + UniTask.Awaiter selectedAwaiter; + UniTask.Awaiter selectedDisposeAsyncAwaiter; + + // await additional + UniTask>.Awaiter collectionSelectorAwaiter; + UniTask.Awaiter resultSelectorAwaiter; + + public Enumerator(IUniTaskAsyncEnumerable source, Func>> selector1, Func>> selector2, Func> resultSelector, CancellationToken cancellationToken) + { + this.source = source; + this.selector1 = selector1; + this.selector2 = selector2; + this.resultSelector = resultSelector; + this.cancellationToken = cancellationToken; + } + + public TResult Current { get; private set; } + + public UniTask MoveNextAsync() + { + completionSource.Reset(); + + // iterate selected field + if (selectedEnumerator != null) + { + MoveNextSelected(); + } + else + { + // iterate source field + if (sourceEnumerator == null) + { + sourceEnumerator = source.GetAsyncEnumerator(cancellationToken); + } + MoveNextSource(); + } + + return new UniTask(this, completionSource.Version); + } + + void MoveNextSource() + { + try + { + sourceAwaiter = sourceEnumerator.MoveNextAsync().GetAwaiter(); + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + return; + } + + if (sourceAwaiter.IsCompleted) + { + SourceMoveNextCore(this); + } + else + { + sourceAwaiter.SourceOnCompleted(sourceMoveNextCoreDelegate, this); + } + } + + void MoveNextSelected() + { + try + { + selectedAwaiter = selectedEnumerator.MoveNextAsync().GetAwaiter(); + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + return; + } + + if (selectedAwaiter.IsCompleted) + { + SeletedSourceMoveNextCore(this); + } + else + { + selectedAwaiter.SourceOnCompleted(selectedSourceMoveNextCoreDelegate, this); + } + } + + static void SourceMoveNextCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.sourceAwaiter, out var result)) + { + if (result) + { + try + { + self.sourceCurrent = self.sourceEnumerator.Current; + + if (self.selector1 != null) + { + self.collectionSelectorAwaiter = self.selector1(self.sourceCurrent, self.cancellationToken).GetAwaiter(); + } + else + { + self.collectionSelectorAwaiter = self.selector2(self.sourceCurrent, checked(self.sourceIndex++), self.cancellationToken).GetAwaiter(); + } + + if (self.collectionSelectorAwaiter.IsCompleted) + { + SelectorAwaitCore(self); + } + else + { + self.collectionSelectorAwaiter.SourceOnCompleted(selectorAwaitCoreDelegate, self); + } + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + } + else + { + self.completionSource.TrySetResult(false); + } + } + } + + static void SeletedSourceMoveNextCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.selectedAwaiter, out var result)) + { + if (result) + { + try + { + self.resultSelectorAwaiter = self.resultSelector(self.sourceCurrent, self.selectedEnumerator.Current, self.cancellationToken).GetAwaiter(); + if (self.resultSelectorAwaiter.IsCompleted) + { + ResultSelectorAwaitCore(self); + } + else + { + self.resultSelectorAwaiter.SourceOnCompleted(resultSelectorAwaitCoreDelegate, self); + } + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + } + else + { + // dispose selected source and try iterate source. + try + { + self.selectedDisposeAsyncAwaiter = self.selectedEnumerator.DisposeAsync().GetAwaiter(); + } + catch (Exception ex) + { + self.completionSource.TrySetException(ex); + return; + } + if (self.selectedDisposeAsyncAwaiter.IsCompleted) + { + SelectedEnumeratorDisposeAsyncCore(self); + } + else + { + self.selectedDisposeAsyncAwaiter.SourceOnCompleted(selectedEnumeratorDisposeAsyncCoreDelegate, self); + } + } + } + } + + static void SelectedEnumeratorDisposeAsyncCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.selectedDisposeAsyncAwaiter)) + { + self.selectedEnumerator = null; + self.selectedAwaiter = default; + + self.MoveNextSource(); // iterate next source + } + } + + static void SelectorAwaitCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.collectionSelectorAwaiter, out var result)) + { + self.selectedEnumerator = result.GetAsyncEnumerator(self.cancellationToken); + self.MoveNextSelected(); // iterated selected source. + } + } + + static void ResultSelectorAwaitCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResult(self.resultSelectorAwaiter, out var result)) + { + self.Current = result; + self.completionSource.TrySetResult(true); + } + } + + public async UniTask DisposeAsync() + { + if (selectedEnumerator != null) + { + await selectedEnumerator.DisposeAsync(); + } + if (sourceEnumerator != null) + { + await sourceEnumerator.DisposeAsync(); + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/_FileMaker.cs b/src/UniTask.NetCore/Linq/_FileMaker.cs index 14d51ad..e04d089 100644 --- a/src/UniTask.NetCore/Linq/_FileMaker.cs +++ b/src/UniTask.NetCore/Linq/_FileMaker.cs @@ -307,65 +307,6 @@ namespace ___Dummy - public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> collectionSelector, Func resultSelector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectMany(this IUniTaskAsyncEnumerable source, Func> collectionSelector, Func resultSelector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwait(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SelectManyAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) - { - throw new NotImplementedException(); - } public static IUniTaskAsyncEnumerable Skip(this IUniTaskAsyncEnumerable source, Int32 count) diff --git a/src/UniTask.NetCoreTests/Linq/Projection.cs b/src/UniTask.NetCoreTests/Linq/Projection.cs index 49f3017..2b411b2 100644 --- a/src/UniTask.NetCoreTests/Linq/Projection.cs +++ b/src/UniTask.NetCoreTests/Linq/Projection.cs @@ -36,5 +36,124 @@ namespace NetCoreTests.Linq await Assert.ThrowsAsync(async () => await xs); } } + + [Theory] + [InlineData(0, 9)] // empty + exists + [InlineData(9, 0)] // exists + empty + [InlineData(9, 9)] // exists + exists + public async Task SelectMany(int leftCount, int rightCount) + { + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => x * y).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount), (x, y) => x * y).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + + // await + + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount))).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount))).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + + // with cancel + + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount))).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount))).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount)).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync(); + var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount), (x, y) => x * y).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + } + + [Fact] + public async Task SelectManyException() + { + // error + exists + // exists + error + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SelectMany(x => UniTaskAsyncEnumerable.Range(0, 1)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectMany(x => item).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + + // await + + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwait(x => UniTask.Run(() => item)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + + // with c + + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => item)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + } + + } }