refactor(game): 拆分 schema 校验模型类型

- 拆分 schema model 类型到独立同名文件

- 清理 schema 校验模型的文件命名 analyzer 告警

- 更新 warning reduction 批处理收口状态
This commit is contained in:
gewuyou 2026-04-29 08:38:23 +08:00
parent 1395b84439
commit 104ac25dc3
18 changed files with 990 additions and 963 deletions

View File

@ -0,0 +1,32 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个节点上声明的单个 <c>enum</c> 候选值。
/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。
/// </summary>
internal sealed class YamlConfigAllowedValue
{
/// <summary>
/// 初始化一个枚举候选值模型。
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始 JSON 文本。</param>
public YamlConfigAllowedValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;
DisplayValue = displayValue;
}
/// <summary>
/// 获取用于运行时比较的稳定键。
/// </summary>
public string ComparableValue { get; }
/// <summary>
/// 获取用于诊断输出的原始 JSON 文本。
/// </summary>
public string DisplayValue { get; }
}

View File

@ -0,0 +1,47 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个数组节点上声明的元素数量、去重与 contains 匹配计数约束。
/// 该模型与标量约束拆分保存,避免数组节点继续共享不适用的标量字段。
/// </summary>
internal sealed class YamlConfigArrayConstraints
{
/// <summary>
/// 初始化数组约束模型。
/// </summary>
/// <param name="minItems">最小元素数量约束。</param>
/// <param name="maxItems">最大元素数量约束。</param>
/// <param name="uniqueItems">是否要求数组元素唯一。</param>
/// <param name="containsConstraints">数组 contains 约束;未声明时为空。</param>
public YamlConfigArrayConstraints(
int? minItems,
int? maxItems,
bool uniqueItems,
YamlConfigArrayContainsConstraints? containsConstraints)
{
MinItems = minItems;
MaxItems = maxItems;
UniqueItems = uniqueItems;
ContainsConstraints = containsConstraints;
}
/// <summary>
/// 获取最小元素数量约束。
/// </summary>
public int? MinItems { get; }
/// <summary>
/// 获取最大元素数量约束。
/// </summary>
public int? MaxItems { get; }
/// <summary>
/// 获取是否要求数组元素唯一。
/// </summary>
public bool UniqueItems { get; }
/// <summary>
/// 获取数组 contains 约束;未声明时返回空。
/// </summary>
public YamlConfigArrayContainsConstraints? ContainsConstraints { get; }
}

View File

@ -0,0 +1,41 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示数组节点声明的 <c>contains</c> 匹配约束。
/// 该模型把 contains 子 schema 与匹配数量边界聚合在一起,避免数组节点再额外散落多组相关成员。
/// </summary>
internal sealed class YamlConfigArrayContainsConstraints
{
/// <summary>
/// 初始化数组 contains 约束模型。
/// </summary>
/// <param name="containsNode">contains 子 schema。</param>
/// <param name="minContains">最小匹配数量;为 <see langword="null" /> 时按 JSON Schema 语义默认 1。</param>
/// <param name="maxContains">最大匹配数量。</param>
public YamlConfigArrayContainsConstraints(
YamlConfigSchemaNode containsNode,
int? minContains,
int? maxContains)
{
ArgumentNullException.ThrowIfNull(containsNode);
ContainsNode = containsNode;
MinContains = minContains;
MaxContains = maxContains;
}
/// <summary>
/// 获取 contains 子 schema。
/// </summary>
public YamlConfigSchemaNode ContainsNode { get; }
/// <summary>
/// 获取最小匹配数量;未显式声明时返回空,由调用方按默认值 1 解释。
/// </summary>
public int? MinContains { get; }
/// <summary>
/// 获取最大匹配数量。
/// </summary>
public int? MaxContains { get; }
}

View File

@ -0,0 +1,41 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个对象节点上声明的 object-focused <c>if</c> / <c>then</c> / <c>else</c> 条件约束。
/// 三个分支都共享父对象已声明字段集合,不会把分支 schema 扩展成新的生成类型形状。
/// </summary>
internal sealed class YamlConfigConditionalSchemas
{
/// <summary>
/// 初始化条件分支约束模型。
/// </summary>
/// <param name="ifSchema">条件判断 schema。</param>
/// <param name="thenSchema">条件命中时需要满足的 schema。</param>
/// <param name="elseSchema">条件未命中时需要满足的 schema。</param>
public YamlConfigConditionalSchemas(
YamlConfigSchemaNode ifSchema,
YamlConfigSchemaNode? thenSchema,
YamlConfigSchemaNode? elseSchema)
{
ArgumentNullException.ThrowIfNull(ifSchema);
IfSchema = ifSchema;
ThenSchema = thenSchema;
ElseSchema = elseSchema;
}
/// <summary>
/// 获取条件判断 schema。
/// </summary>
public YamlConfigSchemaNode IfSchema { get; }
/// <summary>
/// 获取条件命中时需要满足的 schema。
/// </summary>
public YamlConfigSchemaNode? ThenSchema { get; }
/// <summary>
/// 获取条件未命中时需要满足的 schema。
/// </summary>
public YamlConfigSchemaNode? ElseSchema { get; }
}

View File

@ -0,0 +1,32 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个节点上声明的 <c>const</c> 约束。
/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。
/// </summary>
internal sealed class YamlConfigConstantValue
{
/// <summary>
/// 初始化常量约束模型。
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始常量文本。</param>
public YamlConfigConstantValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;
DisplayValue = displayValue;
}
/// <summary>
/// 获取用于运行时比较的稳定键。
/// </summary>
public string ComparableValue { get; }
/// <summary>
/// 获取用于诊断输出的原始 JSON 常量文本。
/// </summary>
public string DisplayValue { get; }
}

View File

@ -0,0 +1,55 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示标量节点上声明的数值范围与步进约束。
/// 该类型只覆盖整数 / 浮点共享的关键字,避免字符串字段继续暴露不相关的成员。
/// </summary>
internal sealed class YamlConfigNumericConstraints
{
/// <summary>
/// 初始化数值约束模型。
/// </summary>
/// <param name="minimum">最小值约束。</param>
/// <param name="maximum">最大值约束。</param>
/// <param name="exclusiveMinimum">开区间最小值约束。</param>
/// <param name="exclusiveMaximum">开区间最大值约束。</param>
/// <param name="multipleOf">数值步进约束。</param>
public YamlConfigNumericConstraints(
double? minimum,
double? maximum,
double? exclusiveMinimum,
double? exclusiveMaximum,
double? multipleOf)
{
Minimum = minimum;
Maximum = maximum;
ExclusiveMinimum = exclusiveMinimum;
ExclusiveMaximum = exclusiveMaximum;
MultipleOf = multipleOf;
}
/// <summary>
/// 获取最小值约束。
/// </summary>
public double? Minimum { get; }
/// <summary>
/// 获取最大值约束。
/// </summary>
public double? Maximum { get; }
/// <summary>
/// 获取开区间最小值约束。
/// </summary>
public double? ExclusiveMinimum { get; }
/// <summary>
/// 获取开区间最大值约束。
/// </summary>
public double? ExclusiveMaximum { get; }
/// <summary>
/// 获取数值步进约束。
/// </summary>
public double? MultipleOf { get; }
}

View File

@ -0,0 +1,67 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个对象节点上声明的属性数量约束、字段依赖约束、条件子 schema 与组合约束。
/// 该模型将对象级约束与数组 / 标量约束拆开保存,避免运行时节点继续暴露无关成员。
/// </summary>
internal sealed class YamlConfigObjectConstraints
{
/// <summary>
/// 初始化对象约束模型。
/// </summary>
/// <param name="minProperties">最小属性数量约束。</param>
/// <param name="maxProperties">最大属性数量约束。</param>
/// <param name="dependentRequired">对象内字段依赖约束。</param>
/// <param name="dependentSchemas">对象内条件 schema 约束。</param>
/// <param name="allOfSchemas">对象内组合 schema 约束。</param>
/// <param name="conditionalSchemas">对象内条件分支约束。</param>
public YamlConfigObjectConstraints(
int? minProperties,
int? maxProperties,
IReadOnlyDictionary<string, IReadOnlyList<string>>? dependentRequired,
IReadOnlyDictionary<string, YamlConfigSchemaNode>? dependentSchemas,
IReadOnlyList<YamlConfigSchemaNode>? allOfSchemas,
YamlConfigConditionalSchemas? conditionalSchemas)
{
MinProperties = minProperties;
MaxProperties = maxProperties;
DependentRequired = dependentRequired;
DependentSchemas = dependentSchemas;
AllOfSchemas = allOfSchemas;
ConditionalSchemas = conditionalSchemas;
}
/// <summary>
/// 获取最小属性数量约束。
/// </summary>
public int? MinProperties { get; }
/// <summary>
/// 获取最大属性数量约束。
/// </summary>
public int? MaxProperties { get; }
/// <summary>
/// 获取对象内字段依赖约束。
/// 键表示“触发字段”,值表示“触发字段出现后还必须存在的同级字段集合”。
/// </summary>
public IReadOnlyDictionary<string, IReadOnlyList<string>>? DependentRequired { get; }
/// <summary>
/// 获取对象内条件 schema 约束。
/// 键表示“触发字段”,值表示“触发字段出现后当前对象还必须满足的额外 schema 子树”。
/// </summary>
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? DependentSchemas { get; }
/// <summary>
/// 获取对象内 <c>allOf</c> 组合约束。
/// 每个条目都表示“当前对象还必须额外满足的 focused constraint block”。
/// </summary>
public IReadOnlyList<YamlConfigSchemaNode>? AllOfSchemas { get; }
/// <summary>
/// 获取对象内 object-focused <c>if</c> / <c>then</c> / <c>else</c> 条件约束。
/// 该模型会先用 <c>if</c> 试匹配当前对象,再只对命中的分支叠加 focused constraint block。
/// </summary>
public YamlConfigConditionalSchemas? ConditionalSchemas { get; }
}

View File

@ -0,0 +1,74 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示单个 YAML 文件中提取出的跨表引用。
/// 该模型保留源文件、字段路径和目标表等诊断信息,以便加载器在批量校验失败时给出可定位的错误。
/// </summary>
internal sealed class YamlConfigReferenceUsage
{
/// <summary>
/// 初始化一个跨表引用使用记录。
/// </summary>
/// <param name="yamlPath">源 YAML 文件路径。</param>
/// <param name="schemaPath">定义该引用的 schema 文件路径。</param>
/// <param name="propertyPath">声明引用的字段路径。</param>
/// <param name="rawValue">YAML 中的原始标量值。</param>
/// <param name="referencedTableName">目标配置表名称。</param>
/// <param name="valueType">引用值的 schema 标量类型。</param>
public YamlConfigReferenceUsage(
string yamlPath,
string schemaPath,
string propertyPath,
string rawValue,
string referencedTableName,
YamlConfigSchemaPropertyType valueType)
{
ArgumentNullException.ThrowIfNull(yamlPath);
ArgumentNullException.ThrowIfNull(schemaPath);
ArgumentNullException.ThrowIfNull(propertyPath);
ArgumentNullException.ThrowIfNull(rawValue);
ArgumentNullException.ThrowIfNull(referencedTableName);
YamlPath = yamlPath;
SchemaPath = schemaPath;
PropertyPath = propertyPath;
RawValue = rawValue;
ReferencedTableName = referencedTableName;
ValueType = valueType;
}
/// <summary>
/// 获取源 YAML 文件路径。
/// </summary>
public string YamlPath { get; }
/// <summary>
/// 获取定义该引用的 schema 文件路径。
/// </summary>
public string SchemaPath { get; }
/// <summary>
/// 获取声明引用的字段路径。
/// </summary>
public string PropertyPath { get; }
/// <summary>
/// 获取 YAML 中的原始标量值。
/// </summary>
public string RawValue { get; }
/// <summary>
/// 获取目标配置表名称。
/// </summary>
public string ReferencedTableName { get; }
/// <summary>
/// 获取引用值的 schema 标量类型。
/// </summary>
public YamlConfigSchemaPropertyType ValueType { get; }
/// <summary>
/// 获取便于诊断显示的字段路径。
/// </summary>
public string DisplayPath => PropertyPath;
}

View File

@ -0,0 +1,31 @@
namespace GFramework.Game.Config;
/// <summary>
/// 聚合一个标量节点上声明的数值约束与字符串约束。
/// 该包装层保留“标量字段有约束”的统一入口,同时把不同语义的约束分成更小的专用模型。
/// </summary>
internal sealed class YamlConfigScalarConstraints
{
/// <summary>
/// 初始化标量约束模型。
/// </summary>
/// <param name="numericConstraints">数值约束分组。</param>
/// <param name="stringConstraints">字符串约束分组。</param>
public YamlConfigScalarConstraints(
YamlConfigNumericConstraints? numericConstraints,
YamlConfigStringConstraints? stringConstraints)
{
NumericConstraints = numericConstraints;
StringConstraints = stringConstraints;
}
/// <summary>
/// 获取数值约束分组。
/// </summary>
public YamlConfigNumericConstraints? NumericConstraints { get; }
/// <summary>
/// 获取字符串约束分组。
/// </summary>
public YamlConfigStringConstraints? StringConstraints { get; }
}

View File

@ -0,0 +1,44 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示已解析并可用于运行时校验的 JSON Schema。
/// 该模型保留根节点与引用依赖集合,避免运行时引入完整 schema 引擎。
/// </summary>
internal sealed class YamlConfigSchema
{
/// <summary>
/// 初始化一个可用于运行时校验的 schema 模型。
/// </summary>
/// <param name="schemaPath">Schema 文件路径。</param>
/// <param name="rootNode">根节点模型。</param>
/// <param name="referencedTableNames">Schema 声明的目标引用表名称集合。</param>
public YamlConfigSchema(
string schemaPath,
YamlConfigSchemaNode rootNode,
IReadOnlyCollection<string> referencedTableNames)
{
ArgumentNullException.ThrowIfNull(schemaPath);
ArgumentNullException.ThrowIfNull(rootNode);
ArgumentNullException.ThrowIfNull(referencedTableNames);
SchemaPath = schemaPath;
RootNode = rootNode;
ReferencedTableNames = referencedTableNames;
}
/// <summary>
/// 获取 schema 文件路径。
/// </summary>
public string SchemaPath { get; }
/// <summary>
/// 获取根节点模型。
/// </summary>
public YamlConfigSchemaNode RootNode { get; }
/// <summary>
/// 获取 schema 声明的目标引用表名称集合。
/// 该信息用于热重载时推导受影响的依赖表闭包。
/// </summary>
public IReadOnlyCollection<string> ReferencedTableNames { get; }
}

View File

@ -0,0 +1,330 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示单个 schema 节点的最小运行时描述。
/// 同一个模型同时覆盖对象、数组和标量,便于递归校验逻辑只依赖一种树结构。
/// </summary>
internal sealed class YamlConfigSchemaNode
{
private readonly NodeChildren _children;
private readonly NodeValidation _validation;
private YamlConfigSchemaNode(
YamlConfigSchemaPropertyType nodeType,
NodeChildren children,
NodeValidation validation,
string schemaPathHint)
{
ArgumentNullException.ThrowIfNull(children);
ArgumentNullException.ThrowIfNull(validation);
ArgumentNullException.ThrowIfNull(schemaPathHint);
_children = children;
_validation = validation;
NodeType = nodeType;
Properties = children.Properties;
RequiredProperties = children.RequiredProperties;
ItemNode = children.ItemNode;
ReferenceTableName = validation.ReferenceTableName;
AllowedValues = validation.AllowedValues;
Constraints = validation.Constraints;
ArrayConstraints = validation.ArrayConstraints;
ObjectConstraints = validation.ObjectConstraints;
ConstantValue = validation.ConstantValue;
NegatedSchemaNode = validation.NegatedSchemaNode;
SchemaPathHint = schemaPathHint;
}
/// <summary>
/// 获取节点类型。
/// </summary>
public YamlConfigSchemaPropertyType NodeType { get; }
/// <summary>
/// 获取对象属性集合;非对象节点时返回空。
/// </summary>
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? Properties { get; }
/// <summary>
/// 获取对象必填属性集合;非对象节点时返回空。
/// </summary>
public IReadOnlyCollection<string>? RequiredProperties { get; }
/// <summary>
/// 获取数组元素节点;非数组节点时返回空。
/// </summary>
public YamlConfigSchemaNode? ItemNode { get; }
/// <summary>
/// 获取目标引用表名称;未声明跨表引用时返回空。
/// </summary>
public string? ReferenceTableName { get; }
/// <summary>
/// 获取节点允许值集合;未声明 <c>enum</c> 时返回空。
/// </summary>
public IReadOnlyCollection<YamlConfigAllowedValue>? AllowedValues { get; }
/// <summary>
/// 获取标量范围与长度约束;未声明时返回空。
/// </summary>
public YamlConfigScalarConstraints? Constraints { get; }
/// <summary>
/// 获取对象属性数量约束;未声明时返回空。
/// </summary>
public YamlConfigObjectConstraints? ObjectConstraints { get; }
/// <summary>
/// 获取数组元素数量约束;未声明时返回空。
/// </summary>
public YamlConfigArrayConstraints? ArrayConstraints { get; }
/// <summary>
/// 获取节点常量约束;未声明 <c>const</c> 时返回空。
/// </summary>
public YamlConfigConstantValue? ConstantValue { get; }
/// <summary>
/// 获取节点声明的 <c>not</c> 子 schema未声明时返回空。
/// </summary>
public YamlConfigSchemaNode? NegatedSchemaNode { get; }
/// <summary>
/// 获取用于诊断显示的 schema 路径提示。
/// 当前节点本身不记录独立路径,因此对象校验会回退到所属根 schema 路径。
/// </summary>
public string SchemaPathHint { get; }
/// <summary>
/// 创建对象节点描述。
/// </summary>
/// <param name="properties">对象属性集合。</param>
/// <param name="requiredProperties">对象必填属性集合。</param>
/// <param name="objectConstraints">对象属性数量约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>对象节点模型。</returns>
public static YamlConfigSchemaNode CreateObject(
IReadOnlyDictionary<string, YamlConfigSchemaNode>? properties,
IReadOnlyCollection<string>? requiredProperties,
YamlConfigObjectConstraints? objectConstraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
YamlConfigSchemaPropertyType.Object,
new NodeChildren(properties, requiredProperties, itemNode: null),
new NodeValidation(
referenceTableName: null,
allowedValues: null,
constraints: null,
arrayConstraints: null,
objectConstraints,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 创建数组节点描述。
/// </summary>
/// <param name="itemNode">数组元素节点。</param>
/// <param name="allowedValues">数组节点允许值集合。</param>
/// <param name="arrayConstraints">数组元素数量约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>数组节点模型。</returns>
public static YamlConfigSchemaNode CreateArray(
YamlConfigSchemaNode itemNode,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigArrayConstraints? arrayConstraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
YamlConfigSchemaPropertyType.Array,
new NodeChildren(properties: null, requiredProperties: null, itemNode),
new NodeValidation(
referenceTableName: null,
allowedValues,
constraints: null,
arrayConstraints,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 创建标量节点描述。
/// </summary>
/// <param name="nodeType">标量节点类型。</param>
/// <param name="referenceTableName">目标引用表名称。</param>
/// <param name="allowedValues">标量允许值集合。</param>
/// <param name="constraints">标量范围与长度约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>标量节点模型。</returns>
public static YamlConfigSchemaNode CreateScalar(
YamlConfigSchemaPropertyType nodeType,
string? referenceTableName,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigScalarConstraints? constraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
nodeType,
NodeChildren.None,
new NodeValidation(
referenceTableName,
allowedValues,
constraints,
arrayConstraints: null,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换引用表名称的新节点。
/// 该方法用于把数组级别的 ref-table 语义挂接到元素节点上。
/// </summary>
/// <param name="referenceTableName">新的目标引用表名称。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithReferenceTable(string referenceTableName)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithReferenceTable(referenceTableName),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换 <c>enum</c> 允许值集合的新节点。
/// </summary>
/// <param name="allowedValues">新的允许值集合。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithAllowedValues(IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithAllowedValues(allowedValues),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换常量约束的新节点。
/// </summary>
/// <param name="constantValue">新的常量约束。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithConstantValue(YamlConfigConstantValue? constantValue)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithConstantValue(constantValue),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换 <c>not</c> 子 schema 的新节点。
/// </summary>
/// <param name="negatedSchemaNode">新的 negated schema。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithNegatedSchemaNode(negatedSchemaNode),
SchemaPathHint);
}
private sealed class NodeChildren
{
public NodeChildren(
IReadOnlyDictionary<string, YamlConfigSchemaNode>? properties,
IReadOnlyCollection<string>? requiredProperties,
YamlConfigSchemaNode? itemNode)
{
Properties = properties;
RequiredProperties = requiredProperties;
ItemNode = itemNode;
}
public static NodeChildren None { get; } = new(properties: null, requiredProperties: null, itemNode: null);
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? Properties { get; }
public IReadOnlyCollection<string>? RequiredProperties { get; }
public YamlConfigSchemaNode? ItemNode { get; }
}
private sealed class NodeValidation
{
public NodeValidation(
string? referenceTableName,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigScalarConstraints? constraints,
YamlConfigArrayConstraints? arrayConstraints,
YamlConfigObjectConstraints? objectConstraints,
YamlConfigConstantValue? constantValue,
YamlConfigSchemaNode? negatedSchemaNode)
{
ReferenceTableName = referenceTableName;
AllowedValues = allowedValues;
Constraints = constraints;
ArrayConstraints = arrayConstraints;
ObjectConstraints = objectConstraints;
ConstantValue = constantValue;
NegatedSchemaNode = negatedSchemaNode;
}
public static NodeValidation None { get; } = new(
referenceTableName: null,
allowedValues: null,
constraints: null,
arrayConstraints: null,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null);
public string? ReferenceTableName { get; }
public IReadOnlyCollection<YamlConfigAllowedValue>? AllowedValues { get; }
public YamlConfigScalarConstraints? Constraints { get; }
public YamlConfigArrayConstraints? ArrayConstraints { get; }
public YamlConfigObjectConstraints? ObjectConstraints { get; }
public YamlConfigConstantValue? ConstantValue { get; }
public YamlConfigSchemaNode? NegatedSchemaNode { get; }
public NodeValidation WithReferenceTable(string referenceTableName)
{
return new NodeValidation(referenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, NegatedSchemaNode);
}
public NodeValidation WithAllowedValues(IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues)
{
return new NodeValidation(ReferenceTableName, allowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, NegatedSchemaNode);
}
public NodeValidation WithConstantValue(YamlConfigConstantValue? constantValue)
{
return new NodeValidation(ReferenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, constantValue, NegatedSchemaNode);
}
public NodeValidation WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode)
{
return new NodeValidation(ReferenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, negatedSchemaNode);
}
}
}

View File

@ -0,0 +1,37 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示当前运行时 schema 校验器支持的属性类型。
/// </summary>
internal enum YamlConfigSchemaPropertyType
{
/// <summary>
/// 对象类型。
/// </summary>
Object,
/// <summary>
/// 整数类型。
/// </summary>
Integer,
/// <summary>
/// 数值类型。
/// </summary>
Number,
/// <summary>
/// 布尔类型。
/// </summary>
Boolean,
/// <summary>
/// 字符串类型。
/// </summary>
String,
/// <summary>
/// 数组类型。
/// </summary>
Array
}

View File

@ -3650,950 +3650,3 @@ internal static partial class YamlConfigSchemaValidator
!string.Equals(tag, "tag:yaml.org,2002:null", StringComparison.Ordinal);
}
}
/// <summary>
/// 表示已解析并可用于运行时校验的 JSON Schema。
/// 该模型保留根节点与引用依赖集合,避免运行时引入完整 schema 引擎。
/// </summary>
internal sealed class YamlConfigSchema
{
/// <summary>
/// 初始化一个可用于运行时校验的 schema 模型。
/// </summary>
/// <param name="schemaPath">Schema 文件路径。</param>
/// <param name="rootNode">根节点模型。</param>
/// <param name="referencedTableNames">Schema 声明的目标引用表名称集合。</param>
public YamlConfigSchema(
string schemaPath,
YamlConfigSchemaNode rootNode,
IReadOnlyCollection<string> referencedTableNames)
{
ArgumentNullException.ThrowIfNull(schemaPath);
ArgumentNullException.ThrowIfNull(rootNode);
ArgumentNullException.ThrowIfNull(referencedTableNames);
SchemaPath = schemaPath;
RootNode = rootNode;
ReferencedTableNames = referencedTableNames;
}
/// <summary>
/// 获取 schema 文件路径。
/// </summary>
public string SchemaPath { get; }
/// <summary>
/// 获取根节点模型。
/// </summary>
public YamlConfigSchemaNode RootNode { get; }
/// <summary>
/// 获取 schema 声明的目标引用表名称集合。
/// 该信息用于热重载时推导受影响的依赖表闭包。
/// </summary>
public IReadOnlyCollection<string> ReferencedTableNames { get; }
}
/// <summary>
/// 表示单个 schema 节点的最小运行时描述。
/// 同一个模型同时覆盖对象、数组和标量,便于递归校验逻辑只依赖一种树结构。
/// </summary>
internal sealed class YamlConfigSchemaNode
{
private readonly NodeChildren _children;
private readonly NodeValidation _validation;
private YamlConfigSchemaNode(
YamlConfigSchemaPropertyType nodeType,
NodeChildren children,
NodeValidation validation,
string schemaPathHint)
{
ArgumentNullException.ThrowIfNull(children);
ArgumentNullException.ThrowIfNull(validation);
ArgumentNullException.ThrowIfNull(schemaPathHint);
_children = children;
_validation = validation;
NodeType = nodeType;
Properties = children.Properties;
RequiredProperties = children.RequiredProperties;
ItemNode = children.ItemNode;
ReferenceTableName = validation.ReferenceTableName;
AllowedValues = validation.AllowedValues;
Constraints = validation.Constraints;
ArrayConstraints = validation.ArrayConstraints;
ObjectConstraints = validation.ObjectConstraints;
ConstantValue = validation.ConstantValue;
NegatedSchemaNode = validation.NegatedSchemaNode;
SchemaPathHint = schemaPathHint;
}
/// <summary>
/// 获取节点类型。
/// </summary>
public YamlConfigSchemaPropertyType NodeType { get; }
/// <summary>
/// 获取对象属性集合;非对象节点时返回空。
/// </summary>
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? Properties { get; }
/// <summary>
/// 获取对象必填属性集合;非对象节点时返回空。
/// </summary>
public IReadOnlyCollection<string>? RequiredProperties { get; }
/// <summary>
/// 获取数组元素节点;非数组节点时返回空。
/// </summary>
public YamlConfigSchemaNode? ItemNode { get; }
/// <summary>
/// 获取目标引用表名称;未声明跨表引用时返回空。
/// </summary>
public string? ReferenceTableName { get; }
/// <summary>
/// 获取节点允许值集合;未声明 <c>enum</c> 时返回空。
/// </summary>
public IReadOnlyCollection<YamlConfigAllowedValue>? AllowedValues { get; }
/// <summary>
/// 获取标量范围与长度约束;未声明时返回空。
/// </summary>
public YamlConfigScalarConstraints? Constraints { get; }
/// <summary>
/// 获取对象属性数量约束;未声明时返回空。
/// </summary>
public YamlConfigObjectConstraints? ObjectConstraints { get; }
/// <summary>
/// 获取数组元素数量约束;未声明时返回空。
/// </summary>
public YamlConfigArrayConstraints? ArrayConstraints { get; }
/// <summary>
/// 获取节点常量约束;未声明 <c>const</c> 时返回空。
/// </summary>
public YamlConfigConstantValue? ConstantValue { get; }
/// <summary>
/// 获取节点声明的 <c>not</c> 子 schema未声明时返回空。
/// </summary>
public YamlConfigSchemaNode? NegatedSchemaNode { get; }
/// <summary>
/// 获取用于诊断显示的 schema 路径提示。
/// 当前节点本身不记录独立路径,因此对象校验会回退到所属根 schema 路径。
/// </summary>
public string SchemaPathHint { get; }
/// <summary>
/// 创建对象节点描述。
/// </summary>
/// <param name="properties">对象属性集合。</param>
/// <param name="requiredProperties">对象必填属性集合。</param>
/// <param name="objectConstraints">对象属性数量约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>对象节点模型。</returns>
public static YamlConfigSchemaNode CreateObject(
IReadOnlyDictionary<string, YamlConfigSchemaNode>? properties,
IReadOnlyCollection<string>? requiredProperties,
YamlConfigObjectConstraints? objectConstraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
YamlConfigSchemaPropertyType.Object,
new NodeChildren(properties, requiredProperties, itemNode: null),
new NodeValidation(
referenceTableName: null,
allowedValues: null,
constraints: null,
arrayConstraints: null,
objectConstraints,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 创建数组节点描述。
/// </summary>
/// <param name="itemNode">数组元素节点。</param>
/// <param name="allowedValues">数组节点允许值集合。</param>
/// <param name="arrayConstraints">数组元素数量约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>数组节点模型。</returns>
public static YamlConfigSchemaNode CreateArray(
YamlConfigSchemaNode itemNode,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigArrayConstraints? arrayConstraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
YamlConfigSchemaPropertyType.Array,
new NodeChildren(properties: null, requiredProperties: null, itemNode),
new NodeValidation(
referenceTableName: null,
allowedValues,
constraints: null,
arrayConstraints,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 创建标量节点描述。
/// </summary>
/// <param name="nodeType">标量节点类型。</param>
/// <param name="referenceTableName">目标引用表名称。</param>
/// <param name="allowedValues">标量允许值集合。</param>
/// <param name="constraints">标量范围与长度约束。</param>
/// <param name="schemaPathHint">用于错误信息的 schema 文件路径提示。</param>
/// <returns>标量节点模型。</returns>
public static YamlConfigSchemaNode CreateScalar(
YamlConfigSchemaPropertyType nodeType,
string? referenceTableName,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigScalarConstraints? constraints,
string schemaPathHint)
{
return new YamlConfigSchemaNode(
nodeType,
NodeChildren.None,
new NodeValidation(
referenceTableName,
allowedValues,
constraints,
arrayConstraints: null,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null),
schemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换引用表名称的新节点。
/// 该方法用于把数组级别的 ref-table 语义挂接到元素节点上。
/// </summary>
/// <param name="referenceTableName">新的目标引用表名称。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithReferenceTable(string referenceTableName)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithReferenceTable(referenceTableName),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换 <c>enum</c> 允许值集合的新节点。
/// </summary>
/// <param name="allowedValues">新的允许值集合。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithAllowedValues(IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithAllowedValues(allowedValues),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换常量约束的新节点。
/// </summary>
/// <param name="constantValue">新的常量约束。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithConstantValue(YamlConfigConstantValue? constantValue)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithConstantValue(constantValue),
SchemaPathHint);
}
/// <summary>
/// 基于当前节点复制一个只替换 <c>not</c> 子 schema 的新节点。
/// </summary>
/// <param name="negatedSchemaNode">新的 negated schema。</param>
/// <returns>复制后的节点。</returns>
public YamlConfigSchemaNode WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode)
{
return new YamlConfigSchemaNode(
NodeType,
_children,
_validation.WithNegatedSchemaNode(negatedSchemaNode),
SchemaPathHint);
}
private sealed class NodeChildren
{
public NodeChildren(
IReadOnlyDictionary<string, YamlConfigSchemaNode>? properties,
IReadOnlyCollection<string>? requiredProperties,
YamlConfigSchemaNode? itemNode)
{
Properties = properties;
RequiredProperties = requiredProperties;
ItemNode = itemNode;
}
public static NodeChildren None { get; } = new(properties: null, requiredProperties: null, itemNode: null);
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? Properties { get; }
public IReadOnlyCollection<string>? RequiredProperties { get; }
public YamlConfigSchemaNode? ItemNode { get; }
}
private sealed class NodeValidation
{
public NodeValidation(
string? referenceTableName,
IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues,
YamlConfigScalarConstraints? constraints,
YamlConfigArrayConstraints? arrayConstraints,
YamlConfigObjectConstraints? objectConstraints,
YamlConfigConstantValue? constantValue,
YamlConfigSchemaNode? negatedSchemaNode)
{
ReferenceTableName = referenceTableName;
AllowedValues = allowedValues;
Constraints = constraints;
ArrayConstraints = arrayConstraints;
ObjectConstraints = objectConstraints;
ConstantValue = constantValue;
NegatedSchemaNode = negatedSchemaNode;
}
public static NodeValidation None { get; } = new(
referenceTableName: null,
allowedValues: null,
constraints: null,
arrayConstraints: null,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null);
public string? ReferenceTableName { get; }
public IReadOnlyCollection<YamlConfigAllowedValue>? AllowedValues { get; }
public YamlConfigScalarConstraints? Constraints { get; }
public YamlConfigArrayConstraints? ArrayConstraints { get; }
public YamlConfigObjectConstraints? ObjectConstraints { get; }
public YamlConfigConstantValue? ConstantValue { get; }
public YamlConfigSchemaNode? NegatedSchemaNode { get; }
public NodeValidation WithReferenceTable(string referenceTableName)
{
return new NodeValidation(referenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, NegatedSchemaNode);
}
public NodeValidation WithAllowedValues(IReadOnlyCollection<YamlConfigAllowedValue>? allowedValues)
{
return new NodeValidation(ReferenceTableName, allowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, NegatedSchemaNode);
}
public NodeValidation WithConstantValue(YamlConfigConstantValue? constantValue)
{
return new NodeValidation(ReferenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, constantValue, NegatedSchemaNode);
}
public NodeValidation WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode)
{
return new NodeValidation(ReferenceTableName, AllowedValues, Constraints, ArrayConstraints,
ObjectConstraints, ConstantValue, negatedSchemaNode);
}
}
}
/// <summary>
/// 表示一个节点上声明的 <c>const</c> 约束。
/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。
/// </summary>
internal sealed class YamlConfigConstantValue
{
/// <summary>
/// 初始化常量约束模型。
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始常量文本。</param>
public YamlConfigConstantValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;
DisplayValue = displayValue;
}
/// <summary>
/// 获取用于运行时比较的稳定键。
/// </summary>
public string ComparableValue { get; }
/// <summary>
/// 获取用于诊断输出的原始 JSON 常量文本。
/// </summary>
public string DisplayValue { get; }
}
/// <summary>
/// 表示一个节点上声明的单个 <c>enum</c> 候选值。
/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。
/// </summary>
internal sealed class YamlConfigAllowedValue
{
/// <summary>
/// 初始化一个枚举候选值模型。
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始 JSON 文本。</param>
public YamlConfigAllowedValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;
DisplayValue = displayValue;
}
/// <summary>
/// 获取用于运行时比较的稳定键。
/// </summary>
public string ComparableValue { get; }
/// <summary>
/// 获取用于诊断输出的原始 JSON 文本。
/// </summary>
public string DisplayValue { get; }
}
/// <summary>
/// 表示一个对象节点上声明的属性数量约束、字段依赖约束、条件子 schema 与组合约束。
/// 该模型将对象级约束与数组 / 标量约束拆开保存,避免运行时节点继续暴露无关成员。
/// </summary>
internal sealed class YamlConfigObjectConstraints
{
/// <summary>
/// 初始化对象约束模型。
/// </summary>
/// <param name="minProperties">最小属性数量约束。</param>
/// <param name="maxProperties">最大属性数量约束。</param>
/// <param name="dependentRequired">对象内字段依赖约束。</param>
/// <param name="dependentSchemas">对象内条件 schema 约束。</param>
/// <param name="allOfSchemas">对象内组合 schema 约束。</param>
/// <param name="conditionalSchemas">对象内条件分支约束。</param>
public YamlConfigObjectConstraints(
int? minProperties,
int? maxProperties,
IReadOnlyDictionary<string, IReadOnlyList<string>>? dependentRequired,
IReadOnlyDictionary<string, YamlConfigSchemaNode>? dependentSchemas,
IReadOnlyList<YamlConfigSchemaNode>? allOfSchemas,
YamlConfigConditionalSchemas? conditionalSchemas)
{
MinProperties = minProperties;
MaxProperties = maxProperties;
DependentRequired = dependentRequired;
DependentSchemas = dependentSchemas;
AllOfSchemas = allOfSchemas;
ConditionalSchemas = conditionalSchemas;
}
/// <summary>
/// 获取最小属性数量约束。
/// </summary>
public int? MinProperties { get; }
/// <summary>
/// 获取最大属性数量约束。
/// </summary>
public int? MaxProperties { get; }
/// <summary>
/// 获取对象内字段依赖约束。
/// 键表示“触发字段”,值表示“触发字段出现后还必须存在的同级字段集合”。
/// </summary>
public IReadOnlyDictionary<string, IReadOnlyList<string>>? DependentRequired { get; }
/// <summary>
/// 获取对象内条件 schema 约束。
/// 键表示“触发字段”,值表示“触发字段出现后当前对象还必须满足的额外 schema 子树”。
/// </summary>
public IReadOnlyDictionary<string, YamlConfigSchemaNode>? DependentSchemas { get; }
/// <summary>
/// 获取对象内 <c>allOf</c> 组合约束。
/// 每个条目都表示“当前对象还必须额外满足的 focused constraint block”。
/// </summary>
public IReadOnlyList<YamlConfigSchemaNode>? AllOfSchemas { get; }
/// <summary>
/// 获取对象内 object-focused <c>if</c> / <c>then</c> / <c>else</c> 条件约束。
/// 该模型会先用 <c>if</c> 试匹配当前对象,再只对命中的分支叠加 focused constraint block。
/// </summary>
public YamlConfigConditionalSchemas? ConditionalSchemas { get; }
}
/// <summary>
/// 表示一个对象节点上声明的 object-focused <c>if</c> / <c>then</c> / <c>else</c> 条件约束。
/// 三个分支都共享父对象已声明字段集合,不会把分支 schema 扩展成新的生成类型形状。
/// </summary>
internal sealed class YamlConfigConditionalSchemas
{
/// <summary>
/// 初始化条件分支约束模型。
/// </summary>
/// <param name="ifSchema">条件判断 schema。</param>
/// <param name="thenSchema">条件命中时需要满足的 schema。</param>
/// <param name="elseSchema">条件未命中时需要满足的 schema。</param>
public YamlConfigConditionalSchemas(
YamlConfigSchemaNode ifSchema,
YamlConfigSchemaNode? thenSchema,
YamlConfigSchemaNode? elseSchema)
{
ArgumentNullException.ThrowIfNull(ifSchema);
IfSchema = ifSchema;
ThenSchema = thenSchema;
ElseSchema = elseSchema;
}
/// <summary>
/// 获取条件判断 schema。
/// </summary>
public YamlConfigSchemaNode IfSchema { get; }
/// <summary>
/// 获取条件命中时需要满足的 schema。
/// </summary>
public YamlConfigSchemaNode? ThenSchema { get; }
/// <summary>
/// 获取条件未命中时需要满足的 schema。
/// </summary>
public YamlConfigSchemaNode? ElseSchema { get; }
}
/// <summary>
/// 聚合一个标量节点上声明的数值约束与字符串约束。
/// 该包装层保留“标量字段有约束”的统一入口,同时把不同语义的约束分成更小的专用模型。
/// </summary>
internal sealed class YamlConfigScalarConstraints
{
/// <summary>
/// 初始化标量约束模型。
/// </summary>
/// <param name="numericConstraints">数值约束分组。</param>
/// <param name="stringConstraints">字符串约束分组。</param>
public YamlConfigScalarConstraints(
YamlConfigNumericConstraints? numericConstraints,
YamlConfigStringConstraints? stringConstraints)
{
NumericConstraints = numericConstraints;
StringConstraints = stringConstraints;
}
/// <summary>
/// 获取数值约束分组。
/// </summary>
public YamlConfigNumericConstraints? NumericConstraints { get; }
/// <summary>
/// 获取字符串约束分组。
/// </summary>
public YamlConfigStringConstraints? StringConstraints { get; }
}
/// <summary>
/// 表示标量节点上声明的数值范围与步进约束。
/// 该类型只覆盖整数 / 浮点共享的关键字,避免字符串字段继续暴露不相关的成员。
/// </summary>
internal sealed class YamlConfigNumericConstraints
{
/// <summary>
/// 初始化数值约束模型。
/// </summary>
/// <param name="minimum">最小值约束。</param>
/// <param name="maximum">最大值约束。</param>
/// <param name="exclusiveMinimum">开区间最小值约束。</param>
/// <param name="exclusiveMaximum">开区间最大值约束。</param>
/// <param name="multipleOf">数值步进约束。</param>
public YamlConfigNumericConstraints(
double? minimum,
double? maximum,
double? exclusiveMinimum,
double? exclusiveMaximum,
double? multipleOf)
{
Minimum = minimum;
Maximum = maximum;
ExclusiveMinimum = exclusiveMinimum;
ExclusiveMaximum = exclusiveMaximum;
MultipleOf = multipleOf;
}
/// <summary>
/// 获取最小值约束。
/// </summary>
public double? Minimum { get; }
/// <summary>
/// 获取最大值约束。
/// </summary>
public double? Maximum { get; }
/// <summary>
/// 获取开区间最小值约束。
/// </summary>
public double? ExclusiveMinimum { get; }
/// <summary>
/// 获取开区间最大值约束。
/// </summary>
public double? ExclusiveMaximum { get; }
/// <summary>
/// 获取数值步进约束。
/// </summary>
public double? MultipleOf { get; }
}
/// <summary>
/// 表示标量节点上声明的字符串长度、模式与 format 约束。
/// 该模型将正则原文、预编译正则和共享 format 枚举绑定保存,
/// 保证诊断内容与运行时匹配逻辑保持一致。
/// </summary>
internal sealed class YamlConfigStringConstraints
{
/// <summary>
/// 初始化字符串约束模型。
/// </summary>
/// <param name="minLength">最小长度约束。</param>
/// <param name="maxLength">最大长度约束。</param>
/// <param name="pattern">正则模式约束原文。</param>
/// <param name="patternRegex">已编译的正则表达式。</param>
/// <param name="formatConstraint">字符串 format 约束。</param>
public YamlConfigStringConstraints(
int? minLength,
int? maxLength,
string? pattern,
Regex? patternRegex,
YamlConfigStringFormatConstraint? formatConstraint)
{
MinLength = minLength;
MaxLength = maxLength;
Pattern = pattern;
PatternRegex = patternRegex;
FormatConstraint = formatConstraint;
}
/// <summary>
/// 获取最小长度约束。
/// </summary>
public int? MinLength { get; }
/// <summary>
/// 获取最大长度约束。
/// </summary>
public int? MaxLength { get; }
/// <summary>
/// 获取正则模式约束原文。
/// </summary>
public string? Pattern { get; }
/// <summary>
/// 获取已编译的正则表达式。
/// </summary>
public Regex? PatternRegex { get; }
/// <summary>
/// 获取字符串 format 约束。
/// </summary>
public YamlConfigStringFormatConstraint? FormatConstraint { get; }
}
/// <summary>
/// 表示一个已归一化的字符串 format 约束。
/// 该模型同时保留 schema 原文与共享枚举,方便诊断信息稳定展示,又避免运行时校验反复解析字符串。
/// </summary>
internal sealed class YamlConfigStringFormatConstraint
{
/// <summary>
/// 初始化字符串 format 约束模型。
/// </summary>
/// <param name="schemaName">schema 中声明的 format 名称。</param>
/// <param name="kind">归一化后的共享 format 枚举。</param>
public YamlConfigStringFormatConstraint(
string schemaName,
YamlConfigStringFormatKind kind)
{
ArgumentException.ThrowIfNullOrWhiteSpace(schemaName);
SchemaName = schemaName;
Kind = kind;
}
/// <summary>
/// 获取 schema 中声明的 format 名称。
/// </summary>
public string SchemaName { get; }
/// <summary>
/// 获取归一化后的共享 format 枚举。
/// </summary>
public YamlConfigStringFormatKind Kind { get; }
}
/// <summary>
/// 表示当前 Runtime / Generator / Tooling 共享支持的字符串 format 子集。
/// </summary>
internal enum YamlConfigStringFormatKind
{
/// <summary>
/// 表示 <c>yyyy-MM-dd</c> 形式的日期。
/// </summary>
Date,
/// <summary>
/// 表示带显式时区偏移的 RFC 3339 日期时间。
/// </summary>
DateTime,
/// <summary>
/// 表示 day-time duration 形式的持续时间。
/// </summary>
Duration,
/// <summary>
/// 表示基础电子邮件地址格式。
/// </summary>
Email,
/// <summary>
/// 表示带显式时区偏移的 RFC 3339 时间。
/// </summary>
Time,
/// <summary>
/// 表示绝对 URI。
/// </summary>
Uri,
/// <summary>
/// 表示连字符分隔的 UUID 文本。
/// </summary>
Uuid
}
/// <summary>
/// 表示一个数组节点上声明的元素数量、去重与 contains 匹配计数约束。
/// 该模型与标量约束拆分保存,避免数组节点继续共享不适用的标量字段。
/// </summary>
internal sealed class YamlConfigArrayConstraints
{
/// <summary>
/// 初始化数组约束模型。
/// </summary>
/// <param name="minItems">最小元素数量约束。</param>
/// <param name="maxItems">最大元素数量约束。</param>
/// <param name="uniqueItems">是否要求数组元素唯一。</param>
/// <param name="containsConstraints">数组 contains 约束;未声明时为空。</param>
public YamlConfigArrayConstraints(
int? minItems,
int? maxItems,
bool uniqueItems,
YamlConfigArrayContainsConstraints? containsConstraints)
{
MinItems = minItems;
MaxItems = maxItems;
UniqueItems = uniqueItems;
ContainsConstraints = containsConstraints;
}
/// <summary>
/// 获取最小元素数量约束。
/// </summary>
public int? MinItems { get; }
/// <summary>
/// 获取最大元素数量约束。
/// </summary>
public int? MaxItems { get; }
/// <summary>
/// 获取是否要求数组元素唯一。
/// </summary>
public bool UniqueItems { get; }
/// <summary>
/// 获取数组 contains 约束;未声明时返回空。
/// </summary>
public YamlConfigArrayContainsConstraints? ContainsConstraints { get; }
}
/// <summary>
/// 表示数组节点声明的 <c>contains</c> 匹配约束。
/// 该模型把 contains 子 schema 与匹配数量边界聚合在一起,避免数组节点再额外散落多组相关成员。
/// </summary>
internal sealed class YamlConfigArrayContainsConstraints
{
/// <summary>
/// 初始化数组 contains 约束模型。
/// </summary>
/// <param name="containsNode">contains 子 schema。</param>
/// <param name="minContains">最小匹配数量;为 <see langword="null" /> 时按 JSON Schema 语义默认 1。</param>
/// <param name="maxContains">最大匹配数量。</param>
public YamlConfigArrayContainsConstraints(
YamlConfigSchemaNode containsNode,
int? minContains,
int? maxContains)
{
ArgumentNullException.ThrowIfNull(containsNode);
ContainsNode = containsNode;
MinContains = minContains;
MaxContains = maxContains;
}
/// <summary>
/// 获取 contains 子 schema。
/// </summary>
public YamlConfigSchemaNode ContainsNode { get; }
/// <summary>
/// 获取最小匹配数量;未显式声明时返回空,由调用方按默认值 1 解释。
/// </summary>
public int? MinContains { get; }
/// <summary>
/// 获取最大匹配数量。
/// </summary>
public int? MaxContains { get; }
}
/// <summary>
/// 表示单个 YAML 文件中提取出的跨表引用。
/// 该模型保留源文件、字段路径和目标表等诊断信息,以便加载器在批量校验失败时给出可定位的错误。
/// </summary>
internal sealed class YamlConfigReferenceUsage
{
/// <summary>
/// 初始化一个跨表引用使用记录。
/// </summary>
/// <param name="yamlPath">源 YAML 文件路径。</param>
/// <param name="schemaPath">定义该引用的 schema 文件路径。</param>
/// <param name="propertyPath">声明引用的字段路径。</param>
/// <param name="rawValue">YAML 中的原始标量值。</param>
/// <param name="referencedTableName">目标配置表名称。</param>
/// <param name="valueType">引用值的 schema 标量类型。</param>
public YamlConfigReferenceUsage(
string yamlPath,
string schemaPath,
string propertyPath,
string rawValue,
string referencedTableName,
YamlConfigSchemaPropertyType valueType)
{
ArgumentNullException.ThrowIfNull(yamlPath);
ArgumentNullException.ThrowIfNull(schemaPath);
ArgumentNullException.ThrowIfNull(propertyPath);
ArgumentNullException.ThrowIfNull(rawValue);
ArgumentNullException.ThrowIfNull(referencedTableName);
YamlPath = yamlPath;
SchemaPath = schemaPath;
PropertyPath = propertyPath;
RawValue = rawValue;
ReferencedTableName = referencedTableName;
ValueType = valueType;
}
/// <summary>
/// 获取源 YAML 文件路径。
/// </summary>
public string YamlPath { get; }
/// <summary>
/// 获取定义该引用的 schema 文件路径。
/// </summary>
public string SchemaPath { get; }
/// <summary>
/// 获取声明引用的字段路径。
/// </summary>
public string PropertyPath { get; }
/// <summary>
/// 获取 YAML 中的原始标量值。
/// </summary>
public string RawValue { get; }
/// <summary>
/// 获取目标配置表名称。
/// </summary>
public string ReferencedTableName { get; }
/// <summary>
/// 获取引用值的 schema 标量类型。
/// </summary>
public YamlConfigSchemaPropertyType ValueType { get; }
/// <summary>
/// 获取便于诊断显示的字段路径。
/// </summary>
public string DisplayPath => PropertyPath;
}
/// <summary>
/// 表示当前运行时 schema 校验器支持的属性类型。
/// </summary>
internal enum YamlConfigSchemaPropertyType
{
/// <summary>
/// 对象类型。
/// </summary>
Object,
/// <summary>
/// 整数类型。
/// </summary>
Integer,
/// <summary>
/// 数值类型。
/// </summary>
Number,
/// <summary>
/// 布尔类型。
/// </summary>
Boolean,
/// <summary>
/// 字符串类型。
/// </summary>
String,
/// <summary>
/// 数组类型。
/// </summary>
Array
}

View File

@ -0,0 +1,58 @@
using System.Text.RegularExpressions;
namespace GFramework.Game.Config;
/// <summary>
/// 表示标量节点上声明的字符串长度、模式与 format 约束。
/// 该模型将正则原文、预编译正则和共享 format 枚举绑定保存,
/// 保证诊断内容与运行时匹配逻辑保持一致。
/// </summary>
internal sealed class YamlConfigStringConstraints
{
/// <summary>
/// 初始化字符串约束模型。
/// </summary>
/// <param name="minLength">最小长度约束。</param>
/// <param name="maxLength">最大长度约束。</param>
/// <param name="pattern">正则模式约束原文。</param>
/// <param name="patternRegex">已编译的正则表达式。</param>
/// <param name="formatConstraint">字符串 format 约束。</param>
public YamlConfigStringConstraints(
int? minLength,
int? maxLength,
string? pattern,
Regex? patternRegex,
YamlConfigStringFormatConstraint? formatConstraint)
{
MinLength = minLength;
MaxLength = maxLength;
Pattern = pattern;
PatternRegex = patternRegex;
FormatConstraint = formatConstraint;
}
/// <summary>
/// 获取最小长度约束。
/// </summary>
public int? MinLength { get; }
/// <summary>
/// 获取最大长度约束。
/// </summary>
public int? MaxLength { get; }
/// <summary>
/// 获取正则模式约束原文。
/// </summary>
public string? Pattern { get; }
/// <summary>
/// 获取已编译的正则表达式。
/// </summary>
public Regex? PatternRegex { get; }
/// <summary>
/// 获取字符串 format 约束。
/// </summary>
public YamlConfigStringFormatConstraint? FormatConstraint { get; }
}

View File

@ -0,0 +1,33 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示一个已归一化的字符串 format 约束。
/// 该模型同时保留 schema 原文与共享枚举,方便诊断信息稳定展示,又避免运行时校验反复解析字符串。
/// </summary>
internal sealed class YamlConfigStringFormatConstraint
{
/// <summary>
/// 初始化字符串 format 约束模型。
/// </summary>
/// <param name="schemaName">schema 中声明的 format 名称。</param>
/// <param name="kind">归一化后的共享 format 枚举。</param>
public YamlConfigStringFormatConstraint(
string schemaName,
YamlConfigStringFormatKind kind)
{
ArgumentException.ThrowIfNullOrWhiteSpace(schemaName);
SchemaName = schemaName;
Kind = kind;
}
/// <summary>
/// 获取 schema 中声明的 format 名称。
/// </summary>
public string SchemaName { get; }
/// <summary>
/// 获取归一化后的共享 format 枚举。
/// </summary>
public YamlConfigStringFormatKind Kind { get; }
}

View File

@ -0,0 +1,42 @@
namespace GFramework.Game.Config;
/// <summary>
/// 表示当前 Runtime / Generator / Tooling 共享支持的字符串 format 子集。
/// </summary>
internal enum YamlConfigStringFormatKind
{
/// <summary>
/// 表示 <c>yyyy-MM-dd</c> 形式的日期。
/// </summary>
Date,
/// <summary>
/// 表示带显式时区偏移的 RFC 3339 日期时间。
/// </summary>
DateTime,
/// <summary>
/// 表示 day-time duration 形式的持续时间。
/// </summary>
Duration,
/// <summary>
/// 表示基础电子邮件地址格式。
/// </summary>
Email,
/// <summary>
/// 表示带显式时区偏移的 RFC 3339 时间。
/// </summary>
Time,
/// <summary>
/// 表示绝对 URI。
/// </summary>
Uri,
/// <summary>
/// 表示连字符分隔的 UUID 文本。
/// </summary>
Uuid
}

View File

@ -12,7 +12,8 @@
- `2026-04-29` 使用 `$gframework-batch-boot 50` 从 clean build warning 基线继续分批清理 analyzer warnings
- 已接受三个 worker 的 `GFramework.Cqrs.Tests/Mediator/*` 独立切片,三个 Mediator 测试文件的 warning 已清零
- 主线程补齐 `YamlConfigSchemaValidator` 运行时正则 timeout 与 ordinal 字符串比较,先收掉低风险 `MA0009` / `MA0006`
- 当前停止条件为相对 `origin/main` 接近 `50` 个变更文件;本轮尚未接近阈值,下一批可继续处理 `GFramework.Game/Config/YamlConfigSchemaValidator*`
- 已收口两个 Game 追加切片:`YamlConfigSchemaValidator.ObjectKeywords.cs` 方法拆分与 schema model 类型拆文件
- 当前停止条件为相对 `origin/main` 接近 `50` 个变更文件;本轮按用户要求到此结束,不再继续开新切片
## 当前活跃事实
@ -21,27 +22,30 @@
- `dotnet clean -p:RestoreFallbackFolders= -v:quiet`
- 最新结果:成功;标准 `dotnet clean` 仍会先命中当前 WSL 环境的 Windows NuGet fallback 目录,已按既有环境口径先执行 `dotnet restore GFramework.sln -p:RestoreFallbackFolders= --disable-parallel` 后清理
- `dotnet build -p:RestoreFallbackFolders= -clp:WarningsOnly -v:minimal -m:1 -nodeReuse:false`
- 最新结果:成功;`75` warnings、`0` errorswarning 从本轮基线 `236` 降到 `75`
- 最新结果:成功;`15` warnings、`0` errorswarning 从本轮基线 `236` 降到 `15`
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 最新结果:成功;`0 Warning(s)``0 Error(s)`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Mediator"`
- 最新结果:成功;`45` 通过、`0` 失败
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 最新结果:成功;`75 Warning(s)`、`0 Error(s)`;剩余均为 `YamlConfigSchemaValidator*``MA0048` / `MA0051`
- 最新结果:成功;`0 Warning(s)`、`0 Error(s)`
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"`
- 最新结果:成功;`80` 通过、`0` 失败
- 当前批次摘要:
- 当前已提交分支相对 `origin/main...HEAD` 包含 `3` 个变更文件;本次主线程待提交的 `GFramework.Game/Config/YamlConfigSchemaValidator.cs``ai-plan` 文档会把累计变更推到约 `6`文件,低于 `50` 个文件阈值
- 当前分支提交后预计相对 `origin/main...HEAD` 包含 `22` 个变更文件,低于 `50` 个文件阈值
- 已完成 worker 切片:
- `ed269d4``MediatorArchitectureIntegrationTests.cs`,清理 `MA0048` / `MA0004` / `MA0016`
- `121df44``MediatorAdvancedFeaturesTests.cs`,清理 `MA0048` / `MA0004` / `MA0015`
- `9109eec``MediatorComprehensiveTests.cs`,清理 `MA0048` / `MA0004` / `MA0016` / `MA0002` / `MA0015`
- 主线程切片:`YamlConfigSchemaValidator.cs` 正则 timeout 与 ordinal equality清理 `MA0009` / `MA0006`
- Game 追加切片:
- `1395b84``YamlConfigSchemaValidator.ObjectKeywords.cs`,清理该文件 `MA0051`
- 待提交:将 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆到独立同名文件,清理 `MA0048`
## 当前风险
- `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,剩余 `45``MA0048``30``MA0051`
- 缓解措施:下一批优先把 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆到独立文件,再评估 `MA0051` 方法拆分
- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 仍有 `5``MA0051` 方法长度 warning`net8.0` / `net9.0` / `net10.0` 重复为 `15`
- 缓解措施:下一轮只做主 validator 方法拆分,不再混入拆文件或正则安全修复
- 标准 `dotnet clean` 在当前 WSL 环境仍会读取失效的 Windows fallback package folder。
- 缓解措施:本主题验证继续沿用 `-p:RestoreFallbackFolders=`,必要时先执行 solution restore 刷新 Linux 侧资产。
@ -65,12 +69,12 @@
- 权威验证结果统一维护在“当前活跃事实”。
- `GFramework.Cqrs.Tests` 的当前受影响项目 Release 构建已清零,并通过 Mediator 定向测试回归。
- `GFramework.Game` 当前低风险正则 / 字符串比较切片通过 Release 构建与 config 定向测试;剩余 warning 属于拆文件与复杂度拆分。
- `GFramework.Game` 当前 Release 构建已清零,并通过 config 定向测试;仓库 Debug 构建剩余 warning 属于主 validator 方法复杂度拆分。
- `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。
- warning reduction 的仓库级真值以同轮 `dotnet build`、定向 `dotnet test``git diff --check` 为准,并与 trace 中的验证里程碑保持一致。
## 下一步建议
1. 提交主线程 `YamlConfigSchemaValidator` 正则安全补丁与本轮 `ai-plan` 同步
2. 继续下一批 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆文件,目标清理 `MA0048`
3. 再评估 `YamlConfigSchemaValidator.ObjectKeywords.cs` 与主 validator 的 `MA0051` 方法拆分,避免单批触碰过多高耦合逻辑
1. 提交 schema model 拆文件与本轮 `ai-plan` 收口
2. 下一轮只处理 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 剩余 `MA0051` 方法拆分
3. 保持 `RestoreFallbackFolders=` 验证口径,避免当前 WSL fallback package folder 干扰

View File

@ -17,27 +17,33 @@
- 主线程实施:
- 在 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 为固定格式正则与 schema `pattern` 正则补充 timeout避免运行时正则输入继续触发 `MA0009`
- 将三处字符串等值比较改为 ordinal `string.Equals`,清理 `MA0006`
- 接受 `1395b84``YamlConfigSchemaValidator.ObjectKeywords.cs` 方法拆分,清理该文件 `MA0051`
- 收口被中止 worker 留下的 schema model 拆文件变更,将 `YamlConfigSchemaValidator.cs` 末尾类型移动到同名文件,清理 `MA0048`
- 验证里程碑:
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 结果:成功;`0 Warning(s)``0 Error(s)`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Mediator"`
- 结果:成功;`45` 通过、`0` 失败
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 结果:成功;`75 Warning(s)`、`0 Error(s)`
- 结果:成功;`0 Warning(s)`、`0 Error(s)`
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"`
- 结果:成功;`80` 通过、`0` 失败
- `dotnet clean -p:RestoreFallbackFolders= -v:quiet`
- 结果:成功
- `dotnet build -p:RestoreFallbackFolders= -clp:WarningsOnly -v:minimal -m:1 -nodeReuse:false`
- 结果:成功;`75` warnings、`0` errors
- 中间结果:成功;`75` warnings、`0` errors
- `dotnet clean -p:RestoreFallbackFolders= -v:quiet`
- 结果:成功
- `dotnet build -p:RestoreFallbackFolders= -clp:Summary -v:minimal -m:1 -nodeReuse:false`
- 结果:成功;`15 Warning(s)``0 Error(s)`
- `git diff --check`
- 结果:成功;无新增 whitespace / conflict-marker 问题
- 当前指标:
- warning 总数:`236` -> `75`
- 剩余 warning 分布:`GFramework.Game/Config/YamlConfigSchemaValidator.cs` `60` 条,`GFramework.Game/Config/YamlConfigSchemaValidator.ObjectKeywords.cs` `15` 条;规则为 `MA0048` `45` 条、`MA0051` `30`
- 已提交分支 diff`3` 个文件;主线程待提交后预计约 `6` 个文件,低于 `50` 个文件阈值
- warning 总数:`236` -> `15`
- 剩余 warning 分布:`GFramework.Game/Config/YamlConfigSchemaValidator.cs` `MA0051` `15`5 个方法跨 3 个 TFM
- 本轮提交后预计分支 diff`22` 个文件,低于 `50` 个文件阈值
- 下一步:
- 提交主线程 Game / ai-plan 同步后,继续 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆文件,优先清理 `MA0048`
- 按用户要求本轮到此结束;下一轮只处理 `YamlConfigSchemaValidator.cs` 剩余 `MA0051` 方法拆分
## 2026-04-28 — RP-092