From 104ac25dc36394080eb37f3e9ab65ed469c8cda4 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 29 Apr 2026 08:38:23 +0800 Subject: [PATCH] =?UTF-8?q?refactor(game):=20=E6=8B=86=E5=88=86=20schema?= =?UTF-8?q?=20=E6=A0=A1=E9=AA=8C=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 schema model 类型到独立同名文件 - 清理 schema 校验模型的文件命名 analyzer 告警 - 更新 warning reduction 批处理收口状态 --- .../Config/YamlConfigAllowedValue.cs | 32 + .../Config/YamlConfigArrayConstraints.cs | 47 + .../YamlConfigArrayContainsConstraints.cs | 41 + .../Config/YamlConfigConditionalSchemas.cs | 41 + .../Config/YamlConfigConstantValue.cs | 32 + .../Config/YamlConfigNumericConstraints.cs | 55 + .../Config/YamlConfigObjectConstraints.cs | 67 ++ .../Config/YamlConfigReferenceUsage.cs | 74 ++ .../Config/YamlConfigScalarConstraints.cs | 31 + GFramework.Game/Config/YamlConfigSchema.cs | 44 + .../Config/YamlConfigSchemaNode.cs | 330 ++++++ .../Config/YamlConfigSchemaPropertyType.cs | 37 + .../Config/YamlConfigSchemaValidator.cs | 947 ------------------ .../Config/YamlConfigStringConstraints.cs | 58 ++ .../YamlConfigStringFormatConstraint.cs | 33 + .../Config/YamlConfigStringFormatKind.cs | 42 + .../analyzer-warning-reduction-tracking.md | 24 +- .../analyzer-warning-reduction-trace.md | 18 +- 18 files changed, 990 insertions(+), 963 deletions(-) create mode 100644 GFramework.Game/Config/YamlConfigAllowedValue.cs create mode 100644 GFramework.Game/Config/YamlConfigArrayConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigArrayContainsConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigConditionalSchemas.cs create mode 100644 GFramework.Game/Config/YamlConfigConstantValue.cs create mode 100644 GFramework.Game/Config/YamlConfigNumericConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigObjectConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigReferenceUsage.cs create mode 100644 GFramework.Game/Config/YamlConfigScalarConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigSchema.cs create mode 100644 GFramework.Game/Config/YamlConfigSchemaNode.cs create mode 100644 GFramework.Game/Config/YamlConfigSchemaPropertyType.cs create mode 100644 GFramework.Game/Config/YamlConfigStringConstraints.cs create mode 100644 GFramework.Game/Config/YamlConfigStringFormatConstraint.cs create mode 100644 GFramework.Game/Config/YamlConfigStringFormatKind.cs diff --git a/GFramework.Game/Config/YamlConfigAllowedValue.cs b/GFramework.Game/Config/YamlConfigAllowedValue.cs new file mode 100644 index 00000000..60077ef5 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigAllowedValue.cs @@ -0,0 +1,32 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个节点上声明的单个 enum 候选值。 +/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。 +/// +internal sealed class YamlConfigAllowedValue +{ + /// + /// 初始化一个枚举候选值模型。 + /// + /// 用于与 YAML 节点比较的稳定键。 + /// 用于诊断输出的原始 JSON 文本。 + public YamlConfigAllowedValue(string comparableValue, string displayValue) + { + ArgumentNullException.ThrowIfNull(comparableValue); + ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); + + ComparableValue = comparableValue; + DisplayValue = displayValue; + } + + /// + /// 获取用于运行时比较的稳定键。 + /// + public string ComparableValue { get; } + + /// + /// 获取用于诊断输出的原始 JSON 文本。 + /// + public string DisplayValue { get; } +} diff --git a/GFramework.Game/Config/YamlConfigArrayConstraints.cs b/GFramework.Game/Config/YamlConfigArrayConstraints.cs new file mode 100644 index 00000000..64ddaf3c --- /dev/null +++ b/GFramework.Game/Config/YamlConfigArrayConstraints.cs @@ -0,0 +1,47 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个数组节点上声明的元素数量、去重与 contains 匹配计数约束。 +/// 该模型与标量约束拆分保存,避免数组节点继续共享不适用的标量字段。 +/// +internal sealed class YamlConfigArrayConstraints +{ + /// + /// 初始化数组约束模型。 + /// + /// 最小元素数量约束。 + /// 最大元素数量约束。 + /// 是否要求数组元素唯一。 + /// 数组 contains 约束;未声明时为空。 + public YamlConfigArrayConstraints( + int? minItems, + int? maxItems, + bool uniqueItems, + YamlConfigArrayContainsConstraints? containsConstraints) + { + MinItems = minItems; + MaxItems = maxItems; + UniqueItems = uniqueItems; + ContainsConstraints = containsConstraints; + } + + /// + /// 获取最小元素数量约束。 + /// + public int? MinItems { get; } + + /// + /// 获取最大元素数量约束。 + /// + public int? MaxItems { get; } + + /// + /// 获取是否要求数组元素唯一。 + /// + public bool UniqueItems { get; } + + /// + /// 获取数组 contains 约束;未声明时返回空。 + /// + public YamlConfigArrayContainsConstraints? ContainsConstraints { get; } +} diff --git a/GFramework.Game/Config/YamlConfigArrayContainsConstraints.cs b/GFramework.Game/Config/YamlConfigArrayContainsConstraints.cs new file mode 100644 index 00000000..a61800f8 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigArrayContainsConstraints.cs @@ -0,0 +1,41 @@ +namespace GFramework.Game.Config; + +/// +/// 表示数组节点声明的 contains 匹配约束。 +/// 该模型把 contains 子 schema 与匹配数量边界聚合在一起,避免数组节点再额外散落多组相关成员。 +/// +internal sealed class YamlConfigArrayContainsConstraints +{ + /// + /// 初始化数组 contains 约束模型。 + /// + /// contains 子 schema。 + /// 最小匹配数量;为 时按 JSON Schema 语义默认 1。 + /// 最大匹配数量。 + public YamlConfigArrayContainsConstraints( + YamlConfigSchemaNode containsNode, + int? minContains, + int? maxContains) + { + ArgumentNullException.ThrowIfNull(containsNode); + + ContainsNode = containsNode; + MinContains = minContains; + MaxContains = maxContains; + } + + /// + /// 获取 contains 子 schema。 + /// + public YamlConfigSchemaNode ContainsNode { get; } + + /// + /// 获取最小匹配数量;未显式声明时返回空,由调用方按默认值 1 解释。 + /// + public int? MinContains { get; } + + /// + /// 获取最大匹配数量。 + /// + public int? MaxContains { get; } +} diff --git a/GFramework.Game/Config/YamlConfigConditionalSchemas.cs b/GFramework.Game/Config/YamlConfigConditionalSchemas.cs new file mode 100644 index 00000000..7d4bbbf2 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigConditionalSchemas.cs @@ -0,0 +1,41 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个对象节点上声明的 object-focused if / then / else 条件约束。 +/// 三个分支都共享父对象已声明字段集合,不会把分支 schema 扩展成新的生成类型形状。 +/// +internal sealed class YamlConfigConditionalSchemas +{ + /// + /// 初始化条件分支约束模型。 + /// + /// 条件判断 schema。 + /// 条件命中时需要满足的 schema。 + /// 条件未命中时需要满足的 schema。 + public YamlConfigConditionalSchemas( + YamlConfigSchemaNode ifSchema, + YamlConfigSchemaNode? thenSchema, + YamlConfigSchemaNode? elseSchema) + { + ArgumentNullException.ThrowIfNull(ifSchema); + + IfSchema = ifSchema; + ThenSchema = thenSchema; + ElseSchema = elseSchema; + } + + /// + /// 获取条件判断 schema。 + /// + public YamlConfigSchemaNode IfSchema { get; } + + /// + /// 获取条件命中时需要满足的 schema。 + /// + public YamlConfigSchemaNode? ThenSchema { get; } + + /// + /// 获取条件未命中时需要满足的 schema。 + /// + public YamlConfigSchemaNode? ElseSchema { get; } +} diff --git a/GFramework.Game/Config/YamlConfigConstantValue.cs b/GFramework.Game/Config/YamlConfigConstantValue.cs new file mode 100644 index 00000000..48caa4c5 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigConstantValue.cs @@ -0,0 +1,32 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个节点上声明的 const 约束。 +/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。 +/// +internal sealed class YamlConfigConstantValue +{ + /// + /// 初始化常量约束模型。 + /// + /// 用于与 YAML 节点比较的稳定键。 + /// 用于诊断输出的原始常量文本。 + public YamlConfigConstantValue(string comparableValue, string displayValue) + { + ArgumentNullException.ThrowIfNull(comparableValue); + ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); + + ComparableValue = comparableValue; + DisplayValue = displayValue; + } + + /// + /// 获取用于运行时比较的稳定键。 + /// + public string ComparableValue { get; } + + /// + /// 获取用于诊断输出的原始 JSON 常量文本。 + /// + public string DisplayValue { get; } +} diff --git a/GFramework.Game/Config/YamlConfigNumericConstraints.cs b/GFramework.Game/Config/YamlConfigNumericConstraints.cs new file mode 100644 index 00000000..977eb007 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigNumericConstraints.cs @@ -0,0 +1,55 @@ +namespace GFramework.Game.Config; + +/// +/// 表示标量节点上声明的数值范围与步进约束。 +/// 该类型只覆盖整数 / 浮点共享的关键字,避免字符串字段继续暴露不相关的成员。 +/// +internal sealed class YamlConfigNumericConstraints +{ + /// + /// 初始化数值约束模型。 + /// + /// 最小值约束。 + /// 最大值约束。 + /// 开区间最小值约束。 + /// 开区间最大值约束。 + /// 数值步进约束。 + public YamlConfigNumericConstraints( + double? minimum, + double? maximum, + double? exclusiveMinimum, + double? exclusiveMaximum, + double? multipleOf) + { + Minimum = minimum; + Maximum = maximum; + ExclusiveMinimum = exclusiveMinimum; + ExclusiveMaximum = exclusiveMaximum; + MultipleOf = multipleOf; + } + + /// + /// 获取最小值约束。 + /// + public double? Minimum { get; } + + /// + /// 获取最大值约束。 + /// + public double? Maximum { get; } + + /// + /// 获取开区间最小值约束。 + /// + public double? ExclusiveMinimum { get; } + + /// + /// 获取开区间最大值约束。 + /// + public double? ExclusiveMaximum { get; } + + /// + /// 获取数值步进约束。 + /// + public double? MultipleOf { get; } +} diff --git a/GFramework.Game/Config/YamlConfigObjectConstraints.cs b/GFramework.Game/Config/YamlConfigObjectConstraints.cs new file mode 100644 index 00000000..01b460f9 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigObjectConstraints.cs @@ -0,0 +1,67 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个对象节点上声明的属性数量约束、字段依赖约束、条件子 schema 与组合约束。 +/// 该模型将对象级约束与数组 / 标量约束拆开保存,避免运行时节点继续暴露无关成员。 +/// +internal sealed class YamlConfigObjectConstraints +{ + /// + /// 初始化对象约束模型。 + /// + /// 最小属性数量约束。 + /// 最大属性数量约束。 + /// 对象内字段依赖约束。 + /// 对象内条件 schema 约束。 + /// 对象内组合 schema 约束。 + /// 对象内条件分支约束。 + public YamlConfigObjectConstraints( + int? minProperties, + int? maxProperties, + IReadOnlyDictionary>? dependentRequired, + IReadOnlyDictionary? dependentSchemas, + IReadOnlyList? allOfSchemas, + YamlConfigConditionalSchemas? conditionalSchemas) + { + MinProperties = minProperties; + MaxProperties = maxProperties; + DependentRequired = dependentRequired; + DependentSchemas = dependentSchemas; + AllOfSchemas = allOfSchemas; + ConditionalSchemas = conditionalSchemas; + } + + /// + /// 获取最小属性数量约束。 + /// + public int? MinProperties { get; } + + /// + /// 获取最大属性数量约束。 + /// + public int? MaxProperties { get; } + + /// + /// 获取对象内字段依赖约束。 + /// 键表示“触发字段”,值表示“触发字段出现后还必须存在的同级字段集合”。 + /// + public IReadOnlyDictionary>? DependentRequired { get; } + + /// + /// 获取对象内条件 schema 约束。 + /// 键表示“触发字段”,值表示“触发字段出现后当前对象还必须满足的额外 schema 子树”。 + /// + public IReadOnlyDictionary? DependentSchemas { get; } + + /// + /// 获取对象内 allOf 组合约束。 + /// 每个条目都表示“当前对象还必须额外满足的 focused constraint block”。 + /// + public IReadOnlyList? AllOfSchemas { get; } + + /// + /// 获取对象内 object-focused if / then / else 条件约束。 + /// 该模型会先用 if 试匹配当前对象,再只对命中的分支叠加 focused constraint block。 + /// + public YamlConfigConditionalSchemas? ConditionalSchemas { get; } +} diff --git a/GFramework.Game/Config/YamlConfigReferenceUsage.cs b/GFramework.Game/Config/YamlConfigReferenceUsage.cs new file mode 100644 index 00000000..ba9ff1ea --- /dev/null +++ b/GFramework.Game/Config/YamlConfigReferenceUsage.cs @@ -0,0 +1,74 @@ +namespace GFramework.Game.Config; + +/// +/// 表示单个 YAML 文件中提取出的跨表引用。 +/// 该模型保留源文件、字段路径和目标表等诊断信息,以便加载器在批量校验失败时给出可定位的错误。 +/// +internal sealed class YamlConfigReferenceUsage +{ + /// + /// 初始化一个跨表引用使用记录。 + /// + /// 源 YAML 文件路径。 + /// 定义该引用的 schema 文件路径。 + /// 声明引用的字段路径。 + /// YAML 中的原始标量值。 + /// 目标配置表名称。 + /// 引用值的 schema 标量类型。 + 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; + } + + /// + /// 获取源 YAML 文件路径。 + /// + public string YamlPath { get; } + + /// + /// 获取定义该引用的 schema 文件路径。 + /// + public string SchemaPath { get; } + + /// + /// 获取声明引用的字段路径。 + /// + public string PropertyPath { get; } + + /// + /// 获取 YAML 中的原始标量值。 + /// + public string RawValue { get; } + + /// + /// 获取目标配置表名称。 + /// + public string ReferencedTableName { get; } + + /// + /// 获取引用值的 schema 标量类型。 + /// + public YamlConfigSchemaPropertyType ValueType { get; } + + /// + /// 获取便于诊断显示的字段路径。 + /// + public string DisplayPath => PropertyPath; +} diff --git a/GFramework.Game/Config/YamlConfigScalarConstraints.cs b/GFramework.Game/Config/YamlConfigScalarConstraints.cs new file mode 100644 index 00000000..df632030 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigScalarConstraints.cs @@ -0,0 +1,31 @@ +namespace GFramework.Game.Config; + +/// +/// 聚合一个标量节点上声明的数值约束与字符串约束。 +/// 该包装层保留“标量字段有约束”的统一入口,同时把不同语义的约束分成更小的专用模型。 +/// +internal sealed class YamlConfigScalarConstraints +{ + /// + /// 初始化标量约束模型。 + /// + /// 数值约束分组。 + /// 字符串约束分组。 + public YamlConfigScalarConstraints( + YamlConfigNumericConstraints? numericConstraints, + YamlConfigStringConstraints? stringConstraints) + { + NumericConstraints = numericConstraints; + StringConstraints = stringConstraints; + } + + /// + /// 获取数值约束分组。 + /// + public YamlConfigNumericConstraints? NumericConstraints { get; } + + /// + /// 获取字符串约束分组。 + /// + public YamlConfigStringConstraints? StringConstraints { get; } +} diff --git a/GFramework.Game/Config/YamlConfigSchema.cs b/GFramework.Game/Config/YamlConfigSchema.cs new file mode 100644 index 00000000..e3ae2f0c --- /dev/null +++ b/GFramework.Game/Config/YamlConfigSchema.cs @@ -0,0 +1,44 @@ +namespace GFramework.Game.Config; + +/// +/// 表示已解析并可用于运行时校验的 JSON Schema。 +/// 该模型保留根节点与引用依赖集合,避免运行时引入完整 schema 引擎。 +/// +internal sealed class YamlConfigSchema +{ + /// + /// 初始化一个可用于运行时校验的 schema 模型。 + /// + /// Schema 文件路径。 + /// 根节点模型。 + /// Schema 声明的目标引用表名称集合。 + public YamlConfigSchema( + string schemaPath, + YamlConfigSchemaNode rootNode, + IReadOnlyCollection referencedTableNames) + { + ArgumentNullException.ThrowIfNull(schemaPath); + ArgumentNullException.ThrowIfNull(rootNode); + ArgumentNullException.ThrowIfNull(referencedTableNames); + + SchemaPath = schemaPath; + RootNode = rootNode; + ReferencedTableNames = referencedTableNames; + } + + /// + /// 获取 schema 文件路径。 + /// + public string SchemaPath { get; } + + /// + /// 获取根节点模型。 + /// + public YamlConfigSchemaNode RootNode { get; } + + /// + /// 获取 schema 声明的目标引用表名称集合。 + /// 该信息用于热重载时推导受影响的依赖表闭包。 + /// + public IReadOnlyCollection ReferencedTableNames { get; } +} diff --git a/GFramework.Game/Config/YamlConfigSchemaNode.cs b/GFramework.Game/Config/YamlConfigSchemaNode.cs new file mode 100644 index 00000000..35509bef --- /dev/null +++ b/GFramework.Game/Config/YamlConfigSchemaNode.cs @@ -0,0 +1,330 @@ +namespace GFramework.Game.Config; + +/// +/// 表示单个 schema 节点的最小运行时描述。 +/// 同一个模型同时覆盖对象、数组和标量,便于递归校验逻辑只依赖一种树结构。 +/// +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; + } + + /// + /// 获取节点类型。 + /// + public YamlConfigSchemaPropertyType NodeType { get; } + + /// + /// 获取对象属性集合;非对象节点时返回空。 + /// + public IReadOnlyDictionary? Properties { get; } + + /// + /// 获取对象必填属性集合;非对象节点时返回空。 + /// + public IReadOnlyCollection? RequiredProperties { get; } + + /// + /// 获取数组元素节点;非数组节点时返回空。 + /// + public YamlConfigSchemaNode? ItemNode { get; } + + /// + /// 获取目标引用表名称;未声明跨表引用时返回空。 + /// + public string? ReferenceTableName { get; } + + /// + /// 获取节点允许值集合;未声明 enum 时返回空。 + /// + public IReadOnlyCollection? AllowedValues { get; } + + /// + /// 获取标量范围与长度约束;未声明时返回空。 + /// + public YamlConfigScalarConstraints? Constraints { get; } + + /// + /// 获取对象属性数量约束;未声明时返回空。 + /// + public YamlConfigObjectConstraints? ObjectConstraints { get; } + + /// + /// 获取数组元素数量约束;未声明时返回空。 + /// + public YamlConfigArrayConstraints? ArrayConstraints { get; } + + /// + /// 获取节点常量约束;未声明 const 时返回空。 + /// + public YamlConfigConstantValue? ConstantValue { get; } + + /// + /// 获取节点声明的 not 子 schema;未声明时返回空。 + /// + public YamlConfigSchemaNode? NegatedSchemaNode { get; } + + /// + /// 获取用于诊断显示的 schema 路径提示。 + /// 当前节点本身不记录独立路径,因此对象校验会回退到所属根 schema 路径。 + /// + public string SchemaPathHint { get; } + + /// + /// 创建对象节点描述。 + /// + /// 对象属性集合。 + /// 对象必填属性集合。 + /// 对象属性数量约束。 + /// 用于错误信息的 schema 文件路径提示。 + /// 对象节点模型。 + public static YamlConfigSchemaNode CreateObject( + IReadOnlyDictionary? properties, + IReadOnlyCollection? 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); + } + + /// + /// 创建数组节点描述。 + /// + /// 数组元素节点。 + /// 数组节点允许值集合。 + /// 数组元素数量约束。 + /// 用于错误信息的 schema 文件路径提示。 + /// 数组节点模型。 + public static YamlConfigSchemaNode CreateArray( + YamlConfigSchemaNode itemNode, + IReadOnlyCollection? 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); + } + + /// + /// 创建标量节点描述。 + /// + /// 标量节点类型。 + /// 目标引用表名称。 + /// 标量允许值集合。 + /// 标量范围与长度约束。 + /// 用于错误信息的 schema 文件路径提示。 + /// 标量节点模型。 + public static YamlConfigSchemaNode CreateScalar( + YamlConfigSchemaPropertyType nodeType, + string? referenceTableName, + IReadOnlyCollection? 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); + } + + /// + /// 基于当前节点复制一个只替换引用表名称的新节点。 + /// 该方法用于把数组级别的 ref-table 语义挂接到元素节点上。 + /// + /// 新的目标引用表名称。 + /// 复制后的节点。 + public YamlConfigSchemaNode WithReferenceTable(string referenceTableName) + { + return new YamlConfigSchemaNode( + NodeType, + _children, + _validation.WithReferenceTable(referenceTableName), + SchemaPathHint); + } + + /// + /// 基于当前节点复制一个只替换 enum 允许值集合的新节点。 + /// + /// 新的允许值集合。 + /// 复制后的节点。 + public YamlConfigSchemaNode WithAllowedValues(IReadOnlyCollection? allowedValues) + { + return new YamlConfigSchemaNode( + NodeType, + _children, + _validation.WithAllowedValues(allowedValues), + SchemaPathHint); + } + + /// + /// 基于当前节点复制一个只替换常量约束的新节点。 + /// + /// 新的常量约束。 + /// 复制后的节点。 + public YamlConfigSchemaNode WithConstantValue(YamlConfigConstantValue? constantValue) + { + return new YamlConfigSchemaNode( + NodeType, + _children, + _validation.WithConstantValue(constantValue), + SchemaPathHint); + } + + /// + /// 基于当前节点复制一个只替换 not 子 schema 的新节点。 + /// + /// 新的 negated schema。 + /// 复制后的节点。 + public YamlConfigSchemaNode WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode) + { + return new YamlConfigSchemaNode( + NodeType, + _children, + _validation.WithNegatedSchemaNode(negatedSchemaNode), + SchemaPathHint); + } + + private sealed class NodeChildren + { + public NodeChildren( + IReadOnlyDictionary? properties, + IReadOnlyCollection? requiredProperties, + YamlConfigSchemaNode? itemNode) + { + Properties = properties; + RequiredProperties = requiredProperties; + ItemNode = itemNode; + } + + public static NodeChildren None { get; } = new(properties: null, requiredProperties: null, itemNode: null); + + public IReadOnlyDictionary? Properties { get; } + + public IReadOnlyCollection? RequiredProperties { get; } + + public YamlConfigSchemaNode? ItemNode { get; } + } + + private sealed class NodeValidation + { + public NodeValidation( + string? referenceTableName, + IReadOnlyCollection? 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? 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? 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); + } + } +} diff --git a/GFramework.Game/Config/YamlConfigSchemaPropertyType.cs b/GFramework.Game/Config/YamlConfigSchemaPropertyType.cs new file mode 100644 index 00000000..725819dc --- /dev/null +++ b/GFramework.Game/Config/YamlConfigSchemaPropertyType.cs @@ -0,0 +1,37 @@ +namespace GFramework.Game.Config; + +/// +/// 表示当前运行时 schema 校验器支持的属性类型。 +/// +internal enum YamlConfigSchemaPropertyType +{ + /// + /// 对象类型。 + /// + Object, + + /// + /// 整数类型。 + /// + Integer, + + /// + /// 数值类型。 + /// + Number, + + /// + /// 布尔类型。 + /// + Boolean, + + /// + /// 字符串类型。 + /// + String, + + /// + /// 数组类型。 + /// + Array +} diff --git a/GFramework.Game/Config/YamlConfigSchemaValidator.cs b/GFramework.Game/Config/YamlConfigSchemaValidator.cs index 4350e035..42d187ce 100644 --- a/GFramework.Game/Config/YamlConfigSchemaValidator.cs +++ b/GFramework.Game/Config/YamlConfigSchemaValidator.cs @@ -3650,950 +3650,3 @@ internal static partial class YamlConfigSchemaValidator !string.Equals(tag, "tag:yaml.org,2002:null", StringComparison.Ordinal); } } - -/// -/// 表示已解析并可用于运行时校验的 JSON Schema。 -/// 该模型保留根节点与引用依赖集合,避免运行时引入完整 schema 引擎。 -/// -internal sealed class YamlConfigSchema -{ - /// - /// 初始化一个可用于运行时校验的 schema 模型。 - /// - /// Schema 文件路径。 - /// 根节点模型。 - /// Schema 声明的目标引用表名称集合。 - public YamlConfigSchema( - string schemaPath, - YamlConfigSchemaNode rootNode, - IReadOnlyCollection referencedTableNames) - { - ArgumentNullException.ThrowIfNull(schemaPath); - ArgumentNullException.ThrowIfNull(rootNode); - ArgumentNullException.ThrowIfNull(referencedTableNames); - - SchemaPath = schemaPath; - RootNode = rootNode; - ReferencedTableNames = referencedTableNames; - } - - /// - /// 获取 schema 文件路径。 - /// - public string SchemaPath { get; } - - /// - /// 获取根节点模型。 - /// - public YamlConfigSchemaNode RootNode { get; } - - /// - /// 获取 schema 声明的目标引用表名称集合。 - /// 该信息用于热重载时推导受影响的依赖表闭包。 - /// - public IReadOnlyCollection ReferencedTableNames { get; } -} - -/// -/// 表示单个 schema 节点的最小运行时描述。 -/// 同一个模型同时覆盖对象、数组和标量,便于递归校验逻辑只依赖一种树结构。 -/// -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; - } - - /// - /// 获取节点类型。 - /// - public YamlConfigSchemaPropertyType NodeType { get; } - - /// - /// 获取对象属性集合;非对象节点时返回空。 - /// - public IReadOnlyDictionary? Properties { get; } - - /// - /// 获取对象必填属性集合;非对象节点时返回空。 - /// - public IReadOnlyCollection? RequiredProperties { get; } - - /// - /// 获取数组元素节点;非数组节点时返回空。 - /// - public YamlConfigSchemaNode? ItemNode { get; } - - /// - /// 获取目标引用表名称;未声明跨表引用时返回空。 - /// - public string? ReferenceTableName { get; } - - /// - /// 获取节点允许值集合;未声明 enum 时返回空。 - /// - public IReadOnlyCollection? AllowedValues { get; } - - /// - /// 获取标量范围与长度约束;未声明时返回空。 - /// - public YamlConfigScalarConstraints? Constraints { get; } - - /// - /// 获取对象属性数量约束;未声明时返回空。 - /// - public YamlConfigObjectConstraints? ObjectConstraints { get; } - - /// - /// 获取数组元素数量约束;未声明时返回空。 - /// - public YamlConfigArrayConstraints? ArrayConstraints { get; } - - /// - /// 获取节点常量约束;未声明 const 时返回空。 - /// - public YamlConfigConstantValue? ConstantValue { get; } - - /// - /// 获取节点声明的 not 子 schema;未声明时返回空。 - /// - public YamlConfigSchemaNode? NegatedSchemaNode { get; } - - /// - /// 获取用于诊断显示的 schema 路径提示。 - /// 当前节点本身不记录独立路径,因此对象校验会回退到所属根 schema 路径。 - /// - public string SchemaPathHint { get; } - - /// - /// 创建对象节点描述。 - /// - /// 对象属性集合。 - /// 对象必填属性集合。 - /// 对象属性数量约束。 - /// 用于错误信息的 schema 文件路径提示。 - /// 对象节点模型。 - public static YamlConfigSchemaNode CreateObject( - IReadOnlyDictionary? properties, - IReadOnlyCollection? 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); - } - - /// - /// 创建数组节点描述。 - /// - /// 数组元素节点。 - /// 数组节点允许值集合。 - /// 数组元素数量约束。 - /// 用于错误信息的 schema 文件路径提示。 - /// 数组节点模型。 - public static YamlConfigSchemaNode CreateArray( - YamlConfigSchemaNode itemNode, - IReadOnlyCollection? 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); - } - - /// - /// 创建标量节点描述。 - /// - /// 标量节点类型。 - /// 目标引用表名称。 - /// 标量允许值集合。 - /// 标量范围与长度约束。 - /// 用于错误信息的 schema 文件路径提示。 - /// 标量节点模型。 - public static YamlConfigSchemaNode CreateScalar( - YamlConfigSchemaPropertyType nodeType, - string? referenceTableName, - IReadOnlyCollection? 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); - } - - /// - /// 基于当前节点复制一个只替换引用表名称的新节点。 - /// 该方法用于把数组级别的 ref-table 语义挂接到元素节点上。 - /// - /// 新的目标引用表名称。 - /// 复制后的节点。 - public YamlConfigSchemaNode WithReferenceTable(string referenceTableName) - { - return new YamlConfigSchemaNode( - NodeType, - _children, - _validation.WithReferenceTable(referenceTableName), - SchemaPathHint); - } - - /// - /// 基于当前节点复制一个只替换 enum 允许值集合的新节点。 - /// - /// 新的允许值集合。 - /// 复制后的节点。 - public YamlConfigSchemaNode WithAllowedValues(IReadOnlyCollection? allowedValues) - { - return new YamlConfigSchemaNode( - NodeType, - _children, - _validation.WithAllowedValues(allowedValues), - SchemaPathHint); - } - - /// - /// 基于当前节点复制一个只替换常量约束的新节点。 - /// - /// 新的常量约束。 - /// 复制后的节点。 - public YamlConfigSchemaNode WithConstantValue(YamlConfigConstantValue? constantValue) - { - return new YamlConfigSchemaNode( - NodeType, - _children, - _validation.WithConstantValue(constantValue), - SchemaPathHint); - } - - /// - /// 基于当前节点复制一个只替换 not 子 schema 的新节点。 - /// - /// 新的 negated schema。 - /// 复制后的节点。 - public YamlConfigSchemaNode WithNegatedSchemaNode(YamlConfigSchemaNode? negatedSchemaNode) - { - return new YamlConfigSchemaNode( - NodeType, - _children, - _validation.WithNegatedSchemaNode(negatedSchemaNode), - SchemaPathHint); - } - - private sealed class NodeChildren - { - public NodeChildren( - IReadOnlyDictionary? properties, - IReadOnlyCollection? requiredProperties, - YamlConfigSchemaNode? itemNode) - { - Properties = properties; - RequiredProperties = requiredProperties; - ItemNode = itemNode; - } - - public static NodeChildren None { get; } = new(properties: null, requiredProperties: null, itemNode: null); - - public IReadOnlyDictionary? Properties { get; } - - public IReadOnlyCollection? RequiredProperties { get; } - - public YamlConfigSchemaNode? ItemNode { get; } - } - - private sealed class NodeValidation - { - public NodeValidation( - string? referenceTableName, - IReadOnlyCollection? 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? 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? 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); - } - } -} - -/// -/// 表示一个节点上声明的 const 约束。 -/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。 -/// -internal sealed class YamlConfigConstantValue -{ - /// - /// 初始化常量约束模型。 - /// - /// 用于与 YAML 节点比较的稳定键。 - /// 用于诊断输出的原始常量文本。 - public YamlConfigConstantValue(string comparableValue, string displayValue) - { - ArgumentNullException.ThrowIfNull(comparableValue); - ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); - - ComparableValue = comparableValue; - DisplayValue = displayValue; - } - - /// - /// 获取用于运行时比较的稳定键。 - /// - public string ComparableValue { get; } - - /// - /// 获取用于诊断输出的原始 JSON 常量文本。 - /// - public string DisplayValue { get; } -} - -/// -/// 表示一个节点上声明的单个 enum 候选值。 -/// 该模型同时保留稳定比较键与原始 JSON 文本,分别供运行时匹配和诊断输出复用。 -/// -internal sealed class YamlConfigAllowedValue -{ - /// - /// 初始化一个枚举候选值模型。 - /// - /// 用于与 YAML 节点比较的稳定键。 - /// 用于诊断输出的原始 JSON 文本。 - public YamlConfigAllowedValue(string comparableValue, string displayValue) - { - ArgumentNullException.ThrowIfNull(comparableValue); - ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); - - ComparableValue = comparableValue; - DisplayValue = displayValue; - } - - /// - /// 获取用于运行时比较的稳定键。 - /// - public string ComparableValue { get; } - - /// - /// 获取用于诊断输出的原始 JSON 文本。 - /// - public string DisplayValue { get; } -} - -/// -/// 表示一个对象节点上声明的属性数量约束、字段依赖约束、条件子 schema 与组合约束。 -/// 该模型将对象级约束与数组 / 标量约束拆开保存,避免运行时节点继续暴露无关成员。 -/// -internal sealed class YamlConfigObjectConstraints -{ - /// - /// 初始化对象约束模型。 - /// - /// 最小属性数量约束。 - /// 最大属性数量约束。 - /// 对象内字段依赖约束。 - /// 对象内条件 schema 约束。 - /// 对象内组合 schema 约束。 - /// 对象内条件分支约束。 - public YamlConfigObjectConstraints( - int? minProperties, - int? maxProperties, - IReadOnlyDictionary>? dependentRequired, - IReadOnlyDictionary? dependentSchemas, - IReadOnlyList? allOfSchemas, - YamlConfigConditionalSchemas? conditionalSchemas) - { - MinProperties = minProperties; - MaxProperties = maxProperties; - DependentRequired = dependentRequired; - DependentSchemas = dependentSchemas; - AllOfSchemas = allOfSchemas; - ConditionalSchemas = conditionalSchemas; - } - - /// - /// 获取最小属性数量约束。 - /// - public int? MinProperties { get; } - - /// - /// 获取最大属性数量约束。 - /// - public int? MaxProperties { get; } - - /// - /// 获取对象内字段依赖约束。 - /// 键表示“触发字段”,值表示“触发字段出现后还必须存在的同级字段集合”。 - /// - public IReadOnlyDictionary>? DependentRequired { get; } - - /// - /// 获取对象内条件 schema 约束。 - /// 键表示“触发字段”,值表示“触发字段出现后当前对象还必须满足的额外 schema 子树”。 - /// - public IReadOnlyDictionary? DependentSchemas { get; } - - /// - /// 获取对象内 allOf 组合约束。 - /// 每个条目都表示“当前对象还必须额外满足的 focused constraint block”。 - /// - public IReadOnlyList? AllOfSchemas { get; } - - /// - /// 获取对象内 object-focused if / then / else 条件约束。 - /// 该模型会先用 if 试匹配当前对象,再只对命中的分支叠加 focused constraint block。 - /// - public YamlConfigConditionalSchemas? ConditionalSchemas { get; } -} - -/// -/// 表示一个对象节点上声明的 object-focused if / then / else 条件约束。 -/// 三个分支都共享父对象已声明字段集合,不会把分支 schema 扩展成新的生成类型形状。 -/// -internal sealed class YamlConfigConditionalSchemas -{ - /// - /// 初始化条件分支约束模型。 - /// - /// 条件判断 schema。 - /// 条件命中时需要满足的 schema。 - /// 条件未命中时需要满足的 schema。 - public YamlConfigConditionalSchemas( - YamlConfigSchemaNode ifSchema, - YamlConfigSchemaNode? thenSchema, - YamlConfigSchemaNode? elseSchema) - { - ArgumentNullException.ThrowIfNull(ifSchema); - - IfSchema = ifSchema; - ThenSchema = thenSchema; - ElseSchema = elseSchema; - } - - /// - /// 获取条件判断 schema。 - /// - public YamlConfigSchemaNode IfSchema { get; } - - /// - /// 获取条件命中时需要满足的 schema。 - /// - public YamlConfigSchemaNode? ThenSchema { get; } - - /// - /// 获取条件未命中时需要满足的 schema。 - /// - public YamlConfigSchemaNode? ElseSchema { get; } -} - -/// -/// 聚合一个标量节点上声明的数值约束与字符串约束。 -/// 该包装层保留“标量字段有约束”的统一入口,同时把不同语义的约束分成更小的专用模型。 -/// -internal sealed class YamlConfigScalarConstraints -{ - /// - /// 初始化标量约束模型。 - /// - /// 数值约束分组。 - /// 字符串约束分组。 - public YamlConfigScalarConstraints( - YamlConfigNumericConstraints? numericConstraints, - YamlConfigStringConstraints? stringConstraints) - { - NumericConstraints = numericConstraints; - StringConstraints = stringConstraints; - } - - /// - /// 获取数值约束分组。 - /// - public YamlConfigNumericConstraints? NumericConstraints { get; } - - /// - /// 获取字符串约束分组。 - /// - public YamlConfigStringConstraints? StringConstraints { get; } -} - -/// -/// 表示标量节点上声明的数值范围与步进约束。 -/// 该类型只覆盖整数 / 浮点共享的关键字,避免字符串字段继续暴露不相关的成员。 -/// -internal sealed class YamlConfigNumericConstraints -{ - /// - /// 初始化数值约束模型。 - /// - /// 最小值约束。 - /// 最大值约束。 - /// 开区间最小值约束。 - /// 开区间最大值约束。 - /// 数值步进约束。 - public YamlConfigNumericConstraints( - double? minimum, - double? maximum, - double? exclusiveMinimum, - double? exclusiveMaximum, - double? multipleOf) - { - Minimum = minimum; - Maximum = maximum; - ExclusiveMinimum = exclusiveMinimum; - ExclusiveMaximum = exclusiveMaximum; - MultipleOf = multipleOf; - } - - /// - /// 获取最小值约束。 - /// - public double? Minimum { get; } - - /// - /// 获取最大值约束。 - /// - public double? Maximum { get; } - - /// - /// 获取开区间最小值约束。 - /// - public double? ExclusiveMinimum { get; } - - /// - /// 获取开区间最大值约束。 - /// - public double? ExclusiveMaximum { get; } - - /// - /// 获取数值步进约束。 - /// - public double? MultipleOf { get; } -} - -/// -/// 表示标量节点上声明的字符串长度、模式与 format 约束。 -/// 该模型将正则原文、预编译正则和共享 format 枚举绑定保存, -/// 保证诊断内容与运行时匹配逻辑保持一致。 -/// -internal sealed class YamlConfigStringConstraints -{ - /// - /// 初始化字符串约束模型。 - /// - /// 最小长度约束。 - /// 最大长度约束。 - /// 正则模式约束原文。 - /// 已编译的正则表达式。 - /// 字符串 format 约束。 - public YamlConfigStringConstraints( - int? minLength, - int? maxLength, - string? pattern, - Regex? patternRegex, - YamlConfigStringFormatConstraint? formatConstraint) - { - MinLength = minLength; - MaxLength = maxLength; - Pattern = pattern; - PatternRegex = patternRegex; - FormatConstraint = formatConstraint; - } - - /// - /// 获取最小长度约束。 - /// - public int? MinLength { get; } - - /// - /// 获取最大长度约束。 - /// - public int? MaxLength { get; } - - /// - /// 获取正则模式约束原文。 - /// - public string? Pattern { get; } - - /// - /// 获取已编译的正则表达式。 - /// - public Regex? PatternRegex { get; } - - /// - /// 获取字符串 format 约束。 - /// - public YamlConfigStringFormatConstraint? FormatConstraint { get; } -} - -/// -/// 表示一个已归一化的字符串 format 约束。 -/// 该模型同时保留 schema 原文与共享枚举,方便诊断信息稳定展示,又避免运行时校验反复解析字符串。 -/// -internal sealed class YamlConfigStringFormatConstraint -{ - /// - /// 初始化字符串 format 约束模型。 - /// - /// schema 中声明的 format 名称。 - /// 归一化后的共享 format 枚举。 - public YamlConfigStringFormatConstraint( - string schemaName, - YamlConfigStringFormatKind kind) - { - ArgumentException.ThrowIfNullOrWhiteSpace(schemaName); - - SchemaName = schemaName; - Kind = kind; - } - - /// - /// 获取 schema 中声明的 format 名称。 - /// - public string SchemaName { get; } - - /// - /// 获取归一化后的共享 format 枚举。 - /// - public YamlConfigStringFormatKind Kind { get; } -} - -/// -/// 表示当前 Runtime / Generator / Tooling 共享支持的字符串 format 子集。 -/// -internal enum YamlConfigStringFormatKind -{ - /// - /// 表示 yyyy-MM-dd 形式的日期。 - /// - Date, - - /// - /// 表示带显式时区偏移的 RFC 3339 日期时间。 - /// - DateTime, - - /// - /// 表示 day-time duration 形式的持续时间。 - /// - Duration, - - /// - /// 表示基础电子邮件地址格式。 - /// - Email, - - /// - /// 表示带显式时区偏移的 RFC 3339 时间。 - /// - Time, - - /// - /// 表示绝对 URI。 - /// - Uri, - - /// - /// 表示连字符分隔的 UUID 文本。 - /// - Uuid -} - -/// -/// 表示一个数组节点上声明的元素数量、去重与 contains 匹配计数约束。 -/// 该模型与标量约束拆分保存,避免数组节点继续共享不适用的标量字段。 -/// -internal sealed class YamlConfigArrayConstraints -{ - /// - /// 初始化数组约束模型。 - /// - /// 最小元素数量约束。 - /// 最大元素数量约束。 - /// 是否要求数组元素唯一。 - /// 数组 contains 约束;未声明时为空。 - public YamlConfigArrayConstraints( - int? minItems, - int? maxItems, - bool uniqueItems, - YamlConfigArrayContainsConstraints? containsConstraints) - { - MinItems = minItems; - MaxItems = maxItems; - UniqueItems = uniqueItems; - ContainsConstraints = containsConstraints; - } - - /// - /// 获取最小元素数量约束。 - /// - public int? MinItems { get; } - - /// - /// 获取最大元素数量约束。 - /// - public int? MaxItems { get; } - - /// - /// 获取是否要求数组元素唯一。 - /// - public bool UniqueItems { get; } - - /// - /// 获取数组 contains 约束;未声明时返回空。 - /// - public YamlConfigArrayContainsConstraints? ContainsConstraints { get; } -} - -/// -/// 表示数组节点声明的 contains 匹配约束。 -/// 该模型把 contains 子 schema 与匹配数量边界聚合在一起,避免数组节点再额外散落多组相关成员。 -/// -internal sealed class YamlConfigArrayContainsConstraints -{ - /// - /// 初始化数组 contains 约束模型。 - /// - /// contains 子 schema。 - /// 最小匹配数量;为 时按 JSON Schema 语义默认 1。 - /// 最大匹配数量。 - public YamlConfigArrayContainsConstraints( - YamlConfigSchemaNode containsNode, - int? minContains, - int? maxContains) - { - ArgumentNullException.ThrowIfNull(containsNode); - - ContainsNode = containsNode; - MinContains = minContains; - MaxContains = maxContains; - } - - /// - /// 获取 contains 子 schema。 - /// - public YamlConfigSchemaNode ContainsNode { get; } - - /// - /// 获取最小匹配数量;未显式声明时返回空,由调用方按默认值 1 解释。 - /// - public int? MinContains { get; } - - /// - /// 获取最大匹配数量。 - /// - public int? MaxContains { get; } -} - -/// -/// 表示单个 YAML 文件中提取出的跨表引用。 -/// 该模型保留源文件、字段路径和目标表等诊断信息,以便加载器在批量校验失败时给出可定位的错误。 -/// -internal sealed class YamlConfigReferenceUsage -{ - /// - /// 初始化一个跨表引用使用记录。 - /// - /// 源 YAML 文件路径。 - /// 定义该引用的 schema 文件路径。 - /// 声明引用的字段路径。 - /// YAML 中的原始标量值。 - /// 目标配置表名称。 - /// 引用值的 schema 标量类型。 - 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; - } - - /// - /// 获取源 YAML 文件路径。 - /// - public string YamlPath { get; } - - /// - /// 获取定义该引用的 schema 文件路径。 - /// - public string SchemaPath { get; } - - /// - /// 获取声明引用的字段路径。 - /// - public string PropertyPath { get; } - - /// - /// 获取 YAML 中的原始标量值。 - /// - public string RawValue { get; } - - /// - /// 获取目标配置表名称。 - /// - public string ReferencedTableName { get; } - - /// - /// 获取引用值的 schema 标量类型。 - /// - public YamlConfigSchemaPropertyType ValueType { get; } - - /// - /// 获取便于诊断显示的字段路径。 - /// - public string DisplayPath => PropertyPath; -} - -/// -/// 表示当前运行时 schema 校验器支持的属性类型。 -/// -internal enum YamlConfigSchemaPropertyType -{ - /// - /// 对象类型。 - /// - Object, - - /// - /// 整数类型。 - /// - Integer, - - /// - /// 数值类型。 - /// - Number, - - /// - /// 布尔类型。 - /// - Boolean, - - /// - /// 字符串类型。 - /// - String, - - /// - /// 数组类型。 - /// - Array -} diff --git a/GFramework.Game/Config/YamlConfigStringConstraints.cs b/GFramework.Game/Config/YamlConfigStringConstraints.cs new file mode 100644 index 00000000..efb75840 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigStringConstraints.cs @@ -0,0 +1,58 @@ +using System.Text.RegularExpressions; + +namespace GFramework.Game.Config; + +/// +/// 表示标量节点上声明的字符串长度、模式与 format 约束。 +/// 该模型将正则原文、预编译正则和共享 format 枚举绑定保存, +/// 保证诊断内容与运行时匹配逻辑保持一致。 +/// +internal sealed class YamlConfigStringConstraints +{ + /// + /// 初始化字符串约束模型。 + /// + /// 最小长度约束。 + /// 最大长度约束。 + /// 正则模式约束原文。 + /// 已编译的正则表达式。 + /// 字符串 format 约束。 + public YamlConfigStringConstraints( + int? minLength, + int? maxLength, + string? pattern, + Regex? patternRegex, + YamlConfigStringFormatConstraint? formatConstraint) + { + MinLength = minLength; + MaxLength = maxLength; + Pattern = pattern; + PatternRegex = patternRegex; + FormatConstraint = formatConstraint; + } + + /// + /// 获取最小长度约束。 + /// + public int? MinLength { get; } + + /// + /// 获取最大长度约束。 + /// + public int? MaxLength { get; } + + /// + /// 获取正则模式约束原文。 + /// + public string? Pattern { get; } + + /// + /// 获取已编译的正则表达式。 + /// + public Regex? PatternRegex { get; } + + /// + /// 获取字符串 format 约束。 + /// + public YamlConfigStringFormatConstraint? FormatConstraint { get; } +} diff --git a/GFramework.Game/Config/YamlConfigStringFormatConstraint.cs b/GFramework.Game/Config/YamlConfigStringFormatConstraint.cs new file mode 100644 index 00000000..97c3a262 --- /dev/null +++ b/GFramework.Game/Config/YamlConfigStringFormatConstraint.cs @@ -0,0 +1,33 @@ +namespace GFramework.Game.Config; + +/// +/// 表示一个已归一化的字符串 format 约束。 +/// 该模型同时保留 schema 原文与共享枚举,方便诊断信息稳定展示,又避免运行时校验反复解析字符串。 +/// +internal sealed class YamlConfigStringFormatConstraint +{ + /// + /// 初始化字符串 format 约束模型。 + /// + /// schema 中声明的 format 名称。 + /// 归一化后的共享 format 枚举。 + public YamlConfigStringFormatConstraint( + string schemaName, + YamlConfigStringFormatKind kind) + { + ArgumentException.ThrowIfNullOrWhiteSpace(schemaName); + + SchemaName = schemaName; + Kind = kind; + } + + /// + /// 获取 schema 中声明的 format 名称。 + /// + public string SchemaName { get; } + + /// + /// 获取归一化后的共享 format 枚举。 + /// + public YamlConfigStringFormatKind Kind { get; } +} diff --git a/GFramework.Game/Config/YamlConfigStringFormatKind.cs b/GFramework.Game/Config/YamlConfigStringFormatKind.cs new file mode 100644 index 00000000..bb13a63c --- /dev/null +++ b/GFramework.Game/Config/YamlConfigStringFormatKind.cs @@ -0,0 +1,42 @@ +namespace GFramework.Game.Config; + +/// +/// 表示当前 Runtime / Generator / Tooling 共享支持的字符串 format 子集。 +/// +internal enum YamlConfigStringFormatKind +{ + /// + /// 表示 yyyy-MM-dd 形式的日期。 + /// + Date, + + /// + /// 表示带显式时区偏移的 RFC 3339 日期时间。 + /// + DateTime, + + /// + /// 表示 day-time duration 形式的持续时间。 + /// + Duration, + + /// + /// 表示基础电子邮件地址格式。 + /// + Email, + + /// + /// 表示带显式时区偏移的 RFC 3339 时间。 + /// + Time, + + /// + /// 表示绝对 URI。 + /// + Uri, + + /// + /// 表示连字符分隔的 UUID 文本。 + /// + Uuid +} diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 920e3240..cc61a5bb 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -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` errors;warning 从本轮基线 `236` 降到 `75` + - 最新结果:成功;`15` warnings、`0` errors;warning 从本轮基线 `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 干扰。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 95d3f165..f6b4be3f 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -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