Coverage for src/ph/store_catalog.py: 100.0%

17 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-28 08:17 +0000

1"""Configured game-store registry and lookup helpers.""" 

2 

3from dataclasses import dataclass 

4from typing import Self 

5 

6from ph.cache_policy import DEFAULT_CATALOGUE_TTL_DAYS, catalogue_ttl_seconds 

7from ph.config import Config 

8from ph.minerva_store import MinervaStore 

9from ph.store import GameStore 

10from ph.vimm_store import VimmStore 

11 

12 

13@dataclass(frozen=True, slots=True) 

14class StoreCatalog: 

15 """The stores available to this application run.""" 

16 

17 stores: tuple[GameStore, ...] 

18 

19 @classmethod 

20 def from_config( 

21 cls, 

22 config: Config, 

23 ttl_seconds: int = catalogue_ttl_seconds(DEFAULT_CATALOGUE_TTL_DAYS), 

24 ) -> Self: 

25 """Create every enabled concrete store from application configuration.""" 

26 

27 available: tuple[GameStore, ...] = ( 

28 VimmStore( 

29 config.vimm_base_url, 

30 config.timeout_seconds, 

31 config.download_directory, 

32 ttl_seconds, 

33 ), 

34 MinervaStore( 

35 config.minerva_base_url, 

36 config.minerva_torrent_base_url, 

37 config.timeout_seconds, 

38 config.download_directory, 

39 ttl_seconds, 

40 ), 

41 ) 

42 enabled = set(config.enabled_stores) 

43 return cls(tuple(store for store in available if store.store_id in enabled)) 

44 

45 def find(self, store_id: str) -> GameStore | None: 

46 """Resolve a store identifier case-insensitively.""" 

47 

48 normalized = store_id.strip().casefold() 

49 return next( 

50 (store for store in self.stores if store.store_id.casefold() == normalized), 

51 None, 

52 )