diff --git a/src/MoreAsyncLINQ.Generators/FoldGenerator.cs b/src/MoreAsyncLINQ.Generators/FoldGenerator.cs new file mode 100644 index 0000000..2edf90d --- /dev/null +++ b/src/MoreAsyncLINQ.Generators/FoldGenerator.cs @@ -0,0 +1,176 @@ +using System.CodeDom.Compiler; +using System.IO; +using Microsoft.CodeAnalysis; + +namespace MoreAsyncLINQ.Generators; + +[Generator] +public class FoldGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) => + context.RegisterPostInitializationOutput(postInitializationContext => + { + var source = GenerateOverloads(); + postInitializationContext.AddSource("MoreAsyncEnumerable.Fold.g.cs", source); + }); + + private string GenerateOverloads() + { + using var stringWriter = new StringWriter(); + using var writer = Writers.CreateSourceWriter(stringWriter); + + using (writer.BeginBracketsScope()) + using (writer.BeginIndentScope()) + { + // Sync overloads: arity 1-16 + for (var arity = 1; arity <= 16; arity++) + { + GenerateSyncOverload(writer, arity); + writer.WriteLine(); + } + + // Async overloads: arity 1-15 (limited by Func delegate having max 16 input params, + // and async needs CancellationToken as additional input) + for (var arity = 1; arity <= 15; arity++) + { + GenerateAsyncOverload(writer, arity); + + if (arity < 15) + { + writer.WriteLine(); + } + } + } + + return stringWriter.ToString(); + } + + private void GenerateSyncOverload(IndentedTextWriter writer, int arity) + { + WriteXmlDocumentation(writer, arity); + WriteMethodSignature(writer, arity, isAsync: false); + + using (writer.BeginBracketsScope()) + using (writer.BeginIndentScope()) + { + WriteNullChecks(writer); + writer.WriteLine(); + + writer.WriteLine("return Core(source, folder, cancellationToken);"); + writer.WriteLine(); + WriteCoreMethod(writer, arity, isAsync: false); + } + } + + private void GenerateAsyncOverload(IndentedTextWriter writer, int arity) + { + WriteXmlDocumentation(writer, arity); + WriteMethodSignature(writer, arity, isAsync: true); + + using (writer.BeginBracketsScope()) + using (writer.BeginIndentScope()) + { + WriteNullChecks(writer); + writer.WriteLine(); + + writer.WriteLine("return Core(source, folder, cancellationToken);"); + writer.WriteLine(); + WriteCoreMethod(writer, arity, isAsync: true); + } + } + + private void WriteXmlDocumentation(IndentedTextWriter writer, int arity) + { + writer = Writers.CreateXmlDocWriter(writer); + + writer.WriteLine(""); + writer.WriteLine("Returns the result of applying a function to a sequence of"); + writer.WriteLine($"{arity} element{(arity == 1 ? "" : "s")}."); + writer.WriteLine(""); + writer.WriteLine(""); + writer.WriteLine("This operator uses immediate execution and effectively buffers"); + writer.WriteLine("as many items of the source sequence as necessary."); + writer.WriteLine(""); + writer.WriteLine("Type of element in the source sequence"); + writer.WriteLine("Type of the result"); + writer.WriteLine("The sequence of items to fold."); + writer.WriteLine("Function to apply to the elements in the sequence."); + writer.WriteLine("The optional cancellation token to be used for cancelling the sequence at any time."); + writer.WriteLine("The folded value returned by ."); + writer.WriteLine(" is null"); + writer.WriteLine(" is null"); + writer.WriteLine($" does not contain exactly {arity} element{(arity == 1 ? "" : "s")}"); + } + + private void WriteMethodSignature(IndentedTextWriter writer, int arity, bool isAsync) + { + var folderType = GetFolderFuncType(arity, isAsync); + + writer.WriteLine("public static ValueTask FoldAsync("); + + using var _ = writer.BeginIndentScope(); + + writer.WriteLine("this IAsyncEnumerable source,"); + writer.WriteLine($"{folderType} folder,"); + writer.WriteLine("CancellationToken cancellationToken = default)"); + } + + private void WriteNullChecks(IndentedTextWriter writer) + { + Writers.WriteNullCheck(writer, "source"); + Writers.WriteNullCheck(writer, "folder"); + } + + private void WriteCoreMethod(IndentedTextWriter writer, int arity, bool isAsync) + { + var folderType = GetFolderFuncType(arity, isAsync); + + writer.WriteLine("static async ValueTask Core("); + + using (writer.BeginIndentScope()) + { + writer.WriteLine("IAsyncEnumerable source,"); + writer.WriteLine($"{folderType} folder,"); + writer.WriteLine("CancellationToken cancellationToken)"); + } + + using (writer.BeginBracketsScope()) + using (writer.BeginIndentScope()) + { + writer.WriteLine($"var elements = await GetFoldElementsAsync(source, count: {arity}, cancellationToken);"); + writer.WriteLine(isAsync ? "return await folder(" : "return folder("); + + using (writer.BeginIndentScope()) + { + for (var index = 0; index < arity; index++) + { + var isLast = index == arity - 1; + + if (isAsync) + { + writer.WriteLine($"elements[{index}],"); + } + else + { + writer.WriteLine(isLast ? $"elements[{index}]);" : $"elements[{index}],"); + } + } + + if (isAsync) + { + writer.WriteLine("cancellationToken);"); + } + } + } + } + + private static string GetFolderFuncType(int arity, bool isAsync) + { + var sourceParams = string.Join(", ", System.Linq.Enumerable.Repeat("TSource", arity)); + + return isAsync + ? $"Func<{sourceParams}, CancellationToken, ValueTask>" + : $"Func<{sourceParams}, TResult>"; + } +} + diff --git a/src/MoreAsyncLINQ/Operators/Fold.cs b/src/MoreAsyncLINQ/Operators/Fold.cs index a1043d5..19e5675 100644 --- a/src/MoreAsyncLINQ/Operators/Fold.cs +++ b/src/MoreAsyncLINQ/Operators/Fold.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,1421 +7,6 @@ namespace MoreAsyncLINQ; static partial class MoreAsyncEnumerable { - /// - /// Returns the result of applying a function to a sequence of - /// 1 element. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 1 element - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 1, cancellationToken); - return folder(elements[0]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 2 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 2 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 2, cancellationToken); - return folder( - elements[0], - elements[1]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 3 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 3 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 3, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 4 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 4 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 4, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 5 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 5 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 5, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 6 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 6 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 6, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 7 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 7 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 7, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 8 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 8 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 8, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 9 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 9 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 9, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 10 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 10 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 10, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 11 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 11 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 11, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 12 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 12 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 12, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 13 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 13 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 13, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 14 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 14 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 14, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - elements[13]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 15 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 15 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 15, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - elements[13], - elements[14]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 16 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 16, cancellationToken); - return folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - elements[13], - elements[14], - elements[15]); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// one element. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 1, cancellationToken); - return await folder(elements[0], cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 2 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 2, cancellationToken); - return await folder( - elements[0], - elements[1], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 3 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 3, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 4 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 4, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 5 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 5, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 6 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 6, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 7 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 7, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 8 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 8, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 9 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 9, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 10 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 10, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 11 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 11, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 12 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 12, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 13 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 13, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 14 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 14, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - elements[13], - cancellationToken); - } - } - - /// - /// Returns the result of applying a function to a sequence of - /// 15 elements. - /// - /// - /// This operator uses immediate execution and effectively buffers - /// as many items of the source sequence as necessary. - /// - /// Type of element in the source sequence - /// Type of the result - /// The sequence of items to fold. - /// Function to apply to the elements in the sequence. - /// The optional cancellation token to be used for cancelling the sequence at any time. - /// The folded value returned by . - /// is null - /// is null - /// does not contain exactly 16 elements - public static ValueTask FoldAsync( - this IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken = default) - { - if (source is null) throw new ArgumentNullException(nameof(source)); - if (folder is null) throw new ArgumentNullException(nameof(folder)); - - return Core(source, folder, cancellationToken); - - static async ValueTask Core( - IAsyncEnumerable source, - Func> folder, - CancellationToken cancellationToken) - { - var elements = await GetFoldElementsAsync(source, count: 15, cancellationToken); - return await folder( - elements[0], - elements[1], - elements[2], - elements[3], - elements[4], - elements[5], - elements[6], - elements[7], - elements[8], - elements[9], - elements[10], - elements[11], - elements[12], - elements[13], - elements[14], - cancellationToken); - } - } - private static async ValueTask GetFoldElementsAsync( IAsyncEnumerable source, int count, @@ -1436,4 +20,4 @@ private static async ValueTask GetFoldElementsAsync( return elements; } -} \ No newline at end of file +} diff --git a/tests/MoreAsyncLINQ.Tests/Operators/FoldTests.cs b/tests/MoreAsyncLINQ.Tests/Operators/FoldTests.cs new file mode 100644 index 0000000..2069f2f --- /dev/null +++ b/tests/MoreAsyncLINQ.Tests/Operators/FoldTests.cs @@ -0,0 +1,412 @@ +using MoreLinq; + +namespace MoreAsyncLINQ.Tests; + +// ReSharper disable StringLiteralTypo + +public class FoldTests : AsyncEnumerableTests +{ + [Fact] + public void InvalidInputs_Throws() + { + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, _ => 0)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (_, _) => ValueTask.FromResult(0))); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func>)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b) => a + b)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, _) => ValueTask.FromResult(a + b))); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func>)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, c, d) => a + b + c + d)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, c, d, _) => ValueTask.FromResult(a + b + c + d))); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func>)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, c, d, e, f, g, h) => a + b + c + d + e + f + g + h)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func)null!)); + + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, c, d, e, f, g, h, _) => ValueTask.FromResult(a + b + c + d + e + f + g + h))); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func>)null!)); + + // Arity 16 - sync only (async not available due to Func delegate limit) + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func)null!)); + + // Arity 15 - async (highest async arity) + Func> asyncFolder15 = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, _) => ValueTask.FromResult(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o); + Assert.Throws("source", () => MoreAsyncEnumerable.FoldAsync(null!, asyncFolder15)); + Assert.Throws("folder", () => AsyncEnumerable.Empty().FoldAsync((Func>)null!)); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task WithTooFewItems_Throws(bool isAsync) + { + var source = AsyncEnumerable.Range(1, 3); + + var exception = + await Assert.ThrowsAsync(async () => + { + if (isAsync) + { + await source.FoldAsync(async (int a, int b, int c, int d, CancellationToken _) => a + b + c + d); + } + else + { + await source.FoldAsync((a, b, c, d) => a + b + c + d); + } + }); + + Assert.Equal("Sequence contains too few elements when exactly 4 were expected.", exception.Message); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task WithEmptySequence_Throws(bool isAsync) + { + var source = AsyncEnumerable.Empty(); + + var exception = + await Assert.ThrowsAsync(async () => + { + if (isAsync) + { + await source.FoldAsync(async (int x, CancellationToken _) => x); + } + else + { + await source.FoldAsync(x => x); + } + }); + + Assert.Equal("Sequence contains too few elements when exactly 1 was expected.", exception.Message); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task WithTooManyItems_Throws(bool isAsync) + { + var source = AsyncEnumerable.Range(1, 3); + + var exception = + await Assert.ThrowsAsync(async () => + { + if (isAsync) + { + await source.FoldAsync(async (int a, int b, CancellationToken _) => a + b); + } + else + { + await source.FoldAsync((a, b) => a + b); + } + }); + + Assert.Equal("Sequence contains too many elements when exactly 2 were expected.", exception.Message); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold1(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(1).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold(a => string.Join(string.Empty, a)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, CancellationToken _) => string.Join(string.Empty, a)) + : await asyncSource.FoldAsync(a => string.Join(string.Empty, a)); + + Assert.Equal(expected, actual); + Assert.Equal("a", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold2(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(2).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b) => string.Join(string.Empty, a, b)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, CancellationToken _) => string.Join(string.Empty, a, b)) + : await asyncSource.FoldAsync((a, b) => string.Join(string.Empty, a, b)); + + Assert.Equal(expected, actual); + Assert.Equal("ab", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold3(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(3).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c) => string.Join(string.Empty, a, b, c)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, CancellationToken _) => string.Join(string.Empty, a, b, c)) + : await asyncSource.FoldAsync((a, b, c) => string.Join(string.Empty, a, b, c)); + + Assert.Equal(expected, actual); + Assert.Equal("abc", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold4(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(4).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d) => string.Join(string.Empty, a, b, c, d)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, CancellationToken _) => string.Join(string.Empty, a, b, c, d)) + : await asyncSource.FoldAsync((a, b, c, d) => string.Join(string.Empty, a, b, c, d)); + + Assert.Equal(expected, actual); + Assert.Equal("abcd", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold5(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(5).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e) => string.Join(string.Empty, a, b, c, d, e)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e)) + : await asyncSource.FoldAsync((a, b, c, d, e) => string.Join(string.Empty, a, b, c, d, e)); + + Assert.Equal(expected, actual); + Assert.Equal("abcde", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold6(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(6).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f) => string.Join(string.Empty, a, b, c, d, e, f)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f)) + : await asyncSource.FoldAsync((a, b, c, d, e, f) => string.Join(string.Empty, a, b, c, d, e, f)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdef", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold7(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(7).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g) => string.Join(string.Empty, a, b, c, d, e, f, g)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g) => string.Join(string.Empty, a, b, c, d, e, f, g)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefg", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold8(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(8).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h) => string.Join(string.Empty, a, b, c, d, e, f, g, h)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h) => string.Join(string.Empty, a, b, c, d, e, f, g, h)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefgh", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold9(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(9).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghi", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold10(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(10).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghij", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold11(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(11).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijk", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold12(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(12).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k, l) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, char l, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k, l) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijkl", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold13(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(13).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k, l, m) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, char l, char m, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k, l, m) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijklm", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold14(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(14).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, char l, char m, char n, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijklmn", actual); + } + + [Theory] + [MemberData(nameof(IsAsync))] + public async Task Fold15(bool isAsync) + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(15).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)); + var actual = + isAsync + ? await asyncSource.FoldAsync(async (char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, char l, char m, char n, char o, CancellationToken _) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)) + : await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijklmno", actual); + } + + [Fact] + public async Task Fold16_Sync() + { + // Async overload not available for arity 16 due to Func delegate limit (16 input params + CancellationToken would exceed) + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + + var source = alphabet.Take(16).ToArray(); + var asyncSource = source.ToAsyncEnumerable(); + + var expected = source.Fold((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)); + var actual = await asyncSource.FoldAsync((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => string.Join(string.Empty, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)); + + Assert.Equal(expected, actual); + Assert.Equal("abcdefghijklmnop", actual); + } +} diff --git a/tests/MoreAsyncLINQ.Tests/PublicApi/ApiSurfaceTests/FoldTests.cs b/tests/MoreAsyncLINQ.Tests/PublicApi/ApiSurfaceTests/FoldTests.cs deleted file mode 100644 index 6f18403..0000000 --- a/tests/MoreAsyncLINQ.Tests/PublicApi/ApiSurfaceTests/FoldTests.cs +++ /dev/null @@ -1,194 +0,0 @@ -namespace MoreAsyncLINQ.Tests.PublicApi.ApiSurfaceTests; - -public class FoldTests -{ - private static IAsyncEnumerable Source(int count) => AsyncEnumerable.Range(0, count); - - [Fact] - public async Task FoldAsync_One_Sync_Compiles() - { - await Source(1).FoldAsync(x1 => x1.ToString()); - } - - [Fact] - public async Task FoldAsync_One_Async_Compiles() - { - await Source(1).FoldAsync((int x1, CancellationToken _) => ValueTask.FromResult(x1)); - } - - [Fact] - public async Task FoldAsync_Two_Sync_Compiles() - { - await Source(2).FoldAsync((x1, x2) => (x1 + x2).ToString()); - } - - [Fact] - public async Task FoldAsync_Two_Async_Compiles() - { - await Source(2).FoldAsync((int x1, int x2, CancellationToken _) => ValueTask.FromResult(x1 + x2)); - } - - [Fact] - public async Task FoldAsync_Three_Sync_Compiles() - { - await Source(3).FoldAsync((x1, x2, x3) => (x1 + x2 + x3).ToString()); - } - - [Fact] - public async Task FoldAsync_Three_Async_Compiles() - { - await Source(3).FoldAsync((int x1, int x2, int x3, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3)); - } - - [Fact] - public async Task FoldAsync_Four_Sync_Compiles() - { - await Source(4).FoldAsync((x1, x2, x3, x4) => (x1 + x2 + x3 + x4).ToString()); - } - - [Fact] - public async Task FoldAsync_Four_Async_Compiles() - { - await Source(4).FoldAsync((int x1, int x2, int x3, int x4, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4)); - } - - [Fact] - public async Task FoldAsync_Five_Sync_Compiles() - { - await Source(5).FoldAsync((x1, x2, x3, x4, x5) => (x1 + x2 + x3 + x4 + x5).ToString()); - } - - [Fact] - public async Task FoldAsync_Five_Async_Compiles() - { - await Source(5).FoldAsync((int x1, int x2, int x3, int x4, int x5, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5)); - } - - [Fact] - public async Task FoldAsync_Six_Sync_Compiles() - { - await Source(6).FoldAsync((x1, x2, x3, x4, x5, x6) => (x1 + x2 + x3 + x4 + x5 + x6).ToString()); - } - - [Fact] - public async Task FoldAsync_Six_Async_Compiles() - { - await Source(6).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6)); - } - - [Fact] - public async Task FoldAsync_Seven_Sync_Compiles() - { - await Source(7).FoldAsync((x1, x2, x3, x4, x5, x6, x7) => (x1 + x2 + x3 + x4 + x5 + x6 + x7).ToString()); - } - - [Fact] - public async Task FoldAsync_Seven_Async_Compiles() - { - Func> folder = (x1, x2, x3, x4, x5, x6, x7, _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7); - await Source(7).FoldAsync(folder); - } - - [Fact] - public async Task FoldAsync_Eight_Sync_Compiles() - { - await Source(8).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8).ToString()); - } - - [Fact] - public async Task FoldAsync_Eight_Async_Compiles() - { - await Source(8).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8)); - } - - [Fact] - public async Task FoldAsync_Nine_Sync_Compiles() - { - await Source(9).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9).ToString()); - } - - [Fact] - public async Task FoldAsync_Nine_Async_Compiles() - { - await Source(9).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9)); - } - - [Fact] - public async Task FoldAsync_Ten_Sync_Compiles() - { - await Source(10).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10).ToString()); - } - - [Fact] - public async Task FoldAsync_Ten_Async_Compiles() - { - await Source(10).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10)); - } - - [Fact] - public async Task FoldAsync_Eleven_Sync_Compiles() - { - await Source(11).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11).ToString()); - } - - [Fact] - public async Task FoldAsync_Eleven_Async_Compiles() - { - await Source(11).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11)); - } - - [Fact] - public async Task FoldAsync_Twelve_Sync_Compiles() - { - await Source(12).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12).ToString()); - } - - [Fact] - public async Task FoldAsync_Twelve_Async_Compiles() - { - await Source(12).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12)); - } - - [Fact] - public async Task FoldAsync_Thirteen_Sync_Compiles() - { - await Source(13).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13).ToString()); - } - - [Fact] - public async Task FoldAsync_Thirteen_Async_Compiles() - { - await Source(13).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, int x13, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13)); - } - - [Fact] - public async Task FoldAsync_Fourteen_Sync_Compiles() - { - await Source(14).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14).ToString()); - } - - [Fact] - public async Task FoldAsync_Fourteen_Async_Compiles() - { - await Source(14).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, int x13, int x14, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14)); - } - - [Fact] - public async Task FoldAsync_Fifteen_Sync_Compiles() - { - await Source(15).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15).ToString()); - } - - [Fact] - public async Task FoldAsync_Fifteen_Async_Compiles() - { - await Source(15).FoldAsync((int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, int x13, int x14, int x15, CancellationToken _) => ValueTask.FromResult(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15)); - } - - [Fact] - public async Task FoldAsync_Sixteen_Sync_Compiles() - { - await Source(16).FoldAsync((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16) => (x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16).ToString()); - } -} -