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 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); } }