Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileTree abstraction #11

Merged
merged 20 commits into from
Aug 23, 2023
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
96 changes: 84 additions & 12 deletions src/NexusMods.Paths/AbsolutePath.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using NexusMods.Paths.Extensions;
Expand All @@ -11,7 +13,7 @@ namespace NexusMods.Paths;
/// A path that represents a full path to a file or directory.
/// </summary>
[PublicAPI]
public readonly partial struct AbsolutePath : IEquatable<AbsolutePath>, IPath
public readonly partial struct AbsolutePath : IEquatable<AbsolutePath>, IPath<AbsolutePath>
{
/// <summary>
/// The directory component of the path.
Expand All @@ -33,6 +35,9 @@ namespace NexusMods.Paths;
/// <example><c>README.md</c></example>
public readonly string FileName;

/// <inheritdoc />
RelativePath IPath.FileName => Name;

/// <summary>
/// The <see cref="IFileSystem"/> implementation used by the IO methods.
/// </summary>
Expand All @@ -48,11 +53,17 @@ public AbsolutePath WithFileSystem(IFileSystem fileSystem)
return new AbsolutePath(Directory, FileName, fileSystem);
}

/// <summary>
/// Returns the FileName as a <see cref="RelativePath"/>.
/// </summary>
/// <remarks>
/// If this is a root directory, returns <see cref="RelativePath.Empty"/>.
/// </remarks>
public RelativePath Name => string.IsNullOrEmpty(FileName) ? RelativePath.Empty : new RelativePath(FileName);

/// <inheritdoc />
public Extension Extension => string.IsNullOrEmpty(FileName) ? Extension.None : Extension.FromPath(FileName);

/// <inheritdoc />
RelativePath IPath.FileName => FileName;

/// <summary>
/// Gets the parent directory, i.e. navigates one folder up.
Expand All @@ -67,6 +78,40 @@ public AbsolutePath Parent
}
}

/// <summary>
/// Returns the root folder of this path.
/// </summary>
public AbsolutePath GetRootComponent => GetRootDirectory();

/// <inheritdoc/>
public IEnumerable<RelativePath> Parts =>
GetNonRootPart().Parts;

/// <inheritdoc/>
public IEnumerable<AbsolutePath> GetAllParents()
{
var currentPath = this;
var root = GetRootDirectory();

while (currentPath != root)
{
yield return currentPath;
currentPath = currentPath.Parent;
}
yield return root;
}

/// <summary>
/// Returns the non-root part of this path.
/// </summary>
public RelativePath GetNonRootPart()
{
return RelativeTo(GetRootDirectory());
}

/// <inheritdoc/>
public bool IsRooted => true;

private AbsolutePath(string directory, string fileName, IFileSystem fileSystem)
{
Directory = directory;
Expand All @@ -90,10 +135,9 @@ internal static AbsolutePath FromSanitizedFullPath(ReadOnlySpan<char> fullPath,
/// </summary>
/// <seealso cref="FromSanitizedFullPath"/>
/// <seealso cref="FromUnsanitizedDirectoryAndFileName"/>
internal static AbsolutePath FromUnsanitizedFullPath(ReadOnlySpan<char> fullPath, IFileSystem fileSystem)
internal static AbsolutePath FromUnsanitizedFullPath(string fullPath, IFileSystem fileSystem)
{
var sanitizedPath = PathHelpers.Sanitize(fullPath, fileSystem.OS);
return FromSanitizedFullPath(sanitizedPath, fileSystem);
return fileSystem.FromUnsanitizedFullPath(fullPath);
}

/// <summary>
Expand Down Expand Up @@ -203,14 +247,21 @@ public AbsolutePath Combine(RelativePath path)
/// <summary>
/// Gets a path relative to another absolute path.
/// </summary>
/// <remarks>
/// Returns <see cref="RelativePath.Empty"/> if <see paramref="other"/> is the same as this path.
/// </remarks>
/// <param name="other">The path from which the relative path should be made.</param>
/// <throws><see cref="PathException"/> if the paths are not in the same folder.</throws>
public RelativePath RelativeTo(AbsolutePath other)
{
var childLength = GetFullPathLength();
var parentLength = other.GetFullPathLength();

if (childLength == parentLength && Equals(other)) return RelativePath.Empty;

var child = childLength <= 512 ? stackalloc char[childLength] : GC.AllocateUninitializedArray<char>(childLength);
GetFullPath(child);

var parentLength = other.GetFullPathLength();
var parent = parentLength <= 512 ? stackalloc char[parentLength] : GC.AllocateUninitializedArray<char>(parentLength);
other.GetFullPath(parent);

Expand All @@ -221,11 +272,7 @@ public RelativePath RelativeTo(AbsolutePath other)
return default;
}

/// <summary>
/// Returns true if this path is a child of the specified path.
/// </summary>
/// <param name="parent">The path to verify.</param>
/// <returns>True if this is a child path of the parent path; else false.</returns>
/// <inheritdoc />
public bool InFolder(AbsolutePath parent)
{
var parentLength = parent.GetFullPathLength();
Expand All @@ -238,6 +285,31 @@ public bool InFolder(AbsolutePath parent)
return PathHelpers.InFolder(Directory, parentSpan, FileSystem.OS);
}

/// <inheritdoc />
public bool StartsWith(AbsolutePath other)
{
var fullPath = GetFullPath();
var prefix = other.GetFullPath();

if (fullPath.Length < prefix.Length) return false;
if (fullPath.Length == prefix.Length) return Equals(other);
if (!fullPath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
return false;
}

// If the other path is a parent of this path, then the next character must be a directory separator.
return fullPath[prefix.Length] == PathHelpers.DirectorySeparatorChar ||
// unless the prefix is a root directory
PathHelpers.IsRootDirectory(prefix, FileSystem.OS);
}

/// <inheritdoc />
public bool EndsWith(RelativePath other)
{
return GetNonRootPart().EndsWith(other);
}

/// <summary/>
public static bool operator ==(AbsolutePath lhs, AbsolutePath rhs) => lhs.Equals(rhs);

Expand Down
Loading