Share critical cell parsing across app and importer

This commit is contained in:
2026-03-15 02:10:17 +01:00
parent c5800d6878
commit 641e33f811
27 changed files with 1207 additions and 19 deletions

View File

@@ -0,0 +1,127 @@
namespace RolemasterDb.CriticalParsing;
public static class CriticalCellTextParser
{
public static CriticalCellParseContent Parse(string rawCellText, AffixLegend affixLegend)
{
var lines = rawCellText
.Split(["\r\n", "\n", "\r"], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToList();
return Parse(lines, affixLegend);
}
public static CriticalCellParseContent Parse(IReadOnlyList<string> lines, AffixLegend affixLegend)
{
var validationErrors = new List<string>();
var branchStartIndexes = FindBranchStartIndexes(lines);
var baseLineCount = branchStartIndexes.Count == 0 ? lines.Count : branchStartIndexes[0];
var baseLines = lines.Take(baseLineCount).ToList();
var branches = new List<ParsedCriticalBranch>();
var affixLegendSymbols = affixLegend.ClassificationSymbols;
validationErrors.AddRange(ValidateSegmentCount(baseLines, affixLegendSymbols, "Base content"));
for (var branchIndex = 0; branchIndex < branchStartIndexes.Count; branchIndex++)
{
var startIndex = branchStartIndexes[branchIndex];
var endIndex = branchIndex == branchStartIndexes.Count - 1
? lines.Count
: branchStartIndexes[branchIndex + 1];
branches.Add(ParseBranch(
lines.Skip(startIndex).Take(endIndex - startIndex).ToList(),
branchIndex + 1,
affixLegend,
validationErrors));
}
var (rawText, descriptionText, rawAffixText) = BuildTextSections(baseLines, affixLegendSymbols);
var effects = AffixEffectParser.Parse(rawAffixText, affixLegend);
return new CriticalCellParseContent(baseLines, rawText, descriptionText, rawAffixText, effects, branches, validationErrors);
}
private static ParsedCriticalBranch ParseBranch(
IReadOnlyList<string> branchLines,
int sortOrder,
AffixLegend affixLegend,
List<string> validationErrors)
{
var firstLine = branchLines[0];
var separatorIndex = firstLine.IndexOf(':', StringComparison.Ordinal);
var conditionText = CriticalCellParserSupport.CollapseWhitespace(firstLine[..separatorIndex]);
var firstPayloadLine = CriticalCellParserSupport.CollapseWhitespace(firstLine[(separatorIndex + 1)..]);
var payloadLines = new List<string>();
if (!string.IsNullOrWhiteSpace(firstPayloadLine))
{
payloadLines.Add(firstPayloadLine);
}
foreach (var continuationLine in branchLines.Skip(1))
{
var normalized = CriticalCellParserSupport.CollapseWhitespace(continuationLine);
if (!string.IsNullOrWhiteSpace(normalized))
{
payloadLines.Add(normalized);
}
}
var affixLegendSymbols = affixLegend.ClassificationSymbols;
validationErrors.AddRange(ValidateSegmentCount(payloadLines, affixLegendSymbols, $"Branch '{conditionText}'"));
var (_, descriptionText, rawAffixText) = BuildTextSections(payloadLines, affixLegendSymbols);
var effects = AffixEffectParser.Parse(rawAffixText, affixLegend);
return new ParsedCriticalBranch(
"conditional",
CriticalCellParserSupport.NormalizeConditionKey(conditionText),
conditionText,
string.Join(Environment.NewLine, branchLines),
descriptionText,
rawAffixText,
effects,
sortOrder);
}
private static List<int> FindBranchStartIndexes(IReadOnlyList<string> lines)
{
var branchStartIndexes = new List<int>();
for (var index = 0; index < lines.Count; index++)
{
if (CriticalCellParserSupport.IsConditionalBranchStartLine(lines[index]))
{
branchStartIndexes.Add(index);
}
}
return branchStartIndexes;
}
private static IReadOnlyList<string> ValidateSegmentCount(
IReadOnlyList<string> lines,
IReadOnlySet<string> affixLegendSymbols,
string scope)
{
if (lines.Count == 0)
{
return [];
}
var segmentCount = CriticalCellParserSupport.CountLineTypeSegments(lines, affixLegendSymbols);
return segmentCount > 2
? [$"{scope} interleaves prose and affix lines."]
: [];
}
private static (string RawText, string DescriptionText, string? RawAffixText) BuildTextSections(
IReadOnlyList<string> lines,
IReadOnlySet<string> affixLegendSymbols)
{
var rawText = string.Join(Environment.NewLine, lines);
var rawAffixLines = lines.Where(line => CriticalCellParserSupport.IsAffixLikeLine(line, affixLegendSymbols)).ToList();
var descriptionLines = lines.Where(line => !CriticalCellParserSupport.IsAffixLikeLine(line, affixLegendSymbols)).ToList();
var descriptionText = CriticalCellParserSupport.CollapseWhitespace(string.Join(' ', descriptionLines));
var rawAffixText = rawAffixLines.Count == 0 ? null : string.Join(Environment.NewLine, rawAffixLines);
return (rawText, descriptionText, rawAffixText);
}
}