Newer
Older
gnexus-book / server / app / inventory.py
from __future__ import annotations

from pathlib import Path
from typing import Any

from .config import Settings


class InventoryError(ValueError):
    pass


class InventoryRepository:
    def __init__(self, settings: Settings) -> None:
        self.settings = settings
        self.inventory_dir = settings.inventory_dir.resolve()

    def list_types(self) -> list[str]:
        return sorted(path.stem for path in self.inventory_dir.glob("*.yml"))

    def read_raw(self, inventory_type: str) -> str:
        path = self._path_for_type(inventory_type)
        return path.read_text(encoding="utf-8")

    def read_parsed(self, inventory_type: str) -> Any:
        try:
            import yaml
        except ModuleNotFoundError as exc:
            raise InventoryError("PyYAML is required for parsed inventory") from exc

        raw = self.read_raw(inventory_type)
        return yaml.safe_load(raw)

    def read_item(self, inventory_type: str, item_id: str) -> dict[str, Any]:
        data = self.read_parsed(inventory_type)
        if not isinstance(data, list):
            raise InventoryError("Inventory file does not contain a list")
        for item in data:
            if isinstance(item, dict) and item.get("id") == item_id:
                return item
        raise InventoryError("Inventory item not found")

    def _path_for_type(self, inventory_type: str) -> Path:
        if "/" in inventory_type or "\\" in inventory_type or inventory_type.startswith("."):
            raise InventoryError("Invalid inventory type")
        path = self.inventory_dir / f"{inventory_type}.yml"
        if not path.is_file():
            raise InventoryError("Inventory type not found")
        return path