diff --git a/src/UniTask.NetCore/Linq/AsyncEnumeratorBase.cs b/src/UniTask.NetCore/Linq/AsyncEnumeratorBase.cs index d9ccd84..d5e5b4f 100644 --- a/src/UniTask.NetCore/Linq/AsyncEnumeratorBase.cs +++ b/src/UniTask.NetCore/Linq/AsyncEnumeratorBase.cs @@ -201,7 +201,7 @@ namespace Cysharp.Threading.Tasks.Linq // abstract protected abstract UniTask TransformAsync(TSource sourceCurrent); - protected abstract bool TrySetCurrentCore(TAwait awaitResult); + protected abstract bool TrySetCurrentCore(TAwait awaitResult, out bool terminateIteration); // Util protected TSource SourceCurrent => enumerator.Current; @@ -282,7 +282,13 @@ namespace Cysharp.Threading.Tasks.Linq var task = TransformAsync(enumerator.Current); if (UnwarapTask(task, out var taskResult)) { - return ActionCompleted(TrySetCurrentCore(taskResult), out result); + var currentResult = TrySetCurrentCore(taskResult, out var terminateIteration); + if (terminateIteration) + { + return IterateFinished(out result); + } + + return ActionCompleted(currentResult, out result); } else { @@ -345,10 +351,11 @@ namespace Cysharp.Threading.Tasks.Linq var self = (AsyncEnumeratorAwaitSelectorBase)state; bool doneSetCurrent; + bool terminateIteration; try { var result = self.resultAwaiter.GetResult(); - doneSetCurrent = self.TrySetCurrentCore(result); + doneSetCurrent = self.TrySetCurrentCore(result, out terminateIteration); } catch (Exception ex) { @@ -368,7 +375,14 @@ namespace Cysharp.Threading.Tasks.Linq } else { - self.SourceMoveNext(); + if (terminateIteration) + { + self.completionSource.TrySetResult(false); + } + else + { + self.SourceMoveNext(); + } } } } diff --git a/src/UniTask.NetCore/Linq/Do.cs b/src/UniTask.NetCore/Linq/Do.cs new file mode 100644 index 0000000..a5565fb --- /dev/null +++ b/src/UniTask.NetCore/Linq/Do.cs @@ -0,0 +1,255 @@ +using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks.Internal; +using Cysharp.Threading.Tasks.Linq; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Cysharp.Threading.Tasks.Linq +{ + public static partial class UniTaskAsyncEnumerable + { + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Action onNext) + { + Error.ThrowArgumentNullException(source, nameof(source)); + return source.Do(onNext, null, null); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Action onNext, Action onError) + { + Error.ThrowArgumentNullException(source, nameof(source)); + return source.Do(onNext, onError, null); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Action onNext, Action onCompleted) + { + Error.ThrowArgumentNullException(source, nameof(source)); + return source.Do(onNext, null, onCompleted); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Action onNext, Action onError, Action onCompleted) + { + Error.ThrowArgumentNullException(source, nameof(source)); + return new Do(source, onNext, onError, onCompleted); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, IObserver observer) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(observer, nameof(observer)); + + return source.Do(observer.OnNext, observer.OnError, observer.OnCompleted); // alloc delegate. + } + + // TODO:Rename -> DoAwait + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onError) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onCompleted) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onError, Func onCompleted) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onError) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onCompleted) + { + throw new NotImplementedException(); + } + + public static IUniTaskAsyncEnumerable Do(this IUniTaskAsyncEnumerable source, Func onNext, Func onError, Func onCompleted) + { + throw new NotImplementedException(); + } + } + + internal sealed class Do : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Action onNext; + readonly Action onError; + readonly Action onCompleted; + + public Do(IUniTaskAsyncEnumerable source, Action onNext, Action onError, Action onCompleted) + { + this.source = source; + this.onNext = onNext; + this.onError = onError; + this.onCompleted = onCompleted; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, onNext, onError, onCompleted, cancellationToken); + } + + sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator + { + static readonly Action MoveNextCoreDelegate = MoveNextCore; + + readonly IUniTaskAsyncEnumerable source; + readonly Action onNext; + readonly Action onError; + readonly Action onCompleted; + CancellationToken cancellationToken; + + IUniTaskAsyncEnumerator enumerator; + UniTask.Awaiter awaiter; + + public Enumerator(IUniTaskAsyncEnumerable source, Action onNext, Action onError, Action onCompleted, CancellationToken cancellationToken) + { + this.source = source; + this.onNext = onNext; + this.onError = onError; + this.onCompleted = onCompleted; + this.cancellationToken = cancellationToken; + } + + public TSource Current { get; private set; } + + + public UniTask MoveNextAsync() + { + cancellationToken.ThrowIfCancellationRequested(); + completionSource.Reset(); + + bool isCompleted = false; + try + { + if (enumerator == null) + { + enumerator = source.GetAsyncEnumerator(cancellationToken); + } + + awaiter = enumerator.MoveNextAsync().GetAwaiter(); + isCompleted = awaiter.IsCompleted; + } + catch (Exception ex) + { + CallTrySetExceptionAfterNotification(ex); + return new UniTask(this, completionSource.Version); + } + + if (isCompleted) + { + MoveNextCore(this); + } + else + { + awaiter.SourceOnCompleted(MoveNextCoreDelegate, this); + } + + return new UniTask(this, completionSource.Version); + } + + void CallTrySetExceptionAfterNotification(Exception ex) + { + if (onError != null) + { + try + { + onError(ex); + } + catch (Exception ex2) + { + completionSource.TrySetException(ex2); + return; + } + } + + completionSource.TrySetException(ex); + } + + bool TryGetResultWithNotification(UniTask.Awaiter awaiter, out T result) + { + try + { + result = awaiter.GetResult(); + return true; + } + catch (Exception ex) + { + CallTrySetExceptionAfterNotification(ex); + result = default; + return false; + } + } + + + static void MoveNextCore(object state) + { + var self = (Enumerator)state; + + if (self.TryGetResultWithNotification(self.awaiter, out var result)) + { + if (result) + { + var v = self.enumerator.Current; + + if (self.onNext != null) + { + try + { + self.onNext(v); + } + catch (Exception ex) + { + self.CallTrySetExceptionAfterNotification(ex); + } + } + + self.Current = v; + self.completionSource.TrySetResult(true); + } + else + { + if (self.onCompleted != null) + { + try + { + self.onCompleted(); + } + catch (Exception ex) + { + self.CallTrySetExceptionAfterNotification(ex); + return; + } + } + + self.completionSource.TrySetResult(false); + } + } + } + + public UniTask DisposeAsync() + { + if (enumerator != null) + { + return enumerator.DisposeAsync(); + } + return default; + } + } + } +} diff --git a/src/UniTask.NetCore/Linq/Select.cs b/src/UniTask.NetCore/Linq/Select.cs index ca1479f..d541fda 100644 --- a/src/UniTask.NetCore/Linq/Select.cs +++ b/src/UniTask.NetCore/Linq/Select.cs @@ -173,9 +173,10 @@ namespace Cysharp.Threading.Tasks.Linq return selector(sourceCurrent); } - protected override bool TrySetCurrentCore(TResult awaitResult) + protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration) { Current = awaitResult; + terminateIteration= false; return true; } } @@ -213,9 +214,10 @@ namespace Cysharp.Threading.Tasks.Linq return selector(sourceCurrent, checked(index++)); } - protected override bool TrySetCurrentCore(TResult awaitResult) + protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration) { Current = awaitResult; + terminateIteration= false; return true; } } @@ -252,9 +254,10 @@ namespace Cysharp.Threading.Tasks.Linq return selector(sourceCurrent, cancellationToken); } - protected override bool TrySetCurrentCore(TResult awaitResult) + protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration) { Current = awaitResult; + terminateIteration= false; return true; } } @@ -292,9 +295,10 @@ namespace Cysharp.Threading.Tasks.Linq return selector(sourceCurrent, checked(index++), cancellationToken); } - protected override bool TrySetCurrentCore(TResult awaitResult) + protected override bool TrySetCurrentCore(TResult awaitResult, out bool terminateIteration) { Current = awaitResult; + terminateIteration= false; return true; } } diff --git a/src/UniTask.NetCore/Linq/Skip.cs b/src/UniTask.NetCore/Linq/Skip.cs index 2ba8c9e..8c46119 100644 --- a/src/UniTask.NetCore/Linq/Skip.cs +++ b/src/UniTask.NetCore/Linq/Skip.cs @@ -1,775 +1,69 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class Skip + public static partial class UniTaskAsyncEnumerable { + public static IUniTaskAsyncEnumerable Skip(this IUniTaskAsyncEnumerable source, Int32 count) + { + Error.ThrowArgumentNullException(source, nameof(source)); + + return new Skip(source, count); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal sealed class Skip : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly int count; + + public Skip(IUniTaskAsyncEnumerable source, int count) + { + this.source = source; + this.count = count; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, count, cancellationToken); + } + + sealed class Enumerator : AsyncEnumeratorBase + { + readonly int count; + + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, int count, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.count = count; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (count <= checked(index++)) + { + Current = SourceCurrent; + result = true; + return true; + } + else + { + result = default; + return false; + } + } + else + { + result = false; + return true; + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/SkipWhile.cs b/src/UniTask.NetCore/Linq/SkipWhile.cs index cedbcef..ec97e42 100644 --- a/src/UniTask.NetCore/Linq/SkipWhile.cs +++ b/src/UniTask.NetCore/Linq/SkipWhile.cs @@ -1,775 +1,379 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class SkipWhile + public static partial class UniTaskAsyncEnumerable { + public static IUniTaskAsyncEnumerable SkipWhile(this IUniTaskAsyncEnumerable source, Func predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhile(source, predicate); + } + + public static IUniTaskAsyncEnumerable SkipWhile(this IUniTaskAsyncEnumerable source, Func predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhileInt(source, predicate); + } + + public static IUniTaskAsyncEnumerable SkipWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhileAwait(source, predicate); + } + + public static IUniTaskAsyncEnumerable SkipWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhileIntAwait(source, predicate); + } + + public static IUniTaskAsyncEnumerable SkipWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhileAwaitWithCancellation(source, predicate); + } + + public static IUniTaskAsyncEnumerable SkipWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new SkipWhileIntAwaitWithCancellation(source, predicate); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal sealed class SkipWhile : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func predicate; + + public SkipWhile(IUniTaskAsyncEnumerable source, Func predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorBase + { + Func predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (predicate == null || !predicate(SourceCurrent)) + { + predicate = null; + Current = SourceCurrent; + result = true; + return true; + } + else + { + result = default; + return false; + } + } + + result = false; + return true; + } + } + } + + internal sealed class SkipWhileInt : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func predicate; + + public SkipWhileInt(IUniTaskAsyncEnumerable source, Func predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorBase + { + Func predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (predicate == null || !predicate(SourceCurrent, checked(index++))) + { + predicate = null; + Current = SourceCurrent; + result = true; + return true; + } + else + { + result = default; + return false; + } + } + + result = false; + return true; + } + } + } + + internal sealed class SkipWhileAwait : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public SkipWhileAwait(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + if (predicate == null) + { + return CompletedTasks.False; + } + + return predicate(sourceCurrent); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + if (!awaitResult) + { + predicate = null; + Current = SourceCurrent; + terminateIteration= false; + return true; + } + else + { + terminateIteration= false; + return false; + } + } + } + } + + internal sealed class SkipWhileIntAwait : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public SkipWhileIntAwait(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + if (predicate == null) + { + return CompletedTasks.False; + } + + return predicate(sourceCurrent, checked(index++)); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + terminateIteration= false; + if (!awaitResult) + { + predicate = null; + Current = SourceCurrent; + return true; + } + else + { + return false; + } + } + } + } + + internal sealed class SkipWhileAwaitWithCancellation : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public SkipWhileAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + if (predicate == null) + { + return CompletedTasks.False; + } + + return predicate(sourceCurrent, cancellationToken); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + terminateIteration= false; + if (!awaitResult) + { + predicate = null; + Current = SourceCurrent; + return true; + } + else + { + return false; + } + } + } + } + + internal sealed class SkipWhileIntAwaitWithCancellation : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public SkipWhileIntAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + if (predicate == null) + { + return CompletedTasks.False; + } + + return predicate(sourceCurrent, checked(index++), cancellationToken); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + terminateIteration= false; + if (!awaitResult) + { + predicate = null; + Current = SourceCurrent; + return true; + } + else + { + return false; + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/Take.cs b/src/UniTask.NetCore/Linq/Take.cs index 901720e..d632b1c 100644 --- a/src/UniTask.NetCore/Linq/Take.cs +++ b/src/UniTask.NetCore/Linq/Take.cs @@ -1,775 +1,69 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class Take + public static partial class UniTaskAsyncEnumerable { + public static IUniTaskAsyncEnumerable Take(this IUniTaskAsyncEnumerable source, Int32 count) + { + Error.ThrowArgumentNullException(source, nameof(source)); + + return new Take(source, count); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal sealed class Take : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly int count; + + public Take(IUniTaskAsyncEnumerable source, int count) + { + this.source = source; + this.count = count; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, count, cancellationToken); + } + + sealed class Enumerator : AsyncEnumeratorBase + { + readonly int count; + + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, int count, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.count = count; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (checked(index++) < count) + { + Current = SourceCurrent; + result = true; + return true; + } + else + { + result = false; + return true; + } + } + else + { + result = false; + return true; + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/TakeWhile.cs b/src/UniTask.NetCore/Linq/TakeWhile.cs index 4445869..8e98292 100644 --- a/src/UniTask.NetCore/Linq/TakeWhile.cs +++ b/src/UniTask.NetCore/Linq/TakeWhile.cs @@ -1,775 +1,342 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class TakeWhile + public static partial class UniTaskAsyncEnumerable { + public static IUniTaskAsyncEnumerable TakeWhile(this IUniTaskAsyncEnumerable source, Func predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhile(source, predicate); + } + + public static IUniTaskAsyncEnumerable TakeWhile(this IUniTaskAsyncEnumerable source, Func predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhileInt(source, predicate); + } + + public static IUniTaskAsyncEnumerable TakeWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhileAwait(source, predicate); + } + + public static IUniTaskAsyncEnumerable TakeWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhileIntAwait(source, predicate); + } + + public static IUniTaskAsyncEnumerable TakeWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhileAwaitWithCancellation(source, predicate); + } + + public static IUniTaskAsyncEnumerable TakeWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(predicate, nameof(predicate)); + + return new TakeWhileIntAwaitWithCancellation(source, predicate); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal sealed class TakeWhile : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func predicate; + + public TakeWhile(IUniTaskAsyncEnumerable source, Func predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorBase + { + Func predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (predicate(SourceCurrent)) + { + Current = SourceCurrent; + result = true; + return true; + } + } + + result = false; + return true; + } + } + } + + internal sealed class TakeWhileInt : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func predicate; + + public TakeWhileInt(IUniTaskAsyncEnumerable source, Func predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorBase + { + readonly Func predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func predicate, CancellationToken cancellationToken) + + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result) + { + if (sourceHasCurrent) + { + if (predicate(SourceCurrent, checked(index++))) + { + Current = SourceCurrent; + result = true; + return true; + } + } + + result = false; + return true; + } + } + } + + internal sealed class TakeWhileAwait : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public TakeWhileAwait(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + return predicate(sourceCurrent); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + if (awaitResult) + { + Current = SourceCurrent; + terminateIteration = false; + return true; + } + else + { + terminateIteration = true; + return false; + } + } + } + } + + internal sealed class TakeWhileIntAwait : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public TakeWhileIntAwait(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + readonly Func> predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + return predicate(sourceCurrent, checked(index++)); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + if (awaitResult) + { + Current = SourceCurrent; + terminateIteration = false; + return true; + } + else + { + terminateIteration = true; + return false; + } + } + } + } + + internal sealed class TakeWhileAwaitWithCancellation : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public TakeWhileAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + Func> predicate; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + return predicate(sourceCurrent, cancellationToken); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + if (awaitResult) + { + Current = SourceCurrent; + terminateIteration = false; + return true; + } + else + { + terminateIteration = true; + return false; + } + } + } + } + + internal sealed class TakeWhileIntAwaitWithCancellation : IUniTaskAsyncEnumerable + { + readonly IUniTaskAsyncEnumerable source; + readonly Func> predicate; + + public TakeWhileIntAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func> predicate) + { + this.source = source; + this.predicate = predicate; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(source, predicate, cancellationToken); + } + + class Enumerator : AsyncEnumeratorAwaitSelectorBase + { + readonly Func> predicate; + int index; + + public Enumerator(IUniTaskAsyncEnumerable source, Func> predicate, CancellationToken cancellationToken) + : base(source, cancellationToken) + { + this.predicate = predicate; + } + + protected override UniTask TransformAsync(TSource sourceCurrent) + { + return predicate(sourceCurrent, checked(index++), cancellationToken); + } + + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) + { + if (awaitResult) + { + Current = SourceCurrent; + terminateIteration = false; + return true; + } + else + { + terminateIteration = true; + return false; + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/Where.cs b/src/UniTask.NetCore/Linq/Where.cs index 05a1ed7..6b1cdd6 100644 --- a/src/UniTask.NetCore/Linq/Where.cs +++ b/src/UniTask.NetCore/Linq/Where.cs @@ -188,8 +188,9 @@ namespace Cysharp.Threading.Tasks.Linq return predicate(sourceCurrent); } - protected override bool TrySetCurrentCore(bool awaitResult) + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) { + terminateIteration = false; if (awaitResult) { Current = SourceCurrent; @@ -236,8 +237,9 @@ namespace Cysharp.Threading.Tasks.Linq return predicate(sourceCurrent, checked(index++)); } - protected override bool TrySetCurrentCore(bool awaitResult) + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) { + terminateIteration = false; if (awaitResult) { Current = SourceCurrent; @@ -285,8 +287,9 @@ namespace Cysharp.Threading.Tasks.Linq return predicate(sourceCurrent, cancellationToken); } - protected override bool TrySetCurrentCore(bool awaitResult) + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) { + terminateIteration = false; if (awaitResult) { Current = SourceCurrent; @@ -333,8 +336,9 @@ namespace Cysharp.Threading.Tasks.Linq return predicate(sourceCurrent, checked(index++), cancellationToken); } - protected override bool TrySetCurrentCore(bool awaitResult) + protected override bool TrySetCurrentCore(bool awaitResult, out bool terminateIteration) { + terminateIteration = false; if (awaitResult) { Current = SourceCurrent; diff --git a/src/UniTask.NetCore/Linq/_FileMaker.cs b/src/UniTask.NetCore/Linq/_FileMaker.cs index e04d089..11e8d7a 100644 --- a/src/UniTask.NetCore/Linq/_FileMaker.cs +++ b/src/UniTask.NetCore/Linq/_FileMaker.cs @@ -308,87 +308,18 @@ namespace ___Dummy - - public static IUniTaskAsyncEnumerable Skip(this IUniTaskAsyncEnumerable source, Int32 count) - { - throw new NotImplementedException(); - } - public static IUniTaskAsyncEnumerable SkipLast(this IUniTaskAsyncEnumerable source, Int32 count) { throw new NotImplementedException(); } - public static IUniTaskAsyncEnumerable SkipWhile(this IUniTaskAsyncEnumerable source, Func predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SkipWhile(this IUniTaskAsyncEnumerable source, Func predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SkipWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SkipWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SkipWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable SkipWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable Take(this IUniTaskAsyncEnumerable source, Int32 count) - { - throw new NotImplementedException(); - } + public static IUniTaskAsyncEnumerable TakeLast(this IUniTaskAsyncEnumerable source, Int32 count) { throw new NotImplementedException(); } - public static IUniTaskAsyncEnumerable TakeWhile(this IUniTaskAsyncEnumerable source, Func predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable TakeWhile(this IUniTaskAsyncEnumerable source, Func predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable TakeWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable TakeWhileAwait(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable TakeWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable TakeWhileAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> predicate) - { - throw new NotImplementedException(); - } - public static IOrderedAsyncEnumerable ThenBy(this IOrderedAsyncEnumerable source, Func keySelector) { throw new NotImplementedException(); @@ -467,26 +398,6 @@ namespace ___Dummy - public static IUniTaskAsyncEnumerable> Zip(this IUniTaskAsyncEnumerable first, IUniTaskAsyncEnumerable second) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable Zip(this IUniTaskAsyncEnumerable first, IUniTaskAsyncEnumerable second, Func selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable ZipAwait(this IUniTaskAsyncEnumerable first, IUniTaskAsyncEnumerable second, Func> selector) - { - throw new NotImplementedException(); - } - - public static IUniTaskAsyncEnumerable ZipAwaitWithCancellation(this IUniTaskAsyncEnumerable first, IUniTaskAsyncEnumerable second, Func> selector) - { - throw new NotImplementedException(); - } - } diff --git a/src/UniTask.NetCoreSandbox/Program.cs b/src/UniTask.NetCoreSandbox/Program.cs index 843564a..024c8da 100644 --- a/src/UniTask.NetCoreSandbox/Program.cs +++ b/src/UniTask.NetCoreSandbox/Program.cs @@ -39,8 +39,20 @@ namespace NetCoreSandbox static async Task Main(string[] args) { - var foo = Enumerable.Range(1, 10).ToArray().AsEnumerable().GetEnumerator() as IEnumerator; - Console.WriteLine(foo.GetType().FullName); + await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10).Do(x => Console.WriteLine("DO:" + x)) + //.TakeWhileAwait(x => UniTask.FromResult(x < 5)) + .Take(5) + + ) + { + + Console.WriteLine(item); + } + + + + + } @@ -48,7 +60,7 @@ namespace NetCoreSandbox void Foo() { - + // AsyncEnumerable.Range(1,10).Do( // AsyncEnumerable.t diff --git a/src/UniTask.NetCoreTests/Linq/Paging.cs b/src/UniTask.NetCoreTests/Linq/Paging.cs new file mode 100644 index 0000000..d0e2b55 --- /dev/null +++ b/src/UniTask.NetCoreTests/Linq/Paging.cs @@ -0,0 +1,235 @@ +using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks.Linq; +using FluentAssertions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Xunit; + + +namespace NetCoreTests.Linq +{ + public class Paging + { + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(9, 0)] + [InlineData(9, 1)] + [InlineData(9, 5)] + [InlineData(9, 9)] + [InlineData(9, 15)] + public async Task Skip(int collection, int skipCount) + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + + [Fact] + public async Task SkipException() + { + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.Skip(5).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + } + + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(9, 0)] + [InlineData(9, 1)] + [InlineData(9, 5)] + [InlineData(9, 9)] + [InlineData(9, 15)] + public async Task Take(int collection, int takeCount) + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + + [Fact] + public async Task TakeException() + { + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.Take(5).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + } + + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(9, 0)] + [InlineData(9, 1)] + [InlineData(9, 5)] + [InlineData(9, 9)] + [InlineData(9, 15)] + public async Task SkipWhile(int collection, int skipCount) + { + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + } + + [Fact] + public async Task SkipWhileException() + { + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhile(x => x < 2).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhile((x, i) => x < 2).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhileAwait((x) => UniTask.Run(() => x < 2)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhileAwait((x, i) => UniTask.Run(() => x < 2)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 2)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 2)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + } + + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(9, 0)] + [InlineData(9, 1)] + [InlineData(9, 5)] + [InlineData(9, 9)] + [InlineData(9, 15)] + public async Task TakeWhile(int collection, int skipCount) + { + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync(); + var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray(); + + xs.Should().BeEquivalentTo(ys); + } + } + + [Fact] + public async Task TakeWhileException() + { + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhile(x => x < 5).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhile((x, i) => x < 5).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhileAwait((x) => UniTask.Run(() => x < 5)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhileAwait((x, i) => UniTask.Run(() => x < 5)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 5)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + foreach (var item in UniTaskTestException.Throws()) + { + var xs = item.TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 5)).ToArrayAsync(); + await Assert.ThrowsAsync(async () => await xs); + } + } + } +}