diff --git a/README.md b/README.md index d0778a3..6c57a99 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Aside from the strict types, the following features are supported for the paths: - `LocalPath::IsAbsolute` to check the path kind (since it supports both kinds); - `LocalPath::IsPrefixOf` to check path prefixes; - `LocalPath::RelativeTo` to get a relative part between two paths, if possible; -- extension methods on `IPath`: `GetExtensionWithDot` and `GetExtensionWithoutDot` to get the file extension with or without the leading dot (note they will return `null` for paths with no extensions: this will allow you to distinguish between paths with empty extension aka ending with dot, and paths with no dot whatsoever). +- extension methods on `IPath`: `GetExtensionWithDot` and `GetExtensionWithoutDot` to get the file extension with or without the leading dot (note that `GetExtensionWithDot` will behave differently for paths ending with dots and paths without dot at all, which allows to reconstruct such a file name from its part without extension and the "extension with dot"). Documentation ------------- diff --git a/TruePath.Tests/PathExtensionsTests.cs b/TruePath.Tests/PathExtensionsTests.cs index 507d1f8..0f67054 100644 --- a/TruePath.Tests/PathExtensionsTests.cs +++ b/TruePath.Tests/PathExtensionsTests.cs @@ -7,12 +7,13 @@ namespace TruePath.Tests; public class PathExtensionsTests { [Theory] + [InlineData("..", ".")] [InlineData("foo/bar.txt", ".txt")] [InlineData("/foo/bar.txt", ".txt")] - [InlineData("foo/bar.", "")] - [InlineData("foo/bar", null)] + [InlineData("foo/bar.", ".")] + [InlineData("foo/bar", "")] [InlineData(".gitignore", ".gitignore")] - public void GetExtensionWithDotTests(string path, string? expected) + public void GetExtensionWithDotTests(string path, string expected) { IPath local = new LocalPath(path); Assert.Equal(expected, local.GetExtensionWithDot()); @@ -24,12 +25,14 @@ public void GetExtensionWithDotTests(string path, string? expected) } [Theory] + [InlineData(".", "")] + [InlineData("..", "")] [InlineData("foo/bar.txt", "txt")] [InlineData("/foo/bar.txt", "txt")] [InlineData("foo/bar.", "")] - [InlineData("foo/bar", null)] + [InlineData("foo/bar", "")] [InlineData(".gitignore", "gitignore")] - public void GetExtensionWithoutDotTests(string path, string? expected) + public void GetExtensionWithoutDotTests(string path, string expected) { IPath l = new LocalPath(path); Assert.Equal(expected, l.GetExtensionWithoutDot()); diff --git a/TruePath/PathExtensions.cs b/TruePath/PathExtensions.cs index d7aeaa0..460c4ae 100644 --- a/TruePath/PathExtensions.cs +++ b/TruePath/PathExtensions.cs @@ -12,38 +12,30 @@ public static class PathExtensions /// /// Gets the extension of the file name of the with the dot character. /// For example, for the path file.txt, this method will return a string .txt. - /// - /// Note that this method will return null for paths without extensions, and will return an empty - /// string for paths whose names end with a dot (even though it is an unusual path). This behavior allows to - /// distinguish such paths. - /// /// File name entirely consisting of extension, such as .gitignore, is returned as-is. /// - /// The extension of the file name of the path with the dot. - /// - public static string? GetExtensionWithDot(this IPath path) - { - var fileExtenstion = Path.GetExtension(path.FileName); - if (string.IsNullOrEmpty(fileExtenstion) && !path.FileName.EndsWith('.')) - return null; - return fileExtenstion; - } + /// The extension of the file name of the path with the dot character (if present). + /// + /// This method will return an empty string for paths without extensions, and will return a dot for paths whose + /// names end with a dot (even though it is an unusual path). This behavior allows to distinguish such paths, + /// and potentially reconstruct the file name from its part without the extension and the "extension with dot". + /// + public static string GetExtensionWithDot(this IPath path) => + path.FileName.EndsWith('.') ? "." : Path.GetExtension(path.FileName); /// /// Gets the extension of the file name of the without the dot character. /// For example, for the path file.txt, this method will return a string txt. /// - /// Note that this method will return null for paths without extensions, and will return an empty - /// string for paths whose names end with a dot (even though it is an unusual path). This behavior allows to - /// distinguish such paths. - /// - /// /// File name entirely consisting of extension, such as .gitignore, is returned with its leading dot /// trimmed. /// /// - /// - /// The extension of the file name of the path without the dot. - /// - public static string? GetExtensionWithoutDot(this IPath path) => GetExtensionWithDot(path)?.TrimStart('.'); + /// The extension of the file name of the path without the dot. + /// + /// This method will return an empty string for paths without extensions and with empty extensions (ending with + /// dot, which may be unusual). This behavior doesn't allow to distinguish such paths using this method, to + /// reconstruct the original name from its name without extension and its extension without dot. + /// + public static string GetExtensionWithoutDot(this IPath path) => GetExtensionWithDot(path).TrimStart('.'); }