using System; using System.Collections.Generic; using System.IO; using System.Text.Json; namespace StellaOps.Reachability.FixtureTests.PatchOracle; /// /// Loads patch-oracle definitions from fixture files. /// public sealed class PatchOracleLoader { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }; private readonly string _fixtureRoot; public PatchOracleLoader(string fixtureRoot) { _fixtureRoot = fixtureRoot ?? throw new ArgumentNullException(nameof(fixtureRoot)); } /// /// Loads the oracle index from INDEX.json. /// public PatchOracleIndex LoadIndex() { var indexPath = Path.Combine(_fixtureRoot, "INDEX.json"); if (!File.Exists(indexPath)) { throw new FileNotFoundException($"Patch-oracle INDEX.json not found at {indexPath}"); } var json = File.ReadAllText(indexPath); return JsonSerializer.Deserialize(json, JsonOptions) ?? throw new InvalidOperationException("Failed to deserialize patch-oracle index"); } /// /// Loads an oracle definition by its ID. /// public PatchOracleDefinition LoadOracle(string oracleId) { var index = LoadIndex(); var entry = index.Oracles .FirstOrDefault(o => string.Equals(o.Id, oracleId, StringComparison.Ordinal)) ?? throw new KeyNotFoundException($"Oracle '{oracleId}' not found in index"); return LoadOracleFromPath(entry.Path); } /// /// Loads an oracle definition from a relative path. /// public PatchOracleDefinition LoadOracleFromPath(string relativePath) { var fullPath = Path.Combine(_fixtureRoot, relativePath); if (!File.Exists(fullPath)) { throw new FileNotFoundException($"Oracle file not found at {fullPath}"); } var json = File.ReadAllText(fullPath); return JsonSerializer.Deserialize(json, JsonOptions) ?? throw new InvalidOperationException($"Failed to deserialize oracle from {fullPath}"); } /// /// Loads all oracles for a specific case. /// public IEnumerable LoadOraclesForCase(string caseRef) { var index = LoadIndex(); foreach (var entry in index.Oracles.Where(o => string.Equals(o.CaseRef, caseRef, StringComparison.Ordinal))) { yield return LoadOracleFromPath(entry.Path); } } /// /// Loads all available oracles. /// public IEnumerable LoadAllOracles() { var index = LoadIndex(); foreach (var entry in index.Oracles) { yield return LoadOracleFromPath(entry.Path); } } /// /// Enumerates all oracle entries without loading full definitions. /// public IEnumerable EnumerateOracles() { var index = LoadIndex(); return index.Oracles; } /// /// Checks if the oracle index exists. /// public bool IndexExists() { return File.Exists(Path.Combine(_fixtureRoot, "INDEX.json")); } }