"""Source protocol shared by every version adapter.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Protocol from ..http import HttpClient from ..models import Item, VersionCheck @dataclass class Context: """Passed to every source. ``scratch`` lets an adapter fetch one shared payload (a vendor-wide downloads feed, say) and reuse it across the dozens of items that resolve from it.""" http: HttpClient scratch: dict[str, Any] = field(default_factory=dict) def memo(self, key: str, factory): if key not in self.scratch: self.scratch[key] = factory() return self.scratch[key] class Source(Protocol): kind: str def check(self, item: Item, ctx: Context) -> VersionCheck: ... def error(item: Item, detail: str) -> VersionCheck: return VersionCheck(item_id=item.id, status="error", detail=detail) def manual(item: Item, detail: str = "No machine-readable feed; check by hand.") -> VersionCheck: return VersionCheck(item_id=item.id, status="manual", detail=detail) def unconfigured(item: Item, detail: str) -> VersionCheck: """AVWatch knows how to track this item, but the capability it needs is not switched on. Kept distinct from ``manual`` on purpose. "There is no firmware for this passive splitter" and "your API key is missing so 60 items could not be looked up" are completely different problems, and collapsing them into one 'check by hand' list makes a configuration gap look like the gear was never tracked at all. """ return VersionCheck(item_id=item.id, status="unconfigured", detail=detail)