1
0
Files
PPTX/openspec/changes/archive/2026-03-02-add-yaml-validation/design.md
lanyuanxiaoyao 83ff827ad1 feat: add YAML validation with check command and auto-validation
Implements comprehensive validation before PPTX conversion to catch errors early. Includes element-level validation (colors, fonts, table consistency) and system-level validation (geometry, resources). Supports standalone check command and automatic validation during conversion.
2026-03-02 18:14:45 +08:00

285 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
当前 yaml2pptx 系统已经有基本的 YAML 加载和元素验证功能:
- `loaders/yaml_loader.py` 提供基本的 YAML 语法检查和结构验证
- `core/elements.py` 定义了元素数据类,在 `__post_init__` 中进行基本验证
但现有验证不够全面,用户经常在转换后才发现问题(元素超出页面、文件不存在等),需要反复修改和转换。
项目约束:
- 使用 uv 运行 Python 脚本
- 无新增外部依赖
- 测试文件放在 temp 目录
- 面向中文开发者
## Goals / Non-Goals
**Goals:**
- 提供全面的 YAML 验证功能,在转换前发现问题
- 支持独立 check 命令和转换前自动检查
- 实现分级错误报告ERROR/WARNING/INFO
- 保持验证逻辑的可扩展性和可维护性
**Non-Goals:**
- 不实现自动修复功能(只检测,不修改)
- 不支持第三阶段的质量警告(元素重叠、文本溢出等)
- 不添加 GUI 或 Web 界面
- 不实现配置文件来自定义验证规则
## Decisions
### 决策 1验证职责分层
**决策**:将验证逻辑分为两层:
1. **元素级验证**:放在元素类本身(`core/elements.py`
2. **系统级验证**:放在独立的验证器模块(`validators/`
**理由**
- 元素类最了解自己的约束,应该负责自身的完整性验证
- 系统级验证需要全局上下文(如页面尺寸、文件路径),适合集中处理
- 符合单一职责原则,便于扩展和维护
**元素级验证职责**
- 必需字段检查(如 image 必须有 src
- 数据类型检查(如 box 必须是 4 个数字)
- 值的有效性检查(如颜色格式、枚举值)
- 元素内部逻辑一致性
**系统级验证职责**
- 几何验证(元素是否在页面范围内,需要知道页面尺寸)
- 资源验证(文件是否存在,需要知道文件路径)
- 跨元素验证(如果未来需要)
**替代方案**
- 方案 A所有验证集中在验证器中 → 验证器会变得臃肿,元素类失去封装性
- 方案 B所有验证都在元素类中 → 元素类需要知道全局上下文,违反依赖倒置原则
### 决策 2验证器模块结构
**决策**:创建 `validators/` 目录,包含以下模块:
```
validators/
├── __init__.py # 导出主验证器
├── validator.py # 主验证器,协调各子验证器
├── geometry.py # 几何验证器
├── resource.py # 资源验证器
└── result.py # 验证结果数据结构
```
**理由**
- 模块化设计,每个验证器职责单一
- 便于测试和扩展
- 主验证器作为门面,简化调用
**验证流程**
```
Validator.validate(yaml_path, template_dir)
1. 加载 YAML复用 yaml_loader
2. 元素级验证(调用元素类的验证方法)
3. 几何验证GeometryValidator
4. 资源验证ResourceValidator
返回 ValidationResult
```
**替代方案**
- 方案 A单一验证器文件 → 代码过长,难以维护
- 方案 B每种验证一个独立包 → 过度设计,增加复杂度
### 决策 3验证结果数据结构
**决策**:定义 `ValidationResult``ValidationIssue` 类:
```python
@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]
warnings: List[ValidationIssue]
infos: List[ValidationIssue]
def has_errors(self) -> bool
def format_output(self) -> str # 格式化为命令行输出
```
**理由**
- 结构化的结果便于测试和扩展
- `code` 字段便于未来实现错误码查询或国际化
- `format_output()` 方法封装输出格式,便于修改
**替代方案**
- 方案 A直接返回字符串 → 难以测试,不便于扩展
- 方案 B返回字典 → 缺少类型安全,容易出错
### 决策 4命令行接口设计
**决策**:修改 `yaml2pptx.py`,使用子命令模式:
```python
# 独立验证
yaml2pptx.py check input.yaml [--template-dir DIR]
# 转换(默认自动验证)
yaml2pptx.py input.yaml [output.pptx] [--template-dir DIR] [--no-check]
```
**实现方式**
- 使用 `argparse``subparsers` 实现子命令
- `check` 子命令调用验证器并输出结果
- 转换命令在加载 YAML 后、渲染前调用验证器
- 如果验证失败(有 ERROR终止转换并返回非零退出码
**理由**
- 子命令模式清晰,符合 CLI 工具惯例
- 默认启用自动验证,提升用户体验
- `--no-check` 提供灵活性,适合调试场景
**替代方案**
- 方案 A独立的 `yaml2pptx-check` 命令 → 增加维护成本,用户需要记住两个命令
- 方案 B只有自动验证没有独立命令 → 用户无法单独验证
### 决策 5元素类验证方法增强
**决策**:在 `core/elements.py` 中为每个元素类添加 `validate()` 方法:
```python
@dataclass
class TextElement:
# ... 现有字段 ...
def validate(self) -> List[ValidationIssue]:
"""验证元素自身的完整性"""
issues = []
# 检查颜色格式
if self.font.get('color'):
if not self._is_valid_color(self.font['color']):
issues.append(ValidationIssue(
level="ERROR",
message=f"无效的颜色格式: {self.font['color']}",
location="", # 由调用者填充
code="INVALID_COLOR_FORMAT"
))
# 检查字体大小
if self.font.get('size'):
size = self.font['size']
if size < 8:
issues.append(ValidationIssue(
level="WARNING",
message=f"字体太小: {size}pt (建议 >= 8pt)",
location="",
code="FONT_TOO_SMALL"
))
return issues
```
**理由**
- 元素类封装自己的验证逻辑
- `__post_init__` 保留用于阻止创建无效对象(如 box 不是 4 个数字)
- `validate()` 用于检测可以创建但不推荐的情况(如字体太小)
**替代方案**
- 方案 A只在 `__post_init__` 中验证 → 无法区分致命错误和警告
- 方案 B不修改元素类所有验证在验证器中 → 违反封装原则
### 决策 6边界检查容忍度
**决策**:几何验证时,允许 0.1 英寸的容忍度:
```python
TOLERANCE = 0.1 # 英寸
if right > slide_width + TOLERANCE:
# 报告 WARNING
```
**理由**
- 浮点数计算可能有精度误差
- 0.1 英寸(约 2.54mm)在视觉上几乎不可见
- 避免误报,提升用户体验
**替代方案**
- 方案 A零容忍 → 可能产生大量误报
- 方案 B更大的容忍度如 0.5 英寸)→ 可能漏掉真正的问题
## Risks / Trade-offs
### 风险 1验证性能影响
**风险**:验证可能增加转换时间,特别是资源验证(检查文件存在性)。
**缓解措施**
- 验证是可选的(`--no-check` 跳过)
- 资源验证只检查文件存在性,不读取文件内容
- 未来可以考虑并行验证(如果性能成为问题)
### 风险 2元素类验证方法的维护成本
**风险**:每个元素类都需要实现 `validate()` 方法,增加维护成本。
**缓解措施**
- 提供基类或工具函数来复用常见验证逻辑(如颜色格式检查)
- 验证逻辑相对稳定,不会频繁修改
- 好处是验证逻辑和元素定义在一起,便于理解和修改
### 风险 3错误消息的可读性
**风险**:错误消息可能不够清晰,用户难以理解如何修复。
**缓解措施**
- 错误消息包含具体的位置信息(幻灯片、元素)
- 错误消息包含期望值和实际值(如 "right=10.5 > 10.0"
- 未来可以添加错误码和文档链接
### Trade-off验证完整性 vs 性能
**选择**:优先保证验证完整性,性能其次。
**理由**
- 验证是可选的,性能敏感的场景可以跳过
- 提前发现问题比快速转换更重要
- 当前验证项不多,性能影响可控
## Migration Plan
**部署步骤**
1. 实现验证器模块(不影响现有功能)
2. 增强元素类的验证方法(向后兼容)
3. 修改 `yaml2pptx.py` 添加 check 子命令
4. 添加自动验证逻辑(默认启用)
5. 编写测试用例(在 temp 目录)
6. 更新 README.md 文档
**回滚策略**
- 如果验证器有问题,用户可以使用 `--no-check` 跳过
- 验证器是独立模块,可以快速禁用或修复
- 不影响现有的转换功能
**兼容性**
- 完全向后兼容,现有 YAML 文件和命令行用法不受影响
- 新增的 check 子命令和 --no-check 选项是可选的
## Open Questions
1. **是否需要配置文件来自定义验证规则?**
- 当前决策:不需要,保持简单
- 未来可以考虑添加 `.yaml2pptx.yaml` 配置文件
2. **是否需要支持 JSON 格式的验证结果输出?**
- 当前决策:只支持命令行文本输出
- 未来可以添加 `--format json` 选项
3. **字体大小的合理范围是否需要可配置?**
- 当前决策:硬编码 8pt-100pt
- 未来可以考虑通过配置文件自定义