import tomllib import tomli_w from pathlib import Path from typing import Dict, List, Optional, Union, Any from src import paths from src.models import ToolPreset, BiasProfile class ToolPresetManager: def __init__(self, project_root: Optional[Union[str, Path]] = None): self.project_root = Path(project_root) if project_root else None def _get_path(self, scope: str) -> Path: if scope == "global": return paths.get_global_tool_presets_path() elif scope == "project": if not self.project_root: raise ValueError("Project root not set for project scope operation.") return paths.get_project_tool_presets_path(self.project_root) else: raise ValueError(f"Invalid scope: {scope}") def _read_raw(self, path: Path) -> Dict[str, Any]: if not path.exists(): return {} try: with open(path, "rb") as f: return tomllib.load(f) except Exception: return {} def _write_raw(self, path: Path, data: Dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) with open(path, "wb") as f: tomli_w.dump(data, f) def load_all_presets(self) -> Dict[str, ToolPreset]: global_path = paths.get_global_tool_presets_path() global_data = self._read_raw(global_path).get("presets", {}) presets = {} for name, config in global_data.items(): if isinstance(config, dict): presets[name] = ToolPreset.from_dict(name, config) if self.project_root: project_path = paths.get_project_tool_presets_path(self.project_root) project_data = self._read_raw(project_path).get("presets", {}) for name, config in project_data.items(): if isinstance(config, dict): presets[name] = ToolPreset.from_dict(name, config) return presets def load_all(self) -> Dict[str, ToolPreset]: """Backward compatibility for load_all().""" return self.load_all_presets() def save_preset(self, preset: ToolPreset, scope: str = "project") -> None: path = self._get_path(scope) data = self._read_raw(path) if "presets" not in data: data["presets"] = {} data["presets"][preset.name] = preset.to_dict() self._write_raw(path, data) def delete_preset(self, name: str, scope: str = "project") -> None: path = self._get_path(scope) data = self._read_raw(path) if "presets" in data and name in data["presets"]: del data["presets"][name] self._write_raw(path, data) def load_all_bias_profiles(self) -> Dict[str, BiasProfile]: global_path = paths.get_global_tool_presets_path() global_data = self._read_raw(global_path).get("bias_profiles", {}) profiles = {} for name, config in global_data.items(): if isinstance(config, dict): cfg = dict(config) if "name" not in cfg: cfg["name"] = name profiles[name] = BiasProfile.from_dict(cfg) if self.project_root: project_path = paths.get_project_tool_presets_path(self.project_root) project_data = self._read_raw(project_path).get("bias_profiles", {}) for name, config in project_data.items(): if isinstance(config, dict): cfg = dict(config) if "name" not in cfg: cfg["name"] = name profiles[name] = BiasProfile.from_dict(cfg) return profiles def save_bias_profile(self, profile: BiasProfile, scope: str = "project") -> None: path = self._get_path(scope) data = self._read_raw(path) if "bias_profiles" not in data: data["bias_profiles"] = {} data["bias_profiles"][profile.name] = profile.to_dict() self._write_raw(path, data) def delete_bias_profile(self, name: str, scope: str = "project") -> None: path = self._get_path(scope) data = self._read_raw(path) if "bias_profiles" in data and name in data["bias_profiles"]: del data["bias_profiles"][name] self._write_raw(path, data)