Add Explorer Workbench with hierarchical, off-UI Storage analysis.
Storage queries run in the background with cancellation and covering indexes so switching views no longer freezes the UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
82
src/Explorer.Domain/VolumeIdentityMatcher.cs
Normal file
82
src/Explorer.Domain/VolumeIdentityMatcher.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
namespace Explorer.Domain;
|
||||
|
||||
public static class VolumeIdentityMatcher
|
||||
{
|
||||
public sealed record MatchResult(Source? Source, MatchStrength Strength, bool Ambiguous);
|
||||
|
||||
public enum MatchStrength
|
||||
{
|
||||
None,
|
||||
Weak,
|
||||
SerialCapacity,
|
||||
Guid
|
||||
}
|
||||
|
||||
public static MatchResult Match(VolumeFingerprint fingerprint, IReadOnlyList<Source> known)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(fingerprint.VolumeGuid))
|
||||
{
|
||||
var guidHits = known
|
||||
.Where(s => !string.IsNullOrEmpty(s.VolumeGuid)
|
||||
&& string.Equals(s.VolumeGuid, fingerprint.VolumeGuid, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
if (guidHits.Count == 1)
|
||||
{
|
||||
return new MatchResult(guidHits[0], MatchStrength.Guid, false);
|
||||
}
|
||||
|
||||
if (guidHits.Count > 1)
|
||||
{
|
||||
return new MatchResult(guidHits[0], MatchStrength.Guid, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (fingerprint.VolumeSerial is > 0)
|
||||
{
|
||||
var serialHits = known.Where(s =>
|
||||
s.VolumeSerial == fingerprint.VolumeSerial
|
||||
&& string.Equals(s.Filesystem, fingerprint.Filesystem, StringComparison.OrdinalIgnoreCase)
|
||||
&& s.CapacityBytes == fingerprint.CapacityBytes)
|
||||
.ToList();
|
||||
if (serialHits.Count == 1)
|
||||
{
|
||||
return new MatchResult(serialHits[0], MatchStrength.SerialCapacity, false);
|
||||
}
|
||||
|
||||
if (serialHits.Count == 0 && !string.IsNullOrEmpty(fingerprint.Label))
|
||||
{
|
||||
serialHits = known.Where(s =>
|
||||
s.VolumeSerial == fingerprint.VolumeSerial
|
||||
&& string.Equals(s.Label, fingerprint.Label, StringComparison.OrdinalIgnoreCase)
|
||||
&& s.CapacityBytes == fingerprint.CapacityBytes)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (serialHits.Count == 1)
|
||||
{
|
||||
return new MatchResult(serialHits[0], MatchStrength.Weak, false);
|
||||
}
|
||||
|
||||
if (serialHits.Count > 1)
|
||||
{
|
||||
return new MatchResult(serialHits[0], MatchStrength.Weak, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (fingerprint.Kind is SourceKind.Smb or SourceKind.Nfs)
|
||||
{
|
||||
var unc = PathRules.CanonicalUncRoot(fingerprint.RootPath);
|
||||
var uncHits = known.Where(s =>
|
||||
s.Kind == fingerprint.Kind
|
||||
&& s.LastRootPath is not null
|
||||
&& string.Equals(PathRules.CanonicalUncRoot(s.LastRootPath), unc, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
if (uncHits.Count == 1)
|
||||
{
|
||||
return new MatchResult(uncHits[0], MatchStrength.SerialCapacity, false);
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(null, MatchStrength.None, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user