Add phase 1 critical import tool

This commit is contained in:
2026-03-14 01:10:44 +01:00
parent 44af81cc38
commit f70d610c92
25 changed files with 851 additions and 166 deletions

View File

@@ -0,0 +1,103 @@
using RolemasterDb.ImportTool.Parsing;
namespace RolemasterDb.ImportTool;
public sealed class CriticalImportCommandRunner
{
private readonly CriticalImportManifestLoader manifestLoader = new();
private readonly PdfTextExtractor pdfTextExtractor = new();
private readonly StandardCriticalTableParser standardParser = new();
public async Task<int> RunAsync(ResetOptions options)
{
if (!string.Equals(options.Target, "criticals", StringComparison.OrdinalIgnoreCase))
{
Console.Error.WriteLine("Only 'criticals' is supported by phase 1.");
return 1;
}
var loader = new CriticalImportLoader(ResolveDatabasePath(options.DatabasePath));
var removedTableCount = await loader.ResetCriticalsAsync();
Console.WriteLine($"Removed {removedTableCount} critical table records.");
return 0;
}
public async Task<int> RunAsync(ExtractOptions options)
{
var entry = GetManifestEntry(options.Table);
var artifactPaths = CreateArtifactPaths(entry.Slug);
await pdfTextExtractor.ExtractAsync(ResolveRepositoryPath(entry.PdfPath), artifactPaths.ExtractedTextPath);
Console.WriteLine($"Extracted {entry.Slug} to {artifactPaths.ExtractedTextPath}");
return 0;
}
public async Task<int> RunAsync(LoadOptions options)
{
var entry = GetManifestEntry(options.Table);
var artifactPaths = CreateArtifactPaths(entry.Slug);
if (!File.Exists(artifactPaths.ExtractedTextPath))
{
Console.Error.WriteLine($"Missing extracted text artifact: {artifactPaths.ExtractedTextPath}");
return 1;
}
var extractedText = await File.ReadAllTextAsync(artifactPaths.ExtractedTextPath);
var parsedTable = Parse(entry, extractedText);
var loader = new CriticalImportLoader(ResolveDatabasePath(options.DatabasePath));
var result = await loader.LoadAsync(parsedTable);
Console.WriteLine(
$"Loaded {result.TableSlug}: {result.ColumnCount} columns, {result.RollBandCount} roll bands, {result.ResultCount} results.");
return 0;
}
public async Task<int> RunAsync(ImportOptions options)
{
var extractExitCode = await RunAsync(new ExtractOptions
{
DatabasePath = options.DatabasePath,
Table = options.Table
});
if (extractExitCode != 0)
{
return extractExitCode;
}
return await RunAsync(new LoadOptions
{
DatabasePath = options.DatabasePath,
Table = options.Table
});
}
private CriticalImportManifestEntry GetManifestEntry(string tableSlug)
{
var manifest = manifestLoader.Load(RepositoryPaths.Discover().ManifestPath);
return manifest.Tables
.Where(item => item.Enabled)
.SingleOrDefault(item => string.Equals(item.Slug, tableSlug.Trim(), StringComparison.OrdinalIgnoreCase))
?? throw new InvalidOperationException($"No enabled manifest entry was found for '{tableSlug}'.");
}
private ParsedCriticalTable Parse(CriticalImportManifestEntry entry, string extractedText)
{
if (!string.Equals(entry.Family, "standard", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"Family '{entry.Family}' is not supported by phase 1.");
}
return standardParser.Parse(entry, extractedText);
}
private static ImportArtifactPaths CreateArtifactPaths(string slug) =>
ImportArtifactPaths.Create(RepositoryPaths.Discover().ArtifactsRootPath, slug);
private static string ResolveDatabasePath(string? databasePath) =>
Path.GetFullPath(databasePath ?? RepositoryPaths.Discover().DefaultDatabasePath);
private static string ResolveRepositoryPath(string path) =>
Path.GetFullPath(Path.Combine(RepositoryPaths.Discover().RootPath, path));
}