from pathlib import Path from typing import Optional import hashlib import json from .models import DesignModel from .utils import read_file, write_file class DesignManager: """设计管理类,用于处理 design.json 文件的加载、保存、README 哈希计算、校验和同步功能。""" def __init__(self, design_file_path: Optional[Path] = None): """初始化 DesignManager。 参数: design_file_path: design.json 文件的可选路径,如果提供,则可在后续方法中省略路径参数。 """ self.design_file_path = design_file_path def load_design(self, file_path: Optional[Path] = None) -> DesignModel: """加载 design.json 文件并解析为 DesignModel 对象。 参数: file_path: design.json 文件路径,如果为 None,则使用初始化时设置的路径。 返回: DesignModel: 解析后的设计模型。 异常: FileNotFoundError: 如果文件不存在。 ValueError: 如果 JSON 解析或模型验证失败。 """ if file_path is None: if self.design_file_path is None: raise ValueError("未提供 design.json 文件路径") file_path = self.design_file_path file_path = Path(file_path) if not file_path.exists(): raise FileNotFoundError(f"design.json 文件不存在: {file_path}") content = read_file(str(file_path)) try: design_dict = json.loads(content) design = DesignModel.parse_obj(design_dict) return design except json.JSONDecodeError as e: raise ValueError(f"JSON 解析失败: {e}") except Exception as e: raise ValueError(f"模型验证失败: {e}") def save_design(self, design: DesignModel, file_path: Optional[Path] = None) -> None: """将 DesignModel 对象保存为 design.json 文件。 参数: design: 要保存的设计模型。 file_path: 保存的文件路径,如果为 None,则使用初始化时设置的路径。 异常: ValueError: 如果未提供文件路径且初始化时未设置。 """ if file_path is None: if self.design_file_path is None: raise ValueError("未提供 design.json 文件路径") file_path = self.design_file_path file_path = Path(file_path) design_dict = design.dict(exclude_none=True) content = json.dumps(design_dict, indent=2, ensure_ascii=False) write_file(str(file_path), content) @staticmethod def compute_readme_hash(readme_path: Path) -> str: """计算 README 文件的 SHA256 哈希值。 参数: readme_path: README 文件路径(例如 README.md)。 返回: str: 十六进制字符串表示的 SHA256 哈希值。 异常: FileNotFoundError: 如果文件不存在。 """ readme_path = Path(readme_path) if not readme_path.exists(): raise FileNotFoundError(f"README 文件不存在: {readme_path}") with open(readme_path, 'rb') as f: content = f.read() hash_obj = hashlib.sha256(content) return hash_obj.hexdigest() def validate_readme_hash(self, design: DesignModel, readme_path: Optional[Path] = None) -> bool: """校验 README 文件的哈希值与 design.json 中存储的哈希值是否一致。 参数: design: 设计模型,包含存储的 readme_hash。 readme_path: README 文件路径,如果为 None,则使用 design.readme_path。 返回: bool: 如果哈希一致或 readme_hash 为 None(视为无效),返回 True;否则返回 False。 """ if readme_path is None: if design.readme_path is None: return False # 没有存储的路径,无法校验 readme_path = Path(design.readme_path) else: readme_path = Path(readme_path) if design.readme_hash is None: return False # 没有存储的哈希,视为无效 try: computed_hash = self.compute_readme_hash(readme_path) return computed_hash == design.readme_hash except FileNotFoundError: return False # 文件不存在,校验失败 def sync_with_readme(self, design: DesignModel, readme_path: Path) -> DesignModel: """同步 design.json 与 README 文件,更新 readme_path 和 readme_hash。 参数: design: 要更新的设计模型。 readme_path: README 文件路径。 返回: DesignModel: 更新后的设计模型(原地修改并返回同一个对象)。 """ readme_path = Path(readme_path) if not readme_path.exists(): raise FileNotFoundError(f"README 文件不存在: {readme_path}") new_hash = self.compute_readme_hash(readme_path) design.readme_path = str(readme_path) design.readme_hash = new_hash return design