from __future__ import annotations from pathlib import Path from genesis.data_models import StructuredSource from genesis.impact.code_parser import CodeParser from genesis.impact.existing_system_explorer import ExistingSystemExplorer from genesis.parsers.excel_parser import ExcelParser from genesis.parsers.rule_doc_parser import RuleDocParser from genesis.parsers.word_template_parser import WordTemplateParser XLSX_EXTS = (".xlsx", ".xls") DOCX_EXT = ".docx" def _validate_path(path: str | Path, allowed_exts: tuple[str, ...]) -> Path: """校验文件扩展名合法且文件存在(T7 DRY:消除三处重复校验)。 Raises: ValueError: 扩展名不在 allowed_exts(含无扩展名) FileNotFoundError: 文件不存在 """ p = Path(path) if p.suffix.lower() not in allowed_exts: raise ValueError(f"不支持的文件类型: {p.suffix or '(无扩展名)'}") if not p.exists(): raise FileNotFoundError(str(path)) return p class SourceParser: """全量输入门面:Excel 要件定义 + Word 模板 + Word 规则 → StructuredSource。 角色由调用方按 api-design file_type 语义显式传入(requirements/template/ write_instruction/rules),不做基于文件名的隐式猜测(spec §3.4)。 """ def __init__(self) -> None: self._excel = ExcelParser() def parse( self, requirement_paths: list[str | Path] | None = None, template_path: str | Path | None = None, write_instruction_paths: list[str | Path] | None = None, rule_paths: list[str | Path] | None = None, existing_system_path: str | Path | None = None, existing_system_language: str | None = None, design_doc_paths: list[str | Path] | None = None, ) -> StructuredSource: """扩展名校验先于存在性校验(不存在的文件若扩展名未知将抛出 ValueError 而非 FileNotFoundError)。 existing_system_path:既有系统源码目录(追加/改修场景)。提供时解析为 ExistingSystemInfo(门控通过 → 进入影响调查);未提供/解析失败 → 保持 None。 existing_system_language:既有系统源码开发语言(如 "java")。默认 None 表示 由 CodeParser 按扩展名自动探测;显式指定时按该语言解析(多语言支持扩展点)。 """ requirement_paths = requirement_paths or [] write_instruction_paths = write_instruction_paths or [] rule_paths = rule_paths or [] tables = [] comments = [] for p in requirement_paths: path = _validate_path(p, XLSX_EXTS) result = self._excel.parse(path) tables.extend(result.tables) comments.extend(result.comments) template = None if template_path is not None: tpath = _validate_path(template_path, (DOCX_EXT,)) template = WordTemplateParser().parse(tpath) rule_docs = [] for p in [*write_instruction_paths, *rule_paths]: path = _validate_path(p, (DOCX_EXT,)) # 做成说明书与记入规则均为 Type A 写入规则 → write(api-design §2.2) rule_docs.append(RuleDocParser().parse(path, category="write")) existing_system = None if existing_system_path is not None: code = CodeParser().parse(existing_system_path, language=existing_system_language) existing_system = ExistingSystemExplorer().explore(code) design_docs = [] for p in (design_doc_paths or []): dpath = _validate_path(p, (DOCX_EXT,)) # 设计文档为 Type A 参考材料(category="design",区别于写入规则) design_docs.append(RuleDocParser().parse(dpath, category="design")) return StructuredSource( tables=tables, template=template, rule_docs=rule_docs, image_analyses=[], existing_system=existing_system, comments=comments, design_docs=design_docs, )