Skip to content

Commit

Permalink
Merge pull request #6 from rameel/cleanup
Browse files Browse the repository at this point in the history
FilePath improvements
  • Loading branch information
rameel authored Aug 6, 2024
2 parents 0cab1e1 + dbec8c7 commit 64af83b
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 81 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
Represents a lightweight .NET library of useful and convenient extensions for `Microsoft.Extensions.FileProviders`
that enhances file handling capabilities in .NET applications.

[![.NET](https://github.com/rameel/ramstack.fileproviders/actions/workflows/test.yml/badge.svg)](https://github.com/rameel/ramstack.fileproviders/actions/workflows/test.yml)

## Getting Started

To install the `Ramstack.FileProviders` [NuGet package](https://www.nuget.org/packages/Ramstack.FileProviders)
Expand Down
40 changes: 37 additions & 3 deletions Ramstack.FileProviders.Tests/FilePathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@ namespace Ramstack.FileProviders;
[TestFixture]
public class FilePathTests
{
[TestCase("", ExpectedResult = "")]
[TestCase(".", ExpectedResult = ".")]
[TestCase("/", ExpectedResult = "")]
[TestCase("/.", ExpectedResult = ".")]
[TestCase("file.txt", ExpectedResult = ".txt")]
[TestCase("/path/to/file.txt", ExpectedResult = ".txt")]
[TestCase("/path/to/.hidden", ExpectedResult = ".hidden")]
[TestCase("/path/to/file", ExpectedResult = "")]
[TestCase("/path.with.dots/to/file.txt", ExpectedResult = ".txt")]
[TestCase("/path/with.dots/file.", ExpectedResult = ".")]
[TestCase("/path.with.dots/to/.hidden.ext", ExpectedResult = ".ext")]
[TestCase("file.with.multiple.dots.ext", ExpectedResult = ".ext")]
[TestCase("/path/to/file.with.multiple.dots.ext", ExpectedResult = ".ext")]
[TestCase("/.hidden", ExpectedResult = ".hidden")]
public string GetExtension(string path) =>
FilePath.GetExtension(path);

[TestCase("", ExpectedResult = "")]
[TestCase(".", ExpectedResult = ".")]
[TestCase(".hidden", ExpectedResult = ".hidden")]
[TestCase("file.txt", ExpectedResult = "file.txt")]
[TestCase("/path/to/file.txt", ExpectedResult = "file.txt")]
[TestCase("/path/to/.hidden", ExpectedResult = ".hidden")]
[TestCase("/path/to/file", ExpectedResult = "file")]
[TestCase("/path/with.dots/file.txt", ExpectedResult = "file.txt")]
[TestCase("/path/with.dots/file.", ExpectedResult = "file.")]
[TestCase("/path/to/file.with.multiple.dots.ext", ExpectedResult = "file.with.multiple.dots.ext")]
[TestCase("/path/to/.hidden.ext", ExpectedResult = ".hidden.ext")]
[TestCase("/.hidden", ExpectedResult = ".hidden")]
[TestCase("/path/to/", ExpectedResult = "")]
[TestCase("/path/to/directory/", ExpectedResult = "")]
public string GetFileNameTest(string path) =>
FilePath.GetFileName(path);

[TestCase("/", ExpectedResult = true)]
[TestCase("/a/b/c", ExpectedResult = true)]
[TestCase("/a/./b/c", ExpectedResult = true)]
Expand Down Expand Up @@ -78,15 +112,15 @@ public void GetFullPath_Error(string path) =>
public bool IsNavigatesAboveRoot(string path) =>
FilePath.IsNavigatesAboveRoot(path);

[TestCase("/", ExpectedResult = null)]
[TestCase("/", ExpectedResult = "")]
[TestCase("/dir", ExpectedResult = "/")]
[TestCase("/dir/file", ExpectedResult = "/dir")]
[TestCase("/dir/dir/", ExpectedResult = "/dir/dir")]
[TestCase("dir/dir", ExpectedResult = "dir")]
[TestCase("dir/dir/", ExpectedResult = "dir/dir")]

[TestCase("//", ExpectedResult = null)]
[TestCase("///", ExpectedResult = null)]
[TestCase("//", ExpectedResult = "")]
[TestCase("///", ExpectedResult = "")]
[TestCase("//dir", ExpectedResult = "/")]
[TestCase("///dir", ExpectedResult = "/")]
[TestCase("////dir", ExpectedResult = "/")]
Expand Down
32 changes: 1 addition & 31 deletions Ramstack.FileProviders/DirectoryNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public DirectoryNode? Parent
if (IsRoot)
return null;

var parent = FilePath.GetDirectoryName(FullName)!;
var parent = FilePath.GetDirectoryName(FullName);
var directory = Provider.GetDirectoryContents(parent);
return new DirectoryNode(Provider, parent, directory);
}
Expand Down Expand Up @@ -77,36 +77,6 @@ internal DirectoryNode(IFileProvider provider, string path, IFileInfo file) : ba
internal DirectoryNode(IFileProvider provider, string path, IDirectoryContents directory) : base(provider, path) =>
_directory = directory;

/// <summary>
/// Returns a directory with the specified path.
/// </summary>
/// <param name="path">The path that identifies the directory.</param>
/// <returns>
/// The <see cref="DirectoryNode"/> representing the desired directory.
/// </returns>
public DirectoryNode GetDirectory(string path)
{
path = FilePath.GetFullPath(FilePath.Combine(FullName, path));

var directory = Provider.GetDirectoryContents(path);
return new DirectoryNode(Provider, path, directory);
}

/// <summary>
/// Returns a file with the specified path.
/// </summary>
/// <param name="path">The path that identifies the file.</param>
/// <returns>
/// The <see cref="FileNode"/> representing the desired file.
/// </returns>
public FileNode GetFile(string path)
{
path = FilePath.GetFullPath(FilePath.Combine(FullName, path));

var file = Provider.GetFileInfo(path);
return new FileNode(Provider, path, file);
}

/// <summary>
/// Returns an enumerable collection of files in the current directory.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions Ramstack.FileProviders/FileNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public sealed class FileNode : FileNodeBase
/// <summary>
/// Gets the string representing the extension part of the file.
/// </summary>
public string Extension => Path.GetExtension(FullName);
public string Extension => FilePath.GetExtension(FullName);

/// <summary>
/// Gets the full path of the directory containing the file.
/// </summary>
public string DirectoryName => FilePath.GetDirectoryName(FullName)!;
public string DirectoryName => FilePath.GetDirectoryName(FullName);

/// <summary>
/// Gets the last write time of the current file.
Expand All @@ -39,7 +39,7 @@ public DirectoryNode Directory
{
get
{
var path = FilePath.GetDirectoryName(FullName)!;
var path = FilePath.GetDirectoryName(FullName);
var directory = Provider.GetDirectoryContents(path);
return new DirectoryNode(Provider, path, directory);
}
Expand Down
4 changes: 3 additions & 1 deletion Ramstack.FileProviders/FileNodeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

using Microsoft.Extensions.FileProviders;

using Ramstack.FileProviders.Internal;

namespace Ramstack.FileProviders;

/// <summary>
Expand All @@ -23,7 +25,7 @@ public abstract class FileNodeBase
/// <summary>
/// Gets the name of the file or directory.
/// </summary>
public string Name => Path.GetFileName(FullName);
public string Name => FilePath.GetFileName(FullName);

/// <summary>
/// Gets a value indicating whether the file or directory exists.
Expand Down
83 changes: 44 additions & 39 deletions Ramstack.FileProviders/Internal/FilePath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,54 @@ internal static class FilePath
private const int StackallocThreshold = 160;

/// <summary>
/// Returns the directory portion of a file path.
/// Gets the extension part of the specified path string, including the leading dot <c>.</c>
/// even if it is the entire file name, or an empty string if no extension is present.
/// </summary>
/// <remarks>
/// Returns a string consisting of all characters up to but not including the last
/// forward slash (<c>/</c>) in the file path. The returned value is <see langword="null" />
/// if the specified path is a root (<c>/</c>).
/// </remarks>
/// <param name="path">The path of a file or directory.</param>
/// <param name="path">The path string from which to get the extension.</param>
/// <returns>
/// Directory information for path, or <see langword="null" /> if path denotes a root directory.
/// The extension of the specified path, including the period <c>.</c>,
/// or an empty string if no extension is present.
/// </returns>
public static string? GetDirectoryName(string path)
public static string GetExtension(string path)
{
if (string.IsNullOrEmpty(path))
return null;
for (var i = path.Length - 1; i >= 0; i--)
{
if (path[i] == '.')
return path.AsSpan(i).ToString();

if (path[i] == '/')
break;
}

return "";
}

/// <summary>
/// Returns the file name and extension for the specified path.
/// </summary>
/// <param name="path">The path from which to obtain the file name and extension.</param>
/// <returns>
/// The file name and extension for the <paramref name="path"/>.
/// </returns>
public static string GetFileName(string path)
{
var p = path.AsSpan();

var start = p.LastIndexOf('/');
return start >= 0
? p.Slice(start + 1).ToString()
: path;
}

/// <summary>
/// Returns the directory portion for the specified path.
/// </summary>
/// <param name="path">The path to retrieve the directory portion from.</param>
/// <returns>
/// Directory portion for <paramref name="path"/>, or an empty string if path denotes a root directory.
/// </returns>
public static string GetDirectoryName(string path)
{
var index = path.AsSpan().LastIndexOf('/');
if (index < 0)
return "";
Expand All @@ -41,7 +73,7 @@ internal static class FilePath

return p switch
{
0 when index + 1 == path.Length => null,
0 when index + 1 == path.Length => "",
0 => "/",
_ => path[..p]
};
Expand Down Expand Up @@ -288,33 +320,6 @@ public static string Join(string path1, string path2)
return string.Concat(path1, "/", path2);
}

/// <summary>
/// Combines two strings into a path.
/// </summary>
/// <param name="path1">The first path to combine.</param>
/// <param name="path2">The second path to combine.</param>
/// <returns>
/// The combined paths.
/// If one of the specified paths is a zero-length string, this method returns the other path.
/// If <paramref name="path2" /> contains an absolute path, this method returns <paramref name="path2" />.
/// </returns>
public static string Combine(string path1, string path2)
{
if (path2.Length == 0)
return path1;

if (path1.Length == 0)
return path2;

if (HasLeadingSlash(path2))
return path2;

if (HasTrailingSlash(path1))
return path1 + path2;

return path1 + "/" + path2;
}

/// <summary>
/// Determines whether the specified path string starts with a directory separator.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions Ramstack.FileProviders/ZipFileProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private static void Initialize(ZipArchive archive, Dictionary<string, IFileInfo>
continue;

var path = FilePath.Normalize(entry.FullName);
var directory = GetDirectory(FilePath.GetDirectoryName(path)!);
var directory = GetDirectory(FilePath.GetDirectoryName(path));
var file = new ZipFileInfo(entry);

directory.RegisterFile(file);
Expand All @@ -101,7 +101,7 @@ ZipDirectoryInfo GetDirectory(string path)
return (ZipDirectoryInfo)di;

di = new ZipDirectoryInfo(path);
var parent = GetDirectory(FilePath.GetDirectoryName(path)!);
var parent = GetDirectory(FilePath.GetDirectoryName(path));
parent.RegisterFile(di);
cache.Add(path, di);

Expand Down

0 comments on commit 64af83b

Please sign in to comment.