qstbak/Questionable/Questionable.Validation/SchemaValidationHelper.cs
2026-08-17 20:29:32 +10:00

126 lines
2.6 KiB
C#

using System;
using System.Collections.Generic;
using Json.Schema;
namespace Questionable.Validation;
internal static class SchemaValidationHelper
{
internal readonly record struct ParsedError(string InstancePath, string PropertyPath, string Message);
public static void CollectLeafErrors(EvaluationResults node, List<ParsedError> errors)
{
if (!node.IsValid)
{
Dictionary<string, string> errors2 = node.Errors;
if (errors2 != null && errors2.Count > 0)
{
foreach (var (keyword, text3) in node.Errors)
{
if (!IsAggregateKeyword(keyword) && !string.IsNullOrWhiteSpace(text3))
{
string text4 = node.InstanceLocation.ToString();
string rawPath = GetRawPath(text4);
errors.Add(new ParsedError(text4 ?? string.Empty, FormatPath(rawPath), CleanMessage(text3)));
}
}
}
}
List<EvaluationResults> details = node.Details;
if (details == null || details.Count <= 0)
{
return;
}
foreach (EvaluationResults detail in node.Details)
{
if (!detail.IsValid)
{
CollectLeafErrors(detail, errors);
}
}
}
public static string FormatPath(string rawPath)
{
if (string.IsNullOrEmpty(rawPath))
{
return string.Empty;
}
string[] array = rawPath.Split('/');
List<string> list = new List<string>();
string[] array2 = array;
foreach (string text in array2)
{
if (int.TryParse(text, out var result))
{
if (list.Count > 0)
{
list[list.Count - 1] += $"[{result}]";
}
else
{
list.Add($"[{result}]");
}
}
else
{
list.Add(text);
}
}
return string.Join(".", list);
}
public static string CleanMessage(string message)
{
return message.Replace("\\u0022", "\"", StringComparison.Ordinal).Replace("\\u0027", "'", StringComparison.Ordinal);
}
public static string? FormatQuestLocation(byte? sequence, int? step)
{
if (!sequence.HasValue)
{
return null;
}
if (!step.HasValue)
{
return $"Seq {sequence}";
}
return $"Seq {sequence}, Step {step}";
}
private static bool IsAggregateKeyword(string keyword)
{
switch (keyword)
{
case "oneOf":
case "allOf":
case "anyOf":
case "else":
case "then":
case "if":
case "not":
case "prefixItems":
case "contains":
return true;
default:
return false;
}
}
private static string GetRawPath(string? instancePath)
{
if (string.IsNullOrEmpty(instancePath))
{
return string.Empty;
}
if (instancePath.StartsWith('#'))
{
instancePath = instancePath.Substring(1);
}
if (instancePath.StartsWith('/'))
{
instancePath = instancePath.Substring(1);
}
return instancePath;
}
}