实现了统一的metadata结构和字体作用域系统,支持文档和模板库之间的单向字体引用。 主要变更: - 模板库必须包含metadata字段(包括size、fonts、fonts_default) - 实现文档和模板库的size一致性校验 - 实现字体作用域系统(文档可引用模板库字体,反之不可) - 实现跨域循环引用检测 - 实现fonts_default级联规则(模板库→文档→系统默认) - 添加错误代码常量(SIZE_MISMATCH、FONT_NOT_FOUND等) - 更新文档和开发者指南 测试覆盖: - 新增33个测试(单元测试20个,集成测试13个) - 所有457个测试通过 Breaking Changes: - 模板库文件必须包含metadata字段 - 模板库metadata.size为必填字段 - 文档和模板库的size必须一致 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
"""
|
||
验证结果数据结构
|
||
|
||
定义验证问题和验证结果的数据类。
|
||
"""
|
||
|
||
from dataclasses import dataclass, field
|
||
from typing import List
|
||
|
||
|
||
@dataclass
|
||
class ValidationIssue:
|
||
"""验证问题"""
|
||
|
||
level: str # "ERROR" | "WARNING" | "INFO"
|
||
message: str
|
||
location: str # "幻灯片 2, 元素 3"
|
||
code: str # "ELEMENT_OUT_OF_BOUNDS"
|
||
|
||
|
||
@dataclass
|
||
class ValidationResult:
|
||
"""验证结果"""
|
||
|
||
valid: bool # 是否有 ERROR
|
||
errors: List[ValidationIssue] = field(default_factory=list)
|
||
warnings: List[ValidationIssue] = field(default_factory=list)
|
||
infos: List[ValidationIssue] = field(default_factory=list)
|
||
|
||
def has_errors(self) -> bool:
|
||
"""是否有错误"""
|
||
return len(self.errors) > 0
|
||
|
||
def format_output(self) -> str:
|
||
"""格式化为命令行输出"""
|
||
lines = []
|
||
|
||
lines.append("🔍 正在检查 YAML 文件...\n")
|
||
|
||
# 错误
|
||
if self.errors:
|
||
lines.append(f"❌ 错误 ({len(self.errors)}):")
|
||
for issue in self.errors:
|
||
location_str = f"[{issue.location}] " if issue.location else ""
|
||
lines.append(f" {location_str}{issue.message}")
|
||
lines.append("")
|
||
|
||
# 警告
|
||
if self.warnings:
|
||
lines.append(f"⚠️ 警告 ({len(self.warnings)}):")
|
||
for issue in self.warnings:
|
||
location_str = f"[{issue.location}] " if issue.location else ""
|
||
lines.append(f" {location_str}{issue.message}")
|
||
lines.append("")
|
||
|
||
# 提示
|
||
if self.infos:
|
||
lines.append(f"ℹ️ 提示 ({len(self.infos)}):")
|
||
for issue in self.infos:
|
||
location_str = f"[{issue.location}] " if issue.location else ""
|
||
lines.append(f" {location_str}{issue.message}")
|
||
lines.append("")
|
||
|
||
# 总结
|
||
if not self.errors and not self.warnings and not self.infos:
|
||
lines.append("验证通过,未发现问题")
|
||
else:
|
||
summary_parts = []
|
||
if self.errors:
|
||
summary_parts.append(f"{len(self.errors)} 个错误")
|
||
if self.warnings:
|
||
summary_parts.append(f"{len(self.warnings)} 个警告")
|
||
if self.infos:
|
||
summary_parts.append(f"{len(self.infos)} 个提示")
|
||
lines.append(f"检查完成: 发现 {', '.join(summary_parts)}")
|
||
|
||
return "\n".join(lines)
|
||
|
||
|
||
# ============= 错误代码常量 =============
|
||
|
||
# 模板库 metadata 相关错误
|
||
ERROR_TEMPLATE_LIBRARY_MISSING_METADATA = "TEMPLATE_LIBRARY_MISSING_METADATA"
|
||
ERROR_TEMPLATE_LIBRARY_METADATA_MISSING_SIZE = "TEMPLATE_LIBRARY_METADATA_MISSING_SIZE"
|
||
ERROR_TEMPLATE_LIBRARY_METADATA_INVALID_SIZE = "TEMPLATE_LIBRARY_METADATA_INVALID_SIZE"
|
||
|
||
# Size 一致性错误
|
||
ERROR_SIZE_MISMATCH = "SIZE_MISMATCH"
|
||
|
||
# 字体引用相关错误
|
||
ERROR_TEMPLATE_FONT_REF_DOC_FORBIDDEN = "TEMPLATE_FONT_REF_DOC_FORBIDDEN"
|
||
ERROR_TEMPLATE_PARENT_REF_DOC_FORBIDDEN = "TEMPLATE_PARENT_REF_DOC_FORBIDDEN"
|
||
ERROR_FONT_NOT_FOUND = "FONT_NOT_FOUND"
|
||
ERROR_CIRCULAR_REFERENCE = "CIRCULAR_REFERENCE"
|
||
ERROR_FONT_DEFAULT_INVALID = "FONT_DEFAULT_INVALID"
|