From abc62404fcaf1b2304b76b80aa6781feb0f11710 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:45:05 +0000 Subject: [PATCH 01/10] Initial plan Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/7d6f3603-259f-4fb5-bca3-a3b31e1779cc Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit.Tests/TestResource.txt | 1 + PowerKit/Extensions/AssemblyExtensions.cs | 48 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 PowerKit.Tests/TestResource.txt diff --git a/PowerKit.Tests/TestResource.txt b/PowerKit.Tests/TestResource.txt new file mode 100644 index 0000000..b6fc4c6 --- /dev/null +++ b/PowerKit.Tests/TestResource.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index a123680..78c11e8 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -1,4 +1,8 @@ +using System.IO; using System.Reflection; +using System.Resources; +using System.Threading; +using System.Threading.Tasks; namespace PowerKit.Extensions; @@ -15,5 +19,49 @@ internal static class AssemblyExtensions assembly .GetCustomAttribute() ?.InformationalVersion ?? assembly.GetName().Version?.ToString(); + + /// + /// Extracts the specified manifest resource to a file at the given path. + /// Throws if the resource is not found. + /// + public void ExtractManifestResource(string resourceName, string filePath) + { + var resourceStream = + assembly.GetManifestResourceStream(resourceName) + ?? throw new MissingManifestResourceException( + $"Failed to find resource '{resourceName}'." + ); + + using (resourceStream) + using (var fileStream = File.Create(filePath)) + { + resourceStream.CopyTo(fileStream); + } + } + + /// + /// Extracts the specified manifest resource to a file at the given path asynchronously. + /// Throws if the resource is not found. + /// + public async Task ExtractManifestResourceAsync( + string resourceName, + string filePath, + CancellationToken cancellationToken = default + ) + { + var resourceStream = + assembly.GetManifestResourceStream(resourceName) + ?? throw new MissingManifestResourceException( + $"Failed to find resource '{resourceName}'." + ); + + await using (resourceStream) + await using (var fileStream = File.Create(filePath)) + { + await resourceStream + .CopyToAsync(fileStream, cancellationToken) + .ConfigureAwait(false); + } + } } } From 029e18b86a66d07b7a79f1dc2090b4d1e9f05aaa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:47:24 +0000 Subject: [PATCH 02/10] Add ExtractManifestResource, ExtractManifestResourceAsync, GetManifestResourceString, and GetManifestResourceStringAsync to AssemblyExtensions Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/7d6f3603-259f-4fb5-bca3-a3b31e1779cc Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit.Tests/AssemblyExtensionsTests.cs | 58 +++++++++++++++++++++-- PowerKit.Tests/PowerKit.Tests.csproj | 4 ++ PowerKit/Extensions/AssemblyExtensions.cs | 58 +++++++++++++++++------ 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/PowerKit.Tests/AssemblyExtensionsTests.cs b/PowerKit.Tests/AssemblyExtensionsTests.cs index 1074520..5694354 100644 --- a/PowerKit.Tests/AssemblyExtensionsTests.cs +++ b/PowerKit.Tests/AssemblyExtensionsTests.cs @@ -1,5 +1,8 @@ +using System.IO; using System.Reflection; +using System.Threading.Tasks; using FluentAssertions; +using PowerKit; using PowerKit.Extensions; using Xunit; @@ -7,16 +10,65 @@ namespace PowerKit.Tests; public class AssemblyExtensionsTests { + private static readonly Assembly ThisAssembly = typeof(AssemblyExtensionsTests).Assembly; + + // The embedded resource name follows the default MSBuild convention: + // . with path separators replaced by dots. + private const string ResourceName = "PowerKit.Tests.TestResource.txt"; + [Fact] public void TryGetVersionString_Test() + { + // Act + var version = ThisAssembly.TryGetVersionString(); + + // Assert + version.Should().NotBeNullOrWhiteSpace(); + } + + [Fact] + public void GetManifestResourceString_Test() + { + // Act + var content = ThisAssembly.GetManifestResourceString(ResourceName); + + // Assert + content.Should().Be("hello"); + } + + [Fact] + public async Task GetManifestResourceStringAsync_Test() + { + // Act + var content = await ThisAssembly.GetManifestResourceStringAsync(ResourceName); + + // Assert + content.Should().Be("hello"); + } + + [Fact] + public void ExtractManifestResource_Test() { // Arrange - var assembly = typeof(AssemblyExtensionsTests).Assembly; + using var tempFile = TempFile.Create(); // Act - var version = assembly.TryGetVersionString(); + ThisAssembly.ExtractManifestResource(ResourceName, tempFile.Path); // Assert - version.Should().NotBeNullOrWhiteSpace(); + File.ReadAllText(tempFile.Path).Should().Be("hello"); + } + + [Fact] + public async Task ExtractManifestResourceAsync_Test() + { + // Arrange + using var tempFile = TempFile.Create(); + + // Act + await ThisAssembly.ExtractManifestResourceAsync(ResourceName, tempFile.Path); + + // Assert + File.ReadAllText(tempFile.Path).Should().Be("hello"); } } diff --git a/PowerKit.Tests/PowerKit.Tests.csproj b/PowerKit.Tests/PowerKit.Tests.csproj index 8026c37..ad557c8 100644 --- a/PowerKit.Tests/PowerKit.Tests.csproj +++ b/PowerKit.Tests/PowerKit.Tests.csproj @@ -11,6 +11,10 @@ + + + + diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index 78c11e8..ae855bb 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -1,6 +1,7 @@ using System.IO; using System.Reflection; using System.Resources; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -20,23 +21,57 @@ internal static class AssemblyExtensions .GetCustomAttribute() ?.InformationalVersion ?? assembly.GetName().Version?.ToString(); + /// + /// Reads the specified manifest resource as a UTF-8 string. + /// Throws if the resource is not found. + /// + public string GetManifestResourceString(string resourceName) + { + using var stream = + assembly.GetManifestResourceStream(resourceName) + ?? throw new MissingManifestResourceException( + $"Failed to find resource '{resourceName}'." + ); + + using var reader = new StreamReader(stream, Encoding.UTF8); + + return reader.ReadToEnd(); + } + + /// + /// Reads the specified manifest resource as a UTF-8 string asynchronously. + /// Throws if the resource is not found. + /// + public async Task GetManifestResourceStringAsync( + string resourceName, + CancellationToken cancellationToken = default + ) + { + await using var stream = + assembly.GetManifestResourceStream(resourceName) + ?? throw new MissingManifestResourceException( + $"Failed to find resource '{resourceName}'." + ); + + using var reader = new StreamReader(stream, Encoding.UTF8); + + return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + } + /// /// Extracts the specified manifest resource to a file at the given path. /// Throws if the resource is not found. /// public void ExtractManifestResource(string resourceName, string filePath) { - var resourceStream = + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); - using (resourceStream) - using (var fileStream = File.Create(filePath)) - { - resourceStream.CopyTo(fileStream); - } + using var fileStream = File.Create(filePath); + stream.CopyTo(fileStream); } /// @@ -49,19 +84,14 @@ public async Task ExtractManifestResourceAsync( CancellationToken cancellationToken = default ) { - var resourceStream = + await using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); - await using (resourceStream) - await using (var fileStream = File.Create(filePath)) - { - await resourceStream - .CopyToAsync(fileStream, cancellationToken) - .ConfigureAwait(false); - } + await using var fileStream = File.Create(filePath); + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } } From 2ae24871906b5b0d08c758fb83d365d3a58b1f13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:23:38 +0000 Subject: [PATCH 03/10] Use File.ReadAllTextAsync in ExtractManifestResourceAsync_Test Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/5361544c-ba41-4c23-89ea-d0da8aedc513 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit.Tests/AssemblyExtensionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerKit.Tests/AssemblyExtensionsTests.cs b/PowerKit.Tests/AssemblyExtensionsTests.cs index 5694354..9206c41 100644 --- a/PowerKit.Tests/AssemblyExtensionsTests.cs +++ b/PowerKit.Tests/AssemblyExtensionsTests.cs @@ -69,6 +69,6 @@ public async Task ExtractManifestResourceAsync_Test() await ThisAssembly.ExtractManifestResourceAsync(ResourceName, tempFile.Path); // Assert - File.ReadAllText(tempFile.Path).Should().Be("hello"); + (await File.ReadAllTextAsync(tempFile.Path)).Should().Be("hello"); } } From a30192399af8ae133e7a7a7deff9df5d81e17e74 Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 12 Apr 2026 22:39:16 +0300 Subject: [PATCH 04/10] Remove unnecessary blank lines in AssemblyExtensions.cs --- PowerKit/Extensions/AssemblyExtensions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index ae855bb..a3ec0d9 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -34,7 +34,6 @@ public string GetManifestResourceString(string resourceName) ); using var reader = new StreamReader(stream, Encoding.UTF8); - return reader.ReadToEnd(); } @@ -54,7 +53,6 @@ public async Task GetManifestResourceStringAsync( ); using var reader = new StreamReader(stream, Encoding.UTF8); - return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); } From 72912fc4d90a91622408bb0372f6eec48bfc203c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:39:18 +0000 Subject: [PATCH 05/10] Move TestResource.txt to TestData folder Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/23fb0196-9cbf-45e3-bb20-9242f781f68e Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit.Tests/AssemblyExtensionsTests.cs | 2 +- PowerKit.Tests/PowerKit.Tests.csproj | 2 +- PowerKit.Tests/{ => TestData}/TestResource.txt | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename PowerKit.Tests/{ => TestData}/TestResource.txt (100%) diff --git a/PowerKit.Tests/AssemblyExtensionsTests.cs b/PowerKit.Tests/AssemblyExtensionsTests.cs index 9206c41..f7cd0c0 100644 --- a/PowerKit.Tests/AssemblyExtensionsTests.cs +++ b/PowerKit.Tests/AssemblyExtensionsTests.cs @@ -14,7 +14,7 @@ public class AssemblyExtensionsTests // The embedded resource name follows the default MSBuild convention: // . with path separators replaced by dots. - private const string ResourceName = "PowerKit.Tests.TestResource.txt"; + private const string ResourceName = "PowerKit.Tests.TestData.TestResource.txt"; [Fact] public void TryGetVersionString_Test() diff --git a/PowerKit.Tests/PowerKit.Tests.csproj b/PowerKit.Tests/PowerKit.Tests.csproj index ad557c8..a4158eb 100644 --- a/PowerKit.Tests/PowerKit.Tests.csproj +++ b/PowerKit.Tests/PowerKit.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/PowerKit.Tests/TestResource.txt b/PowerKit.Tests/TestData/TestResource.txt similarity index 100% rename from PowerKit.Tests/TestResource.txt rename to PowerKit.Tests/TestData/TestResource.txt From 24d45120a5180171d8b54536533b2e059366377a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:42:38 +0000 Subject: [PATCH 06/10] Replace await using with using var for netstandard2.0 compatibility Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/09955441-4d97-4041-b566-8365660c7d34 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit/Extensions/AssemblyExtensions.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index a3ec0d9..b197066 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -46,14 +46,15 @@ public async Task GetManifestResourceStringAsync( CancellationToken cancellationToken = default ) { - await using var stream = + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); + cancellationToken.ThrowIfCancellationRequested(); using var reader = new StreamReader(stream, Encoding.UTF8); - return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + return await reader.ReadToEndAsync().ConfigureAwait(false); } /// @@ -82,14 +83,21 @@ public async Task ExtractManifestResourceAsync( CancellationToken cancellationToken = default ) { - await using var stream = + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); - await using var fileStream = File.Create(filePath); - await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); + using var fileStream = new FileStream( + filePath, + FileMode.Create, + FileAccess.Write, + FileShare.None, + 81920, + FileOptions.Asynchronous + ); + await stream.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false); } } } From c35b3ab98cee684f32ebd680b2a197be9b30554c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:44:49 +0000 Subject: [PATCH 07/10] Add encoding overloads for GetManifestResourceString and GetManifestResourceStringAsync Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/17bd53fb-ccc8-4472-b8d6-2ba22ff107a7 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit/Extensions/AssemblyExtensions.cs | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index b197066..0313e29 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -22,10 +22,10 @@ internal static class AssemblyExtensions ?.InformationalVersion ?? assembly.GetName().Version?.ToString(); /// - /// Reads the specified manifest resource as a UTF-8 string. + /// Reads the specified manifest resource as a string using the specified encoding. /// Throws if the resource is not found. /// - public string GetManifestResourceString(string resourceName) + public string GetManifestResourceString(string resourceName, Encoding encoding) { using var stream = assembly.GetManifestResourceStream(resourceName) @@ -33,16 +33,24 @@ public string GetManifestResourceString(string resourceName) $"Failed to find resource '{resourceName}'." ); - using var reader = new StreamReader(stream, Encoding.UTF8); + using var reader = new StreamReader(stream, encoding); return reader.ReadToEnd(); } /// - /// Reads the specified manifest resource as a UTF-8 string asynchronously. + /// Reads the specified manifest resource as a UTF-8 string. + /// Throws if the resource is not found. + /// + public string GetManifestResourceString(string resourceName) => + assembly.GetManifestResourceString(resourceName, Encoding.UTF8); + + /// + /// Reads the specified manifest resource as a string using the specified encoding asynchronously. /// Throws if the resource is not found. /// public async Task GetManifestResourceStringAsync( string resourceName, + Encoding encoding, CancellationToken cancellationToken = default ) { @@ -53,10 +61,19 @@ public async Task GetManifestResourceStringAsync( ); cancellationToken.ThrowIfCancellationRequested(); - using var reader = new StreamReader(stream, Encoding.UTF8); + using var reader = new StreamReader(stream, encoding); return await reader.ReadToEndAsync().ConfigureAwait(false); } + /// + /// Reads the specified manifest resource as a UTF-8 string asynchronously. + /// Throws if the resource is not found. + /// + public Task GetManifestResourceStringAsync( + string resourceName, + CancellationToken cancellationToken = default + ) => assembly.GetManifestResourceStringAsync(resourceName, Encoding.UTF8, cancellationToken); + /// /// Extracts the specified manifest resource to a file at the given path. /// Throws if the resource is not found. From 03a28b1f599ab772e2683ad220df8e18f80068ef Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Sun, 12 Apr 2026 22:46:47 +0300 Subject: [PATCH 08/10] Update AssemblyExtensions.cs --- PowerKit/Extensions/AssemblyExtensions.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index 0313e29..9e5417f 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -80,14 +80,15 @@ public Task GetManifestResourceStringAsync( /// public void ExtractManifestResource(string resourceName, string filePath) { - using var stream = + using var source = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); - using var fileStream = File.Create(filePath); - stream.CopyTo(fileStream); + using var destination = File.Create(filePath); + source.CopyTo(destination); + destination.Flush(); } /// @@ -100,13 +101,13 @@ public async Task ExtractManifestResourceAsync( CancellationToken cancellationToken = default ) { - using var stream = + using var source = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException( $"Failed to find resource '{resourceName}'." ); - using var fileStream = new FileStream( + using var destination = new FileStream( filePath, FileMode.Create, FileAccess.Write, @@ -114,7 +115,9 @@ public async Task ExtractManifestResourceAsync( 81920, FileOptions.Asynchronous ); - await stream.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false); + + await source.CopyToAsync(destination, 81920, cancellationToken).ConfigureAwait(false); + await destination.FlushAsync(cancellationToken).ConfigureAwait(false); } } } From 48fd46510893bfdb53c5eed0ae334dc9b2729071 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:48:26 +0000 Subject: [PATCH 09/10] Revert ThrowIfCancellationRequested workaround, restore ReadToEndAsync(cancellationToken) Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/df4c76b7-6408-4110-b1ab-a043952f5807 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit/Extensions/AssemblyExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index 9e5417f..e2c6d84 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -60,9 +60,8 @@ public async Task GetManifestResourceStringAsync( $"Failed to find resource '{resourceName}'." ); - cancellationToken.ThrowIfCancellationRequested(); using var reader = new StreamReader(stream, encoding); - return await reader.ReadToEndAsync().ConfigureAwait(false); + return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); } /// From dd28579565b4fc2166cf35307c7376da4eaf021e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:55:50 +0000 Subject: [PATCH 10/10] Use async/await in GetManifestResourceStringAsync UTF-8 overload Agent-Logs-Url: https://github.com/Tyrrrz/PowerKit/sessions/1544d98f-bcd9-413b-8d10-e8c611e51498 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PowerKit/Extensions/AssemblyExtensions.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PowerKit/Extensions/AssemblyExtensions.cs b/PowerKit/Extensions/AssemblyExtensions.cs index e2c6d84..4264bb0 100644 --- a/PowerKit/Extensions/AssemblyExtensions.cs +++ b/PowerKit/Extensions/AssemblyExtensions.cs @@ -68,10 +68,13 @@ public async Task GetManifestResourceStringAsync( /// Reads the specified manifest resource as a UTF-8 string asynchronously. /// Throws if the resource is not found. /// - public Task GetManifestResourceStringAsync( + public async Task GetManifestResourceStringAsync( string resourceName, CancellationToken cancellationToken = default - ) => assembly.GetManifestResourceStringAsync(resourceName, Encoding.UTF8, cancellationToken); + ) => + await assembly + .GetManifestResourceStringAsync(resourceName, Encoding.UTF8, cancellationToken) + .ConfigureAwait(false); /// /// Extracts the specified manifest resource to a file at the given path.