Skip to content

Commit

Permalink
ForNeVeR#18 implement behavior to calculate parents from exclusively-…
Browse files Browse the repository at this point in the history
…relative paths
  • Loading branch information
Kataane committed Jul 20, 2024
1 parent c128df2 commit 827e82b
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 1 deletion.
42 changes: 42 additions & 0 deletions TruePath.Tests/LocalPathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,46 @@ namespace TruePath.Tests;

public class LocalPathTests
{
[Fact]
public void AnyExclusivelyRelativePath()
{
// Arrange
var dots = new string(Enumerable.Repeat('.', Random.Shared.Next(1, 21)).ToArray());
var backslashes = new string(Enumerable.Repeat(Path.DirectorySeparatorChar, Random.Shared.Next(0, 20)).ToArray());
var result = string.Concat(dots.AsSpan(), backslashes.AsSpan()).ToArray();
Random.Shared.Shuffle(result.ToArray());
var path = new string(result);

var a = new LocalPath(path);

// Act
var parent = a.Parent;

// Assert
Assert.Null(parent);
}

[Theory]
[InlineData(".")]
[InlineData("..")]
[InlineData("../..")]
[InlineData("../../")]
[InlineData("../...")]
[InlineData(".../..")]
[InlineData("./.")]
[InlineData("../../.")]
public void ExclusivelyRelativePath(string path)
{
// Arrange
var a = new LocalPath(path);

// Act
var parent = a.Parent;

// Assert
Assert.Null(parent);
}

[Theory]
[InlineData("user", "user/documents")]
[InlineData("usEr", "User/documents")]
Expand All @@ -14,6 +54,8 @@ public class LocalPathTests
public void IsPrefixOfShouldBeEquivalentToStartsWith(string pathA, string pathB)
{
// Arrange
var y = PathStrings.Normalize("../..");

var a = new LocalPath(pathA);
var b = new LocalPath(pathB);

Expand Down
13 changes: 12 additions & 1 deletion TruePath/LocalPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ public readonly struct LocalPath(string value) : IEquatable<LocalPath>, IPath, I
public bool IsAbsolute => Path.IsPathRooted(Value);

/// <inheritdoc cref="IPath.Parent"/>
public LocalPath? Parent => Path.GetDirectoryName(Value) is { } parent ? new(parent) : null;
public LocalPath? Parent {
get
{
var value = PathStrings.ResolveRelativePaths(Value);
if (string.IsNullOrWhiteSpace(value))
{
return null;
}

return Path.GetDirectoryName(Value) is { } parent ? new(parent) : null;
}
}

/// <inheritdoc cref="IPath.Parent"/>
IPath? IPath.Parent => Parent;
Expand Down
30 changes: 30 additions & 0 deletions TruePath/PathStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,34 @@ private static bool SourceContainsDriveLetter(ReadOnlySpan<char> source)

return DriveLetters.Contains(letter) && colon == ':';
}

/// <summary>
/// Resolves and normalizes relative paths in the given path string by removing redundant path components.
/// </summary>
/// <param name="source">The path string to process.</param>
/// <returns>
/// The normalized path string. If the source path contains only directory separators or dots, an empty string is returned. Otherwise, the original path is returned.
/// </returns>
/// <remarks>
/// This method iterates over the characters in the input path string and removes those that are directory separators or dots. If the resulting length of the path is zero, indicating that the path only contained separators or dots, it returns an empty string. Otherwise, it returns the original path unchanged.
/// </remarks>
internal static string ResolveRelativePaths(string source)
{
if (string.IsNullOrWhiteSpace(source))
{
return string.Empty;
}

var c = source.Length;

foreach (var @char in source)
{
if (@char == Path.DirectorySeparatorChar || @char == '.')
{
c--;
}
}

return c == 0 ? string.Empty : source;
}
}

0 comments on commit 827e82b

Please sign in to comment.