Files
gregExtractor/Services/RegexCoverageScanner.cs
T

58 lines
2.2 KiB
C#

namespace gregExtractor.Services;
public sealed class RegexCoverageScanner : ICoverageScanner
{
private static readonly Regex PatchRegex = new(
@"\[HarmonyPatch\(typeof\(([^)]+)\),\s*nameof\(([^)]+)\)\)\]",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
private readonly Dictionary<string, HashSet<string>> _matches = new(StringComparer.Ordinal);
public IReadOnlyDictionary<string, IReadOnlyCollection<string>> LastScanMatches => _matches
.ToDictionary(static x => x.Key, static x => (IReadOnlyCollection<string>)x.Value.ToArray(), StringComparer.Ordinal);
public HashSet<string> ScanImplementedPatches(string[] sourceDirs, IProgress<string>? progress)
{
_matches.Clear();
var keys = new HashSet<string>(StringComparer.Ordinal);
foreach (string dir in sourceDirs.Where(Directory.Exists))
{
foreach (string file in Directory.EnumerateFiles(dir, "*.cs", SearchOption.AllDirectories))
{
progress?.Report($"Regex scanning {file}");
string code = File.ReadAllText(file);
foreach (Match match in PatchRegex.Matches(code))
{
string typeName = match.Groups[1].Value.Replace("global::", string.Empty).Trim();
string methodExpr = match.Groups[2].Value.Trim();
int dotIndex = methodExpr.LastIndexOf('.');
string methodName = dotIndex >= 0 ? methodExpr[(dotIndex + 1)..].Trim() : methodExpr;
if (string.IsNullOrWhiteSpace(typeName) || string.IsNullOrWhiteSpace(methodName))
continue;
string key = NormalizeKey($"{typeName}::{methodName}");
keys.Add(key);
if (!_matches.TryGetValue(key, out HashSet<string>? files))
{
files = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_matches[key] = files;
}
files.Add(file);
}
}
}
return keys;
}
private static string NormalizeKey(string key)
{
return key.Replace(" ", string.Empty);
}
}