namespace NatsNet.PortTracker.Audit; /// /// Classifies features by inspecting the SourceIndexer for their .NET implementation status. /// Priority: n_a lookup -> method-not-found -> stub detection -> verified. /// public sealed class FeatureClassifier { public record ClassificationResult(string Status, string Reason); public record FeatureRecord( long Id, string DotnetClass, string DotnetMethod, string GoFile, string GoMethod); private readonly SourceIndexer _indexer; // N/A lookup: (goMethod pattern) -> reason // Checked case-insensitively against go_method private static readonly Dictionary NaByGoMethod = new(StringComparer.OrdinalIgnoreCase) { ["Noticef"] = ".NET uses Microsoft.Extensions.Logging", ["Debugf"] = ".NET uses Microsoft.Extensions.Logging", ["Tracef"] = ".NET uses Microsoft.Extensions.Logging", ["Warnf"] = ".NET uses Microsoft.Extensions.Logging", ["Errorf"] = ".NET uses Microsoft.Extensions.Logging", ["Fatalf"] = ".NET uses Microsoft.Extensions.Logging", }; // N/A lookup: go_file + go_method patterns private static readonly List<(Func Match, string Reason)> NaPatterns = [ // Signal handling — .NET uses IHostApplicationLifetime (f => f.GoMethod.Equals("handleSignals", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"), (f => f.GoMethod.Equals("processSignal", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"), ]; public FeatureClassifier(SourceIndexer indexer) { _indexer = indexer; } /// /// Classify a single feature. Returns status and reason. /// public ClassificationResult Classify(FeatureRecord feature) { // 1. N/A lookup — check go_method against known patterns if (NaByGoMethod.TryGetValue(feature.GoMethod, out var naReason)) return new ClassificationResult("n_a", naReason); foreach (var (match, reason) in NaPatterns) { if (match(feature)) return new ClassificationResult("n_a", reason); } // 2. Handle comma-separated dotnet_class (e.g. "ClosedRingBuffer,ClosedClient") var classNames = feature.DotnetClass.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); var methodName = feature.DotnetMethod; // Try each class name foreach (var className in classNames) { var methods = _indexer.Lookup(className, methodName); if (methods.Count > 0) { // Found the method — classify based on body analysis // Use the "best" match: prefer non-stub over stub var best = methods.OrderByDescending(m => m.StatementCount).First(); if (best.IsStub) return new ClassificationResult("stub", $"Body is throw NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); if (best.IsPartial) return new ClassificationResult("stub", $"Partial implementation with NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); return new ClassificationResult("verified", $"Method found with {best.StatementCount} statement(s) at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); } } // 3. Method not found — check if any class exists var anyClassFound = classNames.Any(c => _indexer.HasClass(c)); if (anyClassFound) return new ClassificationResult("deferred", "Class exists but method not found"); return new ClassificationResult("deferred", "Class not found in .NET source"); } }