Skip to content

Commit 93d9065

Browse files
authored
Expand README with usage in async/await context and cleanup (#118)
1 parent a21d9ad commit 93d9065

File tree

7 files changed

+205
-99
lines changed

7 files changed

+205
-99
lines changed

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ public bool DisableQuotesParsing { get; init; } = false;
312312
/// state after unescaped cols before next col. This is for efficiency to
313313
/// avoid allocating secondary memory for unescaped columns. Header
314314
/// columns/names will also be unescaped.
315+
/// Requires <see cref="DisableQuotesParsing"/> to be false.
315316
/// </remarks>
316317
public bool Unescape { get; init; } = false;
317318
```
@@ -1432,6 +1433,57 @@ foreach (var row in reader)
14321433
CollectionAssert.AreEqual(expected, actual);
14331434
```
14341435

1436+
### Example - Use Extension Method Enumerate within async/await Context
1437+
Since `SepReader.Row` is a `ref struct` as covered above, one has to avoid
1438+
referencing it directly in async context. This can be done in a number of ways,
1439+
but one way is to use `Enumerate` extension method to parse/extract data from
1440+
row like shown below.
1441+
1442+
```csharp
1443+
var text = """
1444+
C
1445+
1
1446+
2
1447+
""";
1448+
1449+
using var reader = Sep.Reader().FromText(text);
1450+
var squaredSum = 0;
1451+
// Use Enumerate to avoid referencing SepReader.Row in async context
1452+
foreach (var value in reader.Enumerate(row => row["C"].Parse<int>()))
1453+
{
1454+
squaredSum += await Task.Run(() => value * value);
1455+
}
1456+
Assert.AreEqual(5, squaredSum);
1457+
```
1458+
1459+
### Example - Use Local Function within async/await Context
1460+
Another way to avoid referencing `SepReader.Row` directly in async context is to
1461+
use custom iterator via `yield return` to parse/extract data from row like shown
1462+
below.
1463+
1464+
```csharp
1465+
var text = """
1466+
C
1467+
1
1468+
2
1469+
""";
1470+
1471+
using var reader = Sep.Reader().FromText(text);
1472+
var squaredSum = 0;
1473+
// Use custom local function Enumerate to avoid referencing
1474+
// SepReader.Row in async context
1475+
foreach (var value in Enumerate(reader))
1476+
{
1477+
squaredSum += await Task.Run(() => value * value);
1478+
}
1479+
Assert.AreEqual(5, squaredSum);
1480+
1481+
static IEnumerable<int> Enumerate(SepReader reader)
1482+
{
1483+
foreach (var r in reader) { yield return r["C"].Parse<int>(); }
1484+
}
1485+
```
1486+
14351487
## RFC-4180
14361488
While the [RFC-4180](https://www.ietf.org/rfc/rfc4180.txt) requires `\r\n`
14371489
(CR,LF) as line ending, the well-known line endings (`\r\n`, `\n` and `\r`) are

src/Sep.Test/PackageAssetsTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ public class PackageAssetsTest
1111
{
1212
internal static IEnumerable<object[]> ToStrings =>
1313
[
14-
new[]{ SepToString.Direct },
15-
new[]{ SepToString.OnePool() },
16-
new[]{ SepToString.PoolPerCol() },
17-
new[]{ SepToString.PoolPerColThreadSafe() },
18-
new[]{ SepToString.PoolPerColThreadSafeFixedCapacity() },
14+
[SepToString.Direct],
15+
[SepToString.OnePool()],
16+
[SepToString.PoolPerCol()],
17+
[SepToString.PoolPerColThreadSafe()],
18+
[SepToString.PoolPerColThreadSafeFixedCapacity()],
1919
];
2020

2121
[DataTestMethod]

src/Sep.Test/ReadMeTest.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
using System.Linq;
66
using System.Runtime.CompilerServices;
77
using System.Text;
8+
using System.Threading.Tasks;
89
using Microsoft.VisualStudio.TestTools.UnitTesting;
10+
#if NET8_0
911
using PublicApiGenerator;
12+
#endif
13+
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
1014

1115
// Only parallize on class level to avoid multiple writes to README file
1216
[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.ClassLevel)]
@@ -288,6 +292,50 @@ public void ReadMeTest_Example_CustomSep_DisableColCountCheck()
288292
foreach (var readRow in reader) { }
289293
}
290294

295+
[TestMethod]
296+
public async Task ReadMeTest_Example_AsyncAwaitContext_Enumerate()
297+
{
298+
var text = """
299+
C
300+
1
301+
2
302+
""";
303+
304+
using var reader = Sep.Reader().FromText(text);
305+
var squaredSum = 0;
306+
// Use Enumerate to avoid referencing SepReader.Row in async context
307+
foreach (var value in reader.Enumerate(row => row["C"].Parse<int>()))
308+
{
309+
squaredSum += await Task.Run(() => value * value);
310+
}
311+
Assert.AreEqual(5, squaredSum);
312+
}
313+
314+
[TestMethod]
315+
public async Task ReadMeTest_Example_AsyncAwaitContext_CustomIterator()
316+
{
317+
var text = """
318+
C
319+
1
320+
2
321+
""";
322+
323+
using var reader = Sep.Reader().FromText(text);
324+
var squaredSum = 0;
325+
// Use custom local function Enumerate to avoid referencing
326+
// SepReader.Row in async context
327+
foreach (var value in Enumerate(reader))
328+
{
329+
squaredSum += await Task.Run(() => value * value);
330+
}
331+
Assert.AreEqual(5, squaredSum);
332+
333+
static IEnumerable<int> Enumerate(SepReader reader)
334+
{
335+
foreach (var r in reader) { yield return r["C"].Parse<int>(); }
336+
}
337+
}
338+
291339
[TestMethod]
292340
public void ReadMeTest_UpdateBenchmarksInMarkdown()
293341
{
@@ -361,6 +409,8 @@ public void ReadMeTest_UpdateExampleCodeInMarkdown()
361409
(nameof(ReadMeTest_EnumerateTrySelect) + "()", "With this the above custom `Enumerate`"),
362410
(nameof(ReadMeTest_Example_Copy_Rows) + "()", "### Example - Copy Rows"),
363411
(nameof(ReadMeTest_Example_Skip_Empty_Rows) + "()", "### Example - Skip Empty Rows"),
412+
(nameof(ReadMeTest_Example_AsyncAwaitContext_Enumerate) + "()", "### Example - Use Extension Method Enumerate within async/await Context"),
413+
(nameof(ReadMeTest_Example_AsyncAwaitContext_CustomIterator) + "()", "### Example - Use Local Function within async/await Context"),
364414
};
365415
readmeLines = UpdateReadme(testSourceLines, readmeLines, testBlocksToUpdate,
366416
sourceStartLineOffset: 2, " }", sourceEndLineOffset: 0, sourceWhitespaceToRemove: 8);
@@ -385,6 +435,8 @@ public void ReadMeTest_UpdateExampleCodeInMarkdown()
385435
File.WriteAllText(readmeFilePath, newReadme, Encoding.UTF8);
386436
}
387437

438+
// Only update public API in README for .NET 8.0 to keep consistent
439+
#if NET8_0
388440
[TestMethod]
389441
public void ReadMeTest_PublicApi()
390442
{
@@ -398,6 +450,7 @@ public void ReadMeTest_PublicApi()
398450
var newReadme = string.Join(Environment.NewLine, readmeLines) + Environment.NewLine;
399451
File.WriteAllText(readmeFilePath, newReadme, Encoding.UTF8);
400452
}
453+
#endif
401454

402455
static string[] UpdateReadme(string[] sourceLines, string[] readmeLines,
403456
(string StartLineContains, string ReadmeLineBefore)[] blocksToUpdate,

src/Sep.Test/SepParserTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@ public void SepParserTest_ParseColEnds_Long_ColEndsAlmostFilled(object parserObj
216216

217217
var expectedOutput = new ExpectedOutput([
218218
new(ColEnds: [0, 10, 46, 83], LineNumberTo: 4),
219-
new(ColEnds: [83, 86], LineNumberTo: 5),
220-
new(ColEnds: [87, 89, 101, 105, 107], LineNumberTo: 6),
221-
new(ColEnds: [107, 111], LineNumberTo: 7),
219+
new(ColEnds: [83, 86], LineNumberTo: 5),
220+
new(ColEnds: [87, 89, 101, 105, 107], LineNumberTo: 6),
221+
new(ColEnds: [107, 111], LineNumberTo: 7),
222222
], new(ColEnds: [111], LineNumberTo: 7, CharsStartIndex: 112, ColEndsStartIndex: 13, ColCount: 0));
223223

224224
AssertOutput(parser, charsStart, charsEnd, expectedOutput);

src/Sep.Test/SepReaderTest.cs

Lines changed: 90 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -267,75 +267,75 @@ public void SepReaderTest_Info_Props()
267267

268268
internal static IEnumerable<object[]> ColCountMismatchData =>
269269
[
270-
[ """
271-
C1;C2
272-
123
273-
""",
274-
"""
275-
Found 1 column(s) on row 1/lines [2..3]:'123'
276-
Expected 2 column(s) matching header/first row 'C1;C2'
277-
""",
278-
new [] { 2, 1 } ],
279-
[ """
280-
C1;C2
281-
282-
1;2
283-
""",
284-
"""
285-
Found 1 column(s) on row 1/lines [2..3]:''
286-
Expected 2 column(s) matching header/first row 'C1;C2'
287-
""",
288-
new [] { 2, 1, 2 } ],
289-
[ """
290-
C1;C2;C3
291-
1;2;3
292-
4;5
293-
""",
294-
"""
295-
Found 2 column(s) on row 2/lines [3..4]:'4;5'
296-
Expected 3 column(s) matching header/first row 'C1;C2;C3'
297-
""",
298-
new [] { 3, 3, 2 } ],
299-
[ """
300-
C1;C2;C3
301-
4;5
302-
1;2;3
303-
""",
304-
"""
305-
Found 2 column(s) on row 1/lines [2..3]:'4;5'
306-
Expected 3 column(s) matching header/first row 'C1;C2;C3'
307-
""",
308-
new [] { 3, 2, 3 } ],
309-
[ """
310-
C1
311-
312-
4;5
313-
""",
314-
"""
315-
Found 2 column(s) on row 2/lines [3..4]:'4;5'
316-
Expected 1 column(s) matching header/first row 'C1'
317-
""",
318-
new [] { 1, 1, 2 } ],
319-
[ """
320-
C1;C2
321-
4;5
322-
1;2;3
323-
""",
324-
"""
325-
Found 3 column(s) on row 2/lines [3..4]:'1;2;3'
326-
Expected 2 column(s) matching header/first row 'C1;C2'
327-
""",
328-
new [] { 2, 2, 3 } ],
329-
[ """
330-
C1;C2
331-
4";"5
332-
1;2;345
333-
""",
334-
"""
335-
Found 1 column(s) on row 1/lines [2..3]:'4";"5'
336-
Expected 2 column(s) matching header/first row 'C1;C2'
337-
""",
338-
new [] { 2, 1, 3 } ],
270+
["""
271+
C1;C2
272+
123
273+
""",
274+
"""
275+
Found 1 column(s) on row 1/lines [2..3]:'123'
276+
Expected 2 column(s) matching header/first row 'C1;C2'
277+
""",
278+
new[] { 2, 1 }],
279+
["""
280+
C1;C2
281+
282+
1;2
283+
""",
284+
"""
285+
Found 1 column(s) on row 1/lines [2..3]:''
286+
Expected 2 column(s) matching header/first row 'C1;C2'
287+
""",
288+
new[] { 2, 1, 2 }],
289+
["""
290+
C1;C2;C3
291+
1;2;3
292+
4;5
293+
""",
294+
"""
295+
Found 2 column(s) on row 2/lines [3..4]:'4;5'
296+
Expected 3 column(s) matching header/first row 'C1;C2;C3'
297+
""",
298+
new[] { 3, 3, 2 }],
299+
["""
300+
C1;C2;C3
301+
4;5
302+
1;2;3
303+
""",
304+
"""
305+
Found 2 column(s) on row 1/lines [2..3]:'4;5'
306+
Expected 3 column(s) matching header/first row 'C1;C2;C3'
307+
""",
308+
new[] { 3, 2, 3 }],
309+
["""
310+
C1
311+
312+
4;5
313+
""",
314+
"""
315+
Found 2 column(s) on row 2/lines [3..4]:'4;5'
316+
Expected 1 column(s) matching header/first row 'C1'
317+
""",
318+
new[] { 1, 1, 2 }],
319+
["""
320+
C1;C2
321+
4;5
322+
1;2;3
323+
""",
324+
"""
325+
Found 3 column(s) on row 2/lines [3..4]:'1;2;3'
326+
Expected 2 column(s) matching header/first row 'C1;C2'
327+
""",
328+
new[] { 2, 2, 3 }],
329+
["""
330+
C1;C2
331+
4";"5
332+
1;2;345
333+
""",
334+
"""
335+
Found 1 column(s) on row 1/lines [2..3]:'4";"5'
336+
Expected 2 column(s) matching header/first row 'C1;C2'
337+
""",
338+
new[] { 2, 1, 3 }],
339339
];
340340

341341
[DataTestMethod]
@@ -412,28 +412,28 @@ public void SepReaderTest_ColumnCountMismatch_IgnoreThrows(
412412

413413
internal static IEnumerable<object[]> LineNumbersData =>
414414
[
415-
["C1;C2\n123;456", new [] { (1, 2), (2, 3) }],
416-
["C1;C2\n123;456\n", new [] { (1, 2), (2, 3) }],
417-
["C1;C2\r\n123;456\r\n", new [] { (1, 2), (2, 3) }],
418-
["C1;C2\r123;456\r", new [] { (1, 2), (2, 3) }],
419-
["C1;C2\n123;456\n789;012\n", new [] { (1, 2), (2, 3), (3, 4) }],
415+
["C1;C2\n123;456", new[] { (1, 2), (2, 3) }],
416+
["C1;C2\n123;456\n", new[] { (1, 2), (2, 3) }],
417+
["C1;C2\r\n123;456\r\n", new[] { (1, 2), (2, 3) }],
418+
["C1;C2\r123;456\r", new[] { (1, 2), (2, 3) }],
419+
["C1;C2\n123;456\n789;012\n", new[] { (1, 2), (2, 3), (3, 4) }],
420420
// Line endings in quotes
421-
[ """
422-
"C1
423-
;
424-
";C2
425-
"ab
426-
";"cd
427-
;
428-
e"
429-
"
430-
431-
432-
433-
1";2
434-
""", new [] { (1, 4), (4, 8), (8, 13) } ],
435-
[ "\"C1\n\";C2\n\"1\n2\r3\";\"4\r\n56\"\n\"7\r\r\r\r\r89\";012\n",
436-
new [] { (1, 3), (3, 7), (7, 13) } ],
421+
["""
422+
"C1
423+
;
424+
";C2
425+
"ab
426+
";"cd
427+
;
428+
e"
429+
"
430+
431+
432+
433+
1";2
434+
""", new[] { (1, 4), (4, 8), (8, 13) }],
435+
["\"C1\n\";C2\n\"1\n2\r3\";\"4\r\n56\"\n\"7\r\r\r\r\r89\";012\n",
436+
new[] { (1, 3), (3, 7), (7, 13) }],
437437
];
438438

439439
[DataTestMethod]

src/Sep.Test/SepWriterColTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ static void Run(SepWriter.ColAction action, string? expectedColValue = ColText,
115115
[
116116
() => Sep.Writer(o => o with { CultureInfo = cultureInfo ?? SepDefaults.CultureInfo }).ToText(),
117117
() => Sep.Default.Writer(o => o with { CultureInfo = cultureInfo ?? SepDefaults.CultureInfo }).ToText(),
118-
() => new SepSpec(Sep.Default, cultureInfo ?? SepDefaults.CultureInfo).Writer(o => o with { }).ToText(),
118+
() => new SepSpec(Sep.Default, cultureInfo ?? SepDefaults.CultureInfo).Writer(o => o with { }).ToText(),
119119
];
120120
foreach (var createWriter in createWriters)
121121
{

src/Sep/SepReaderOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public SepReaderOptions(Sep? sep)
6666
/// state after unescaped cols before next col. This is for efficiency to
6767
/// avoid allocating secondary memory for unescaped columns. Header
6868
/// columns/names will also be unescaped.
69+
/// Requires <see cref="DisableQuotesParsing"/> to be false.
6970
/// </remarks>
7071
public bool Unescape { get; init; } = false;
7172
}

0 commit comments

Comments
 (0)