From 3ce8d281b792ee8e58a66a1f0651711c5fd070f7 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Thu, 30 Mar 2023 17:55:41 +0100 Subject: [PATCH] feat: add TextReader.EnumerateLines/Async --- CHANGELOG.md | 1 + X10D.Tests/src/IO/TextReaderTests.cs | 83 ++++++++++++++++++++++++++++ X10D/src/IO/TextReaderExtensions.cs | 53 ++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 X10D.Tests/src/IO/TextReaderTests.cs create mode 100644 X10D/src/IO/TextReaderExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d88d34e..6ff7772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - X10D: Added `string.IsWhiteSpace()`. - X10D: Added `string.IsNullOrEmpty()`. - X10D: Added `string.IsNullOrWhiteSpace()`. +- X10D: Added `TextReader.EnumerateLines()` and `TextReader.EnumerateLinesAsync()`. - X10D: Added `TimeSpan.TryParse(ReadOnlySpan, out TimeSpan)`. - X10D: Added `Quaternion.Multiply(Vector3)` - this functions as an equivalent to Unity's `Quaternion * Vector3` operator. - X10D: Added `Vector2.Deconstruct()`. diff --git a/X10D.Tests/src/IO/TextReaderTests.cs b/X10D.Tests/src/IO/TextReaderTests.cs new file mode 100644 index 0000000..6e10ef4 --- /dev/null +++ b/X10D.Tests/src/IO/TextReaderTests.cs @@ -0,0 +1,83 @@ +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using X10D.IO; + +namespace X10D.Tests.IO; + +[TestClass] +public class TextReaderTests +{ + [TestMethod] + public void EnumerateLines_ShouldYield10Lines_Given10LineString() + { + using var stream = new MemoryStream(); + using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true)) + { + for (var index = 0; index < 10; index++) + { + writer.WriteLine(index); + } + } + + stream.Position = 0; + using var reader = new StreamReader(stream, Encoding.UTF8); + var lineCount = 0; + + foreach (string _ in reader.EnumerateLines()) + { + lineCount++; + } + + Assert.AreEqual(10, lineCount); + } + + [TestMethod] + public async Task EnumerateLinesAsync_ShouldYield10Lines_Given10LineString() + { + using var stream = new MemoryStream(); + await using (var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true)) + { + for (var index = 0; index < 10; index++) + { + writer.WriteLine(index); + } + } + + stream.Position = 0; + using var reader = new StreamReader(stream, Encoding.UTF8); + var lineCount = 0; + + await foreach (string _ in reader.EnumerateLinesAsync().ConfigureAwait(false)) + { + lineCount++; + } + + Assert.AreEqual(10, lineCount); + } + + [TestMethod] + public void EnumerateLines_ShouldThrowArgumentNullException_GivenNullSource() + { + TextReader reader = null!; + Assert.ThrowsException(() => + { + foreach (string _ in reader.EnumerateLines()) + { + // loop body is intentionally empty + } + }); + } + + [TestMethod] + public async Task EnumerateLinesAsync_ShouldThrowArgumentNullException_GivenNullSource() + { + TextReader reader = null!; + await Assert.ThrowsExceptionAsync(async () => + { + await foreach (string _ in reader.EnumerateLinesAsync().ConfigureAwait(false)) + { + // loop body is intentionally empty + } + }).ConfigureAwait(false); + } +} diff --git a/X10D/src/IO/TextReaderExtensions.cs b/X10D/src/IO/TextReaderExtensions.cs new file mode 100644 index 0000000..4ecb213 --- /dev/null +++ b/X10D/src/IO/TextReaderExtensions.cs @@ -0,0 +1,53 @@ +namespace X10D.IO; + +/// +/// IO-related extension methods for . +/// +public static class TextReaderExtensions +{ + /// + /// Enumerates the lines provided by the current text reader. + /// + /// The reader whose lines to enumerate. + /// An enumerable collection of lines as read from . + /// is . + public static IEnumerable EnumerateLines(this TextReader reader) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(reader); +#else + if (reader is null) + { + throw new ArgumentNullException(nameof(reader)); + } +#endif + + while (reader.ReadLine() is { } line) + { + yield return line; + } + } + + /// + /// Asynchronously enumerates the lines provided by the current text reader. + /// + /// The reader whose lines to enumerate. + /// An asynchronous enumerable collection of lines as read from . + /// is . + public static async IAsyncEnumerable EnumerateLinesAsync(this TextReader reader) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(reader); +#else + if (reader is null) + { + throw new ArgumentNullException(nameof(reader)); + } +#endif + + while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line) + { + yield return line; + } + } +}