"""Simple manifest loader for mindmodel manifests. Provides `load_manifest(path: str) -> dict` and `ManifestLoadError`. Behavior: - If PyYAML is installed, uses yaml.safe_load to parse the file. - Otherwise falls back to the stdlib json parser. - If the top-level document is a list it will be normalized to {"constraints": }. - Raises ManifestLoadError for missing file or parse errors. """ from typing import Any, Dict import json from pathlib import Path class ManifestLoadError(Exception): """Raised when a manifest cannot be loaded or parsed.""" try: import yaml # type: ignore except Exception: # YAML not available yaml = None # type: ignore def _parse_with_yaml(text: str) -> Any: # yamlsafe_load may return any Python structure try: return yaml.safe_load(text) except Exception as exc: # pragma: no cover - defensive raise ManifestLoadError(f"YAML parse error: {exc}") from exc def _parse_with_json(text: str) -> Any: try: return json.loads(text) except Exception as exc: raise ManifestLoadError(f"JSON parse error: {exc}") from exc def load_manifest(path: str) -> Dict[str, Any]: """Load a manifest from the given file path and normalize it to a dict. If the top-level document is a list, it will be returned as {"constraints": list}. Raises ManifestLoadError if the file does not exist or if parsing fails. """ p = Path(path) if not p.exists(): raise ManifestLoadError(f"Manifest file not found: {path}") text = p.read_text(encoding="utf-8") if yaml is not None: data = _parse_with_yaml(text) else: data = _parse_with_json(text) # Normalize if isinstance(data, list): return {"constraints": data} if isinstance(data, dict): return data # Unexpected top-level type, wrap it return {"manifest": data}