Files
Explorer-Workbench/src/Explorer.Domain/PathRules.cs
netquick 09a8cfafa3 Add settings, cloud places, and optional archive-content indexing.
Keep official clients in charge of sync while Explorer can group locations, persist UI prefs, and list zip/rar/7z members without extracting them.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-23 12:16:09 +02:00

215 lines
5.4 KiB
C#

namespace Explorer.Domain;
public static class PathRules
{
public const string ExtendedPrefix = @"\\?\";
public const string ExtendedUncPrefix = @"\\?\UNC\";
public static string ToExtended(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return path;
}
if (path.StartsWith(ExtendedPrefix, StringComparison.Ordinal))
{
return path;
}
if (path.StartsWith(@"\\", StringComparison.Ordinal))
{
return ExtendedUncPrefix + path[2..];
}
return ExtendedPrefix + path;
}
public static string FromExtended(string path)
{
if (path.StartsWith(ExtendedUncPrefix, StringComparison.OrdinalIgnoreCase))
{
return @"\\" + path[ExtendedUncPrefix.Length..];
}
if (path.StartsWith(ExtendedPrefix, StringComparison.Ordinal))
{
return path[ExtendedPrefix.Length..];
}
return path;
}
public static bool IsUnc(string path)
{
var p = FromExtended(path);
return p.StartsWith(@"\\", StringComparison.Ordinal);
}
public static string NormalizeDirectorySeparators(string path)
=> path.Replace('/', '\\');
public static string Combine(string root, string relative)
{
root = FromExtended(root).TrimEnd('\\');
relative = relative.Replace('/', '\\').TrimStart('\\');
if (string.IsNullOrEmpty(relative))
{
return EnsureDirectoryTrailingSlashIfRoot(root);
}
return root + "\\" + relative;
}
public static string MakeRelative(string root, string fullPath)
{
var r = FromExtended(root).TrimEnd('\\');
var f = FromExtended(fullPath).TrimEnd('\\');
if (f.Equals(r, StringComparison.OrdinalIgnoreCase))
{
return string.Empty;
}
if (f.StartsWith(r + "\\", StringComparison.OrdinalIgnoreCase))
{
return f[(r.Length + 1)..];
}
return f;
}
public static string CanonicalUncRoot(string path)
{
var p = FromExtended(path).TrimEnd('\\');
if (!p.StartsWith(@"\\", StringComparison.Ordinal))
{
return p;
}
var parts = p[2..].Split('\\', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 2)
{
return @"\\" + p[2..].ToLowerInvariant();
}
return @"\\" + parts[0].ToLowerInvariant() + "\\" + parts[1];
}
public static string Parent(string path)
{
var p = FromExtended(path).TrimEnd('\\');
if (p.Length >= 2 && p[1] == ':' && p.Length <= 3)
{
return p.Length == 2 ? p + "\\" : p + (p.EndsWith('\\') ? "" : "\\");
}
if (IsUnc(p))
{
var root = CanonicalUncRoot(p);
if (p.Equals(root, StringComparison.OrdinalIgnoreCase))
{
return root;
}
}
var idx = p.LastIndexOf('\\');
if (idx <= 0)
{
return p;
}
if (idx == 2 && p[1] == ':')
{
return p[..3];
}
return p[..idx];
}
public static bool IsDriveRoot(string path)
{
var p = FromExtended(path).TrimEnd('\\');
return p.Length == 2 && p[1] == ':';
}
public static string EnsureDirectoryTrailingSlashIfRoot(string path)
{
var p = FromExtended(path);
if (p.Length == 2 && p[1] == ':')
{
return p + "\\";
}
return p;
}
public static string GetFileName(string path)
{
var p = FromExtended(path).TrimEnd('\\');
var idx = p.LastIndexOf('\\');
return idx < 0 ? p : p[(idx + 1)..];
}
public static string RelativeParent(string pathRel)
{
if (string.IsNullOrEmpty(pathRel))
{
return string.Empty;
}
var n = NormalizeDirectorySeparators(pathRel).TrimEnd('\\');
var idx = n.LastIndexOf('\\');
return idx < 0 ? string.Empty : n[..idx];
}
public static string JoinDisplay(string? root, string pathRel)
{
if (string.IsNullOrWhiteSpace(root))
{
return pathRel;
}
return string.IsNullOrWhiteSpace(pathRel) ? EnsureDirectoryTrailingSlashIfRoot(root) : Combine(root, pathRel);
}
public static string ShortenDisplay(string path, int maxChars = 64)
{
var p = FromExtended(path);
if (p.Length <= maxChars)
{
return p;
}
var unc = IsUnc(p);
var parts = p.TrimEnd('\\').Split('\\', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length <= 2)
{
return p;
}
string prefix;
var start = 0;
if (unc)
{
prefix = @"\\" + parts[0] + "\\" + parts[1];
start = 2;
}
else
{
prefix = parts.Length > 2 ? parts[0] + "\\" + parts[1] : parts[0];
start = parts.Length > 2 ? 2 : 1;
}
var leaf = parts[^1];
var parent = parts.Length - 1 > start ? parts[^2] + "\\" + leaf : leaf;
var candidate = prefix + @"\…\" + parent;
if (candidate.Length <= maxChars)
{
return candidate;
}
candidate = prefix + @"\…\" + leaf;
return candidate.Length <= maxChars ? candidate : prefix + @"\…";
}
}