Skip to content

Commit

Permalink
SepReader: Add async support (C# 13+ only) (#219)
Browse files Browse the repository at this point in the history
Fix #27
  • Loading branch information
nietras authored Jan 19, 2025
1 parent c2e7cd9 commit f92b65b
Show file tree
Hide file tree
Showing 14 changed files with 1,696 additions and 716 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ dotnet_diagnostic.CA1851.severity = warning
dotnet_diagnostic.CA1834.severity = warning

# CA2007: Do not directly await a Task
dotnet_diagnostic.CA2007.severity = warning
dotnet_diagnostic.CA2007.severity = none

# CA2008: Do not create tasks without passing a TaskScheduler
dotnet_diagnostic.CA2008.severity = warning
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1916,17 +1916,25 @@ namespace nietras.SeparatedValues
public static char Separator { get; }
}
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class SepReader : nietras.SeparatedValues.SepReaderState, System.Collections.Generic.IEnumerable<nietras.SeparatedValues.SepReader.Row>, System.Collections.Generic.IEnumerator<nietras.SeparatedValues.SepReader.Row>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
public sealed class SepReader : nietras.SeparatedValues.SepReaderState, System.Collections.Generic.IAsyncEnumerable<nietras.SeparatedValues.SepReader.Row>, System.Collections.Generic.IEnumerable<nietras.SeparatedValues.SepReader.Row>, System.Collections.Generic.IEnumerator<nietras.SeparatedValues.SepReader.Row>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
{
public nietras.SeparatedValues.SepReader.Row Current { get; }
public bool HasHeader { get; }
public bool HasRows { get; }
public nietras.SeparatedValues.SepReaderHeader Header { get; }
public bool IsEmpty { get; }
public nietras.SeparatedValues.SepSpec Spec { get; }
public nietras.SeparatedValues.SepReader.AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default) { }
public nietras.SeparatedValues.SepReader GetEnumerator() { }
public bool MoveNext() { }
public System.Threading.Tasks.ValueTask<bool> MoveNextAsync(System.Threading.CancellationToken cancellationToken = default) { }
public string ToString(int index) { }
public readonly struct AsyncEnumerator : System.Collections.Generic.IAsyncEnumerator<nietras.SeparatedValues.SepReader.Row>, System.IAsyncDisposable
{
public nietras.SeparatedValues.SepReader.Row Current { get; }
public System.Threading.Tasks.ValueTask DisposeAsync() { }
public System.Threading.Tasks.ValueTask<bool> MoveNextAsync() { }
}
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay}")]
[System.Obsolete(("Types with embedded references are not supported in this version of your compiler" +
"."), true)]
Expand Down Expand Up @@ -2008,8 +2016,15 @@ namespace nietras.SeparatedValues
public static nietras.SeparatedValues.SepReader From(in this nietras.SeparatedValues.SepReaderOptions options, System.IO.TextReader reader) { }
public static nietras.SeparatedValues.SepReader From(in this nietras.SeparatedValues.SepReaderOptions options, string name, System.Func<string, System.IO.Stream> nameToStream) { }
public static nietras.SeparatedValues.SepReader From(in this nietras.SeparatedValues.SepReaderOptions options, string name, System.Func<string, System.IO.TextReader> nameToReader) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromAsync(this nietras.SeparatedValues.SepReaderOptions options, byte[] buffer, System.Threading.CancellationToken cancellationToken = default) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromAsync(this nietras.SeparatedValues.SepReaderOptions options, System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromAsync(this nietras.SeparatedValues.SepReaderOptions options, System.IO.TextReader reader, System.Threading.CancellationToken cancellationToken = default) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromAsync(this nietras.SeparatedValues.SepReaderOptions options, string name, System.Func<string, System.IO.Stream> nameToStream, System.Threading.CancellationToken cancellationToken = default) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromAsync(this nietras.SeparatedValues.SepReaderOptions options, string name, System.Func<string, System.IO.TextReader> nameToReader, System.Threading.CancellationToken cancellationToken = default) { }
public static nietras.SeparatedValues.SepReader FromFile(in this nietras.SeparatedValues.SepReaderOptions options, string filePath) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromFileAsync(this nietras.SeparatedValues.SepReaderOptions options, string filePath, System.Threading.CancellationToken cancellationToken = default) { }
public static nietras.SeparatedValues.SepReader FromText(in this nietras.SeparatedValues.SepReaderOptions options, string text) { }
public static System.Threading.Tasks.ValueTask<nietras.SeparatedValues.SepReader> FromTextAsync(this nietras.SeparatedValues.SepReaderOptions options, string text, System.Threading.CancellationToken cancellationToken = default) { }
public static System.Collections.Generic.IEnumerable<T> ParallelEnumerate<T>(this nietras.SeparatedValues.SepReader reader, nietras.SeparatedValues.SepReader.RowFunc<T> select) { }
public static System.Collections.Generic.IEnumerable<T> ParallelEnumerate<T>(this nietras.SeparatedValues.SepReader reader, nietras.SeparatedValues.SepReader.RowTryFunc<T> trySelect) { }
public static System.Collections.Generic.IEnumerable<T> ParallelEnumerate<T>(this nietras.SeparatedValues.SepReader reader, nietras.SeparatedValues.SepReader.RowFunc<T> select, int degreeOfParallism) { }
Expand Down
9 changes: 9 additions & 0 deletions src/Sep.ComparisonBenchmarks/PackageAssetsBench.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using CsvHelper;
Expand Down Expand Up @@ -78,6 +79,14 @@ public void Sep______()
foreach (var row in reader) { }
}

[Benchmark]
public async ValueTask Sep_Async()
{
using var reader = await Sep.Reader(o => o with { HasHeader = false })
.FromAsync(Reader.CreateReader());
await foreach (var row in reader) { }
}

[Benchmark]
public void Sep_Unescape()
{
Expand Down
46 changes: 42 additions & 4 deletions src/Sep.Test/PackageAssetsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace nietras.SeparatedValues.Test;
Expand Down Expand Up @@ -35,6 +36,23 @@ public class PackageAssetsTest
public void PackageAssetsTest_Read_WithQuotes_Unescape(SepCreateToString createToString) => VerifyRead(WithQuotes, createToString, unescape: true);


[DataTestMethod]
[DynamicData(nameof(ToStrings))]
public async ValueTask PackageAssetsTest_Read_NoQuotes_Async(SepCreateToString createToString) => await VerifyReadAsync(NoQuotes, createToString);

[DataTestMethod]
[DynamicData(nameof(ToStrings))]
public async ValueTask PackageAssetsTest_Read_NoQuotes_Unescape_Async(SepCreateToString createToString) => await VerifyReadAsync(NoQuotes, createToString, unescape: true);

[DataTestMethod]
[DynamicData(nameof(ToStrings))]
public async ValueTask PackageAssetsTest_Read_WithQuotes_Async(SepCreateToString createToString) => await VerifyReadAsync(WithQuotes, createToString);

[DataTestMethod]
[DynamicData(nameof(ToStrings))]
public async ValueTask PackageAssetsTest_Read_WithQuotes_Unescape_Async(SepCreateToString createToString) => await VerifyReadAsync(WithQuotes, createToString, unescape: true);


[DataTestMethod]
[DynamicData(nameof(ToStrings))]
public void PackageAssetsTest_Enumerate_NoQuotes(SepCreateToString createToString) =>
Expand Down Expand Up @@ -184,15 +202,35 @@ static void VerifyRead(string text, SepCreateToString createToString, bool unesc
var rowIndex = 0;
foreach (var row in reader)
{
var expectedCols = expected[rowIndex];
expectedCols = unescape ? UnescapeColsByTrim(expectedCols) : expectedCols;
Assert.AreEqual(expectedCols.Length, row.ColCount);
CollectionAssert.AreEqual(expectedCols, row[..].ToStringsArray());
AssertCols(row, rowIndex, unescape, expected);
++rowIndex;
}
Assert.AreEqual(expected.Count, rowIndex);
}

static async ValueTask VerifyReadAsync(string text, SepCreateToString createToString, bool unescape = false)
{
var expected = ReadLineSplitAsList(text);
using var reader = await Sep
.Reader(o => o with { HasHeader = false, CreateToString = createToString, Unescape = unescape })
.FromTextAsync(text);
var rowIndex = 0;
await foreach (var row in reader)
{
AssertCols(row, rowIndex, unescape, expected);
++rowIndex;
}
Assert.AreEqual(expected.Count, rowIndex);
}

static void AssertCols(SepReader.Row row, int rowIndex, bool unescape, List<string[]> expected)
{
var expectedCols = expected[rowIndex];
expectedCols = unescape ? UnescapeColsByTrim(expectedCols) : expectedCols;
Assert.AreEqual(expectedCols.Length, row.ColCount);
CollectionAssert.AreEqual(expectedCols, row[..].ToStringsArray());
}

static void VerifyEnumerate(string text, SepCreateToString createToString,
Func<SepReader, SepReader.RowFunc<string[]>, IEnumerable<string[]>> enumerate,
bool unescape = false)
Expand Down
226 changes: 0 additions & 226 deletions src/Sep.Test/SepReaderNoHeaderTest.cs

This file was deleted.

Loading

0 comments on commit f92b65b

Please sign in to comment.