137 lines
5.2 KiB
Python
137 lines
5.2 KiB
Python
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
|