diff --git a/PolyShim.Tests/NetCore30/PathTests.cs b/PolyShim.Tests/NetCore30/PathTests.cs new file mode 100644 index 0000000..4c9b25c --- /dev/null +++ b/PolyShim.Tests/NetCore30/PathTests.cs @@ -0,0 +1,44 @@ +using System.IO; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore30; + +public class PathTests +{ + [Fact] + public void EndsInDirectorySeparator_Test() + { + // Act & assert + Path.EndsInDirectorySeparator("C:\\Folder\\Subfolder\\").Should().BeTrue(); + Path.EndsInDirectorySeparator("C:/Folder/Subfolder/").Should().BeTrue(); + Path.EndsInDirectorySeparator("C:\\Folder\\Subfolder").Should().BeFalse(); + Path.EndsInDirectorySeparator("C:/Folder/Subfolder").Should().BeFalse(); + Path.EndsInDirectorySeparator("/a/b/c/").Should().BeTrue(); + Path.EndsInDirectorySeparator("/a/b/c").Should().BeFalse(); + Path.EndsInDirectorySeparator(string.Empty).Should().BeFalse(); + } + + [Fact] + public void TrimEndingDirectorySeparator_Test() + { + // Act & assert + Path.TrimEndingDirectorySeparator("C:\\Folder\\Subfolder\\") + .Should() + .Be("C:\\Folder\\Subfolder"); + Path.TrimEndingDirectorySeparator("C:\\Folder\\Subfolder\\\\") + .Should() + .Be("C:\\Folder\\Subfolder\\"); + Path.TrimEndingDirectorySeparator("C:/Folder/Subfolder/") + .Should() + .Be("C:/Folder/Subfolder"); + Path.TrimEndingDirectorySeparator("C:\\Folder\\Subfolder") + .Should() + .Be("C:\\Folder\\Subfolder"); + Path.TrimEndingDirectorySeparator("C:/Folder/Subfolder").Should().Be("C:/Folder/Subfolder"); + Path.TrimEndingDirectorySeparator("/a/b/c/").Should().Be("/a/b/c"); + Path.TrimEndingDirectorySeparator("/a/b/c//").Should().Be("/a/b/c/"); + Path.TrimEndingDirectorySeparator("/a/b/c").Should().Be("/a/b/c"); + Path.TrimEndingDirectorySeparator(string.Empty).Should().Be(string.Empty); + } +} diff --git a/PolyShim/Net70/Stream.cs b/PolyShim/Net70/Stream.cs index 51ab95b..8d6cf12 100644 --- a/PolyShim/Net70/Stream.cs +++ b/PolyShim/Net70/Stream.cs @@ -146,7 +146,7 @@ public int ReadAtLeast( var totalBytesRead = 0; while (totalBytesRead < buffer.Length) { - var bytesRead = stream.Read(buffer.Slice(totalBytesRead)); + var bytesRead = stream.Read(buffer[totalBytesRead..]); if (bytesRead <= 0) break; @@ -181,7 +181,7 @@ public async Task ReadAtLeastAsync( while (totalBytesRead < buffer.Length) { var bytesRead = await stream - .ReadAsync(buffer.Slice(totalBytesRead), cancellationToken) + .ReadAsync(buffer[totalBytesRead..], cancellationToken) .ConfigureAwait(false); if (bytesRead <= 0) diff --git a/PolyShim/NetCore10/RuntimeInformation.cs b/PolyShim/NetCore10/RuntimeInformation.cs index 65511b7..6d02023 100644 --- a/PolyShim/NetCore10/RuntimeInformation.cs +++ b/PolyShim/NetCore10/RuntimeInformation.cs @@ -2,8 +2,6 @@ // #if !FEATURE_RUNTIMEINFORMATION -// No way to detect on .NET Standard lower than 1.3 -#if !(NETSTANDARD && !NETSTANDARD1_3_OR_GREATER) #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -19,6 +17,8 @@ namespace System.Runtime.InteropServices; [ExcludeFromCodeCoverage] internal static class RuntimeInformation { +// No way to detect on .NET Standard lower than 1.3 +#if !(NETSTANDARD && !NETSTANDARD1_3_OR_GREATER) public static bool IsOSPlatform(OSPlatform osPlatform) { if (osPlatform == OSPlatform.FreeBSD) @@ -49,6 +49,6 @@ or PlatformID.Win32S return false; } -} #endif +} #endif diff --git a/PolyShim/NetCore30/Path.cs b/PolyShim/NetCore30/Path.cs new file mode 100644 index 0000000..0d1de89 --- /dev/null +++ b/PolyShim/NetCore30/Path.cs @@ -0,0 +1,39 @@ +// The following comment is required to instruct analyzers to skip this file +// + +#if (NETCOREAPP && !NETCOREAPP3_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD) +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System.IO; + +internal static partial class PolyfillExtensions +{ + extension(Path) + { + // https://learn.microsoft.com/dotnet/api/system.io.path.endsindirectoryseparator#system-io-path-endsindirectoryseparator(system-string) + public static bool EndsInDirectorySeparator(string? path) + { + if (string.IsNullOrEmpty(path)) + return false; + + var lastChar = path![^1]; + +#if !NETSTANDARD || NETSTANDARD1_3_OR_GREATER + return lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar; +#else + return lastChar is '\\' or '/'; +#endif + } + + // https://learn.microsoft.com/dotnet/api/system.io.path.trimendingdirectoryseparator#system-io-path-trimendingdirectoryseparator(system-string) + public static string TrimEndingDirectorySeparator(string path) => + EndsInDirectorySeparator(path) && !Path.GetPathRoot(path).Equals(path, System.StringComparison.Ordinal) + ? path[..^1] + : path; + } +} +#endif \ No newline at end of file