Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions Semantics.Paths/Implementations/RelativeDirectoryPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

namespace ktsu.Semantics.Paths;

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
#if !NET5_0_OR_GREATER
using System.Runtime.InteropServices;
#endif

/// <summary>
/// Represents a relative directory path
Expand Down Expand Up @@ -209,18 +207,26 @@ public RelativeDirectoryPath AsRelative(AbsoluteDirectoryPath baseDirectory)
return this;
}

// Use Path.GetFullPath with a dummy base to normalize relative paths
#if NET5_0_OR_GREATER
string dummyBase = OperatingSystem.IsWindows() ? "C:\\" : "/";
#else
string dummyBase = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "C:\\" : "/";
#endif
string fullPath = Path.GetFullPath(Path.Combine(dummyBase, path));
#if NETSTANDARD2_0
string normalized = PathPolyfill.GetRelativePath(dummyBase, fullPath);
#else
string normalized = Path.GetRelativePath(dummyBase, fullPath);
#endif
// Resolve segments lexically. Resolving against a dummy root would discard every ".." that
// climbs above the starting point, changing which directory the path names.
List<string> segments = [];
foreach (string segment in path.Split(['/', '\\'], StringSplitOptions.RemoveEmptyEntries))
{
if (segment == ".")
{
continue;
}

if (segment == ".." && segments.Count > 0 && segments[^1] != "..")
{
segments.RemoveAt(segments.Count - 1);
continue;
}

segments.Add(segment);
}

string normalized = segments.Count == 0 ? "." : string.Join(Path.DirectorySeparatorChar.ToString(), segments);

return Create<RelativeDirectoryPath>(normalized);
}
Expand Down
18 changes: 16 additions & 2 deletions Semantics.Test/Paths/PathUtilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,22 @@ public void Normalize_WithOnlyDots_ResolvesCorrectly()

RelativeDirectoryPath normalized = dotPath.Normalize();

Assert.IsNotNull(normalized);
Assert.Contains("folder", normalized.WeakString);
Assert.AreEqual(TestPaths.Relative("..", "folder"), normalized.WeakString);
}

[TestMethod]
[DataRow(new[] { "..", "sibling" }, new[] { "..", "sibling" })]
[DataRow(new[] { "a", "..", "..", "b" }, new[] { "..", "b" })]
[DataRow(new[] { "..", "..", "x", "y" }, new[] { "..", "..", "x", "y" })]
[DataRow(new[] { "a", ".", "b", "..", "c" }, new[] { "a", "c" })]
[DataRow(new[] { "a", ".." }, new[] { "." })]
public void Normalize_KeepsParentSegmentsThatClimbAboveTheStart(string[] input, string[] expected)
{
RelativeDirectoryPath path = RelativeDirectoryPath.Create<RelativeDirectoryPath>(TestPaths.Relative(input));

RelativeDirectoryPath normalized = path.Normalize();

Assert.AreEqual(TestPaths.Relative(expected), normalized.WeakString);
}

[TestMethod]
Expand Down
Loading