SessionZeroWasm/SessionZero/Pages/SzfParseTest.razor

337 lines
14 KiB
Plaintext

@page "/szf-parser-test"
@using SessionZero.Data
@using SessionZero.Services
@using SessionZero.Models
@inject SzfParser SzfParser
@inject ILogger<SzfParseTest> Logger
@inject SzfGenerator SzfGenerator
@inject ISzfStorageService SzfStorageService
@inject ILocalStorageService LocalStorageService
<h1 class="page-title">SZF Testing Page</h1>
<div class="row">
<div class="col-md-6">
<h3>Input SZF</h3>
<textarea @bind="szfInputText" rows="15" class="form-control" placeholder="Paste your SZF content here..."></textarea>
<div class="mt-2">
<button class="btn btn-primary" @onclick="ParseSzf">Parse SZF</button>
<button class="btn btn-success ms-2" @onclick="SaveSzf">Save Parsed SZF</button>
</div>
</div>
<div class="col-md-6">
<h3>Generated SZF</h3>
<textarea @bind="szfOutputText" rows="15" class="form-control" readonly></textarea>
<div class="mt-2">
<button class="btn btn-info" @onclick="GenerateSzf">Generate SZF from Object</button>
</div>
</div>
</div>
<div class="mt-4">
<h3>Load SZF by GUID</h3>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Enter GUID to load" @bind="loadGuid" />
<button class="btn btn-secondary" @onclick="LoadSzf">Load SZF</button>
</div>
</div>
<div class="mt-4">
<h3>Parsed Object Details</h3>
@if (!string.IsNullOrEmpty(errorMessage))
{
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> @errorMessage
</div>
}
@if (!string.IsNullOrEmpty(statusMessage))
{
<div class="alert alert-info" role="alert">
<strong>Status:</strong> @statusMessage
</div>
}
@if (currentSzfObject != null)
{
<div class="card card-body bg-light">
<h5>Metadata</h5>
<p><strong>Type:</strong> @currentSzfObject.TypeIdentifier</p>
<p><strong>Name:</strong> @currentSzfObject.Metadata.Name</p>
<p><strong>GUID:</strong> @currentSzfObject.Metadata.Guid</p>
<p><strong>Version:</strong> @currentSzfObject.Metadata.Version</p>
@* For a more detailed display of sections, you might need a recursive component or more complex rendering logic.
For now, we'll just indicate if sections are present. *@
<h5>Sections:</h5>
@if (currentSzfObject is Character character)
{
<p><strong>Character Sections Count:</strong> @character.CharacterSections.Count</p>
@if (character.CharacterSections.Any())
{
<h6>Top-Level Sections:</h6>
<ul>
@foreach (var section in character.CharacterSections)
{
<li>@(section.Name) (@(section.Fields.Count) fields, @(section.Subsections.Count) subsections)</li>
}
</ul>
}
}
else if (currentSzfObject is CharacterTemplate characterTemplate)
{
<p><strong>Template Sections Count:</strong> @characterTemplate.Sections.Count</p>
@if (characterTemplate.Sections.Any())
{
<h6>Top-Level Template Sections:</h6>
<ul>
@foreach (var section in characterTemplate.Sections)
{
<li>@(section.Name) (@(section.Fields.Count) fields, @(section.Subsections.Count) subsections)</li>
}
</ul>
}
<p><strong>Required Datasets Count:</strong> @characterTemplate.RequiredDatasets.Count</p>
}
else if (currentSzfObject is Dataset dataset)
{
<p><strong>Dataset Entries Count:</strong> @dataset.Entries.Count</p>
@if (dataset.Entries.Any())
{
<h6>First 5 Dataset Entries:</h6>
<ul>
@foreach (var entry in dataset.Entries.Take(5))
{
<li>@(entry.Name) (@(entry.Fields.Count) fields, @(entry.Sections.Count) subsections)</li>
}
</ul>
}
}
else
{
<p>No specific display logic for this SzfObject type.</p>
}
</div>
}
else
{
<p>No SZF object parsed yet.</p>
}
</div>
@code {
private string szfInputText = string.Empty;
private string szfOutputText = string.Empty;
private SzfObject? currentSzfObject;
private string loadGuid = string.Empty;
private string statusMessage = string.Empty;
private string errorMessage = string.Empty;
// Helper to clear messages
private void ClearMessages()
{
statusMessage = string.Empty;
errorMessage = string.Empty;
}
private async void ParseSzf() // Changed from async Task to void for event handler, but calls async internally
{
ClearMessages();
try
{
if (string.IsNullOrWhiteSpace(szfInputText))
{
errorMessage = "Input SZF text cannot be empty.";
return;
}
// Perform initial parsing
currentSzfObject = SzfParser.Parse(szfInputText);
statusMessage = $"SZF successfully parsed. Type: {currentSzfObject.TypeIdentifier}, Name: {currentSzfObject.Metadata.Name}";
szfOutputText = string.Empty; // Clear generated output on new parse
// NEW: Resolve dependencies after initial parsing
await ResolveSzfObjectDependencies(currentSzfObject);
}
catch (SzfParseException ex)
{
errorMessage = $"Parsing failed: {ex.Message}";
Logger.LogError(ex, "SZF parsing error.");
}
catch (Exception ex)
{
errorMessage = $"An unexpected error occurred during parsing: {ex.Message}";
Logger.LogError(ex, "Unexpected error during SZF parsing.");
}
}
private void GenerateSzf() // Changed from async Task to void
{
ClearMessages();
try
{
if (currentSzfObject == null)
{
errorMessage = "No SZF object parsed to generate from. Please parse an SZF first.";
return;
}
szfOutputText = SzfGenerator.Generate(currentSzfObject);
statusMessage = "SZF successfully generated from the parsed object.";
}
catch (Exception ex)
{
errorMessage = $"An error occurred during SZF generation: {ex.Message}";
Logger.LogError(ex, "SZF generation error.");
}
}
private async Task SaveSzf()
{
ClearMessages();
try
{
if (currentSzfObject == null)
{
errorMessage = "No SZF object parsed to save. Please parse an SZF first.";
return;
}
// Generate the raw SZF content from the current object
var rawContent = SzfGenerator.Generate(currentSzfObject);
// Create an SzfFile DTO from SessionZero.Models
var szfFileDto = new SzfFile
{
Guid = currentSzfObject.Metadata.Guid.ToString(),
Name = currentSzfObject.Metadata.Name,
Type = currentSzfObject.TypeIdentifier, // Use TypeIdentifier
Version = currentSzfObject.Metadata.Version?.ToString() ?? "1.0.0", // Handle null version
Content = rawContent // Storing the raw SZF string
};
await SzfStorageService.SaveSzfFileAsync(szfFileDto);
statusMessage = $"SZF '{currentSzfObject.Metadata.Name}' (GUID: {currentSzfObject.Metadata.Guid}) successfully saved to storage.";
}
catch (ArgumentException ex)
{
errorMessage = $"Save failed: {ex.Message}";
Logger.LogError(ex, "Error saving SZF due to invalid argument.");
}
catch (Exception ex)
{
errorMessage = $"An unexpected error occurred during saving: {ex.Message}";
Logger.LogError(ex, "Unexpected error during SZF saving.");
}
}
private async Task LoadSzf()
{
ClearMessages();
try
{
if (string.IsNullOrWhiteSpace(loadGuid))
{
errorMessage = "Please enter a GUID to load.";
return;
}
var loadedSzfFileDto = await SzfStorageService.GetSzfFileByGuidAsync(loadGuid);
if (loadedSzfFileDto == null)
{
statusMessage = $"No SZF file found for GUID: {loadGuid}";
currentSzfObject = null;
szfInputText = string.Empty;
szfOutputText = string.Empty;
return;
}
if (string.IsNullOrWhiteSpace(loadedSzfFileDto.Content))
{
errorMessage = "Loaded SZF file has no content. Cannot re-parse.";
currentSzfObject = null;
szfInputText = string.Empty;
szfOutputText = string.Empty;
return;
}
// Parse the content from the loaded DTO back into an SzfObject
currentSzfObject = SzfParser.Parse(loadedSzfFileDto.Content!); // Using null-forgiving operator
szfInputText = loadedSzfFileDto.Content!; // Using null-forgiving operator
szfOutputText = string.Empty; // Clear previous generated output
statusMessage = $"SZF '{currentSzfObject.Metadata.Name}' (GUID: {currentSzfObject.Metadata.Guid}) successfully loaded and parsed.";
// NEW: Resolve dependencies after loading and parsing
await ResolveSzfObjectDependencies(currentSzfObject);
}
catch (SzfParseException ex)
{
errorMessage = $"Loading and parsing failed: {ex.Message}";
Logger.LogError(ex, "Error re-parsing loaded SZF content.");
}
catch (Exception ex)
{
errorMessage = $"An unexpected error occurred during loading: {ex.Message}";
Logger.LogError(ex, "Unexpected error during SZF loading.");
}
}
/// <summary>
/// Attempts to resolve dependencies for the given SzfObject.
/// Currently, this resolves RequiredDatasets for CharacterTemplates.
/// </summary>
/// <param name="szfObject">The SzfObject to resolve dependencies for.</param>
private async Task ResolveSzfObjectDependencies(SzfObject szfObject)
{
if (szfObject is CharacterTemplate characterTemplate)
{
foreach (var reqDatasetRef in characterTemplate.RequiredDatasets)
{
if (reqDatasetRef.Reference != null)
{
var datasetGuid = reqDatasetRef.Reference.Guid;
try
{
var loadedSzfFileDto = await SzfStorageService.GetSzfFileByGuidAsync(datasetGuid.ToString());
if (loadedSzfFileDto != null && !string.IsNullOrWhiteSpace(loadedSzfFileDto.Content))
{
// Parse the content of the referenced dataset
var parsedReferencedObject = SzfParser.Parse(loadedSzfFileDto.Content);
if (parsedReferencedObject is Dataset dataset)
{
reqDatasetRef.ResolvedDataset = dataset;
// Add a message that the dataset was resolved
statusMessage += $"\n- Resolved dataset '{dataset.Metadata.Name}' (GUID: {dataset.Metadata.Guid}) for alias '{reqDatasetRef.Alias}'.";
// You can optionally run validation on the resolved dataset immediately
// if you want feedback during resolution, in addition to the main object's validation.
var validationResult = dataset.Validate();
if (!validationResult.IsValid)
{
statusMessage += $"\n (Warning: Resolved dataset has validation issues.)";
}
}
else
{
// Log a warning if the resolved object is not a Dataset
statusMessage += $"\n- Warning: Reference for alias '{reqDatasetRef.Alias}' (GUID: {datasetGuid}) resolved to an object of type '{parsedReferencedObject?.GetType().Name ?? "Unknown"}' instead of 'Dataset'.";
}
}
else
{
// Log a warning if the dataset file is not found or has no content
statusMessage += $"\n- Warning: Dataset for alias '{reqDatasetRef.Alias}' (GUID: {datasetGuid}) not found in storage or is empty. Cannot resolve.";
}
}
catch (Exception ex)
{
// Log any parsing errors for the referenced dataset
statusMessage += $"\n- Warning: Failed to load or parse dataset for alias '{reqDatasetRef.Alias}' (GUID: {datasetGuid}): {ex.Message}";
Logger.LogError(ex, $"Failed to load/parse referenced dataset {datasetGuid} for CharacterTemplate.");
}
}
}
}
// Add similar logic for other SzfObject types that might have dependencies
}
}