Skip to content
Merged
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
65 changes: 16 additions & 49 deletions src/Elastic.Documentation.Tooling/FileSystemFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using Elastic.Documentation.Extensions;
using Nullean.ScopedFileSystem;
using DirectoryInfoExtensions = Elastic.Documentation.Extensions.IDirectoryInfoExtensions;

// ReSharper disable once CheckNamespace — intentionally preserving the original namespace so consumers need no using changes
#pragma warning disable IDE0130
Expand Down Expand Up @@ -109,63 +111,28 @@ public static ScopedFileSystem ScopeCurrentWorkingDirectory(IFileSystem inner, I
if (extensionRoots is null)
return ScopeCurrentWorkingDirectory(inner);

var roots = NormalizeDisjointRoots([
Paths.WorkingDirectoryRoot.FullName,
Paths.ApplicationData.FullName,
.. extensionRoots,
]);
if (roots.Length == 2
&& roots.Contains(NormalizeRootPath(Paths.WorkingDirectoryRoot.FullName), StringComparer.OrdinalIgnoreCase)
&& roots.Contains(NormalizeRootPath(Paths.ApplicationData.FullName), StringComparer.OrdinalIgnoreCase))
var workingRoot = inner.DirectoryInfo.New(Paths.WorkingDirectoryRoot.FullName);
var externalRoots = extensionRoots
.Select(r => inner.DirectoryInfo.New(r))
.Where(d => !DirectoryInfoExtensions.IsSubPathOf(d, workingRoot))
.Select(d => d.FullName)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();

if (externalRoots.Length == 0)
return ScopeCurrentWorkingDirectory(inner);

var roots = new[] { Paths.WorkingDirectoryRoot.FullName, Paths.ApplicationData.FullName }
.Concat(externalRoots)
.ToArray();

return new ScopedFileSystem(inner, new ScopedFileSystemOptions(roots)
{
AllowedHiddenFolderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".git", ".artifacts" },
AllowedHiddenFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".git", ".doc.state" }
});
}

private static string NormalizeRootPath(string path) =>
Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

private static bool IsSameOrSubPathOf(string path, string possibleAncestor)
{
path = NormalizeRootPath(path);
possibleAncestor = NormalizeRootPath(possibleAncestor);
if (path.Equals(possibleAncestor, StringComparison.OrdinalIgnoreCase))
return true;

return path.StartsWith(possibleAncestor + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)
|| path.StartsWith(possibleAncestor + Path.AltDirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
}

private static string[] NormalizeDisjointRoots(IEnumerable<string> roots)
{
var normalized = roots
.Select(NormalizeRootPath)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(static root => root.Length)
.ToList();

var kept = new List<string>();
foreach (var root in normalized)
{
if (kept.Exists(existing => IsSameOrSubPathOf(root, existing)))
continue;

for (var i = kept.Count - 1; i >= 0; i--)
{
if (IsSameOrSubPathOf(kept[i], root))
kept.RemoveAt(i);
}

kept.Add(root);
}

return [.. kept];
}

// Builds write options that include AllowedSpecialFolders.Temp PLUS the inner FS's own
// GetTempPath() as an explicit root — but only when the inner FS is MockFileSystem.
//
Expand Down Expand Up @@ -285,7 +252,7 @@ public static ScopedFileSystem RealGitRootForPathWrite(string? path, string? out
if (output is not null)
{
var absOutput = Path.IsPathRooted(output) ? output : Path.GetFullPath(output);
if (!plain.DirectoryInfo.New(absOutput).IsSubPathOf(plain.DirectoryInfo.New(gitRoot)))
if (!DirectoryInfoExtensions.IsSubPathOf(plain.DirectoryInfo.New(absOutput), plain.DirectoryInfo.New(gitRoot)))
roots.Add(absOutput);
}

Expand Down
Loading